Improve relative paths, check missing files

This commit is contained in:
Christoph Hagen
2022-08-29 13:33:48 +02:00
parent 761845311e
commit 268c0e5f39
4 changed files with 103 additions and 15 deletions

View File

@@ -5,7 +5,6 @@ import AppKit
typealias SourceFile = (data: Data, didChange: Bool)
typealias SourceTextFile = (content: String, didChange: Bool)
#warning("Skip external files")
final class FileSystem {
private static let hashesFileName = "hashes.json"
@@ -39,6 +38,20 @@ final class FileSystem {
*/
private var requiredFiles: Set<String> = []
/**
The files marked as external in element metadata.
Files included here are not generated, since they are assumed to be added separately.
*/
private var externalFiles: Set<String> = []
/**
The files marked as expected, i.e. they exist after the generation is completed.
The key of the dictionary is the file path, the value is the file providing the link
*/
private var expectedFiles: [String : String] = [:]
/**
The image creation tasks.
@@ -227,7 +240,6 @@ final class FileSystem {
return scaledSize
}
#warning("Implement image functions")
func createImages() {
for (destination, image) in imageTasks.sorted(by: { $0.key < $1.key }) {
createImageIfNeeded(image, for: destination)
@@ -322,12 +334,34 @@ final class FileSystem {
requiredFiles.insert(file)
}
/**
Mark a file as explicitly missing.
This is done for the `externalFiles` entries in metadata,
to indicate that these files will be copied to the output folder manually.
*/
func exclude(file: String) {
externalFiles.insert(file)
}
/**
Mark a file as expected to be present in the output folder after generation.
This is done for all links between pages, which only exist after the pages have been generated.
*/
func expect(file: String, source: String) {
expectedFiles[file] = source
}
func copyRequiredFiles() {
var missingFiles = [String]()
for file in requiredFiles {
let sourceUrl = input.appendingPathComponent(file)
let cleanPath = cleanRelativeURL(file)
let sourceUrl = input.appendingPathComponent(cleanPath)
let destinationUrl = output.appendingPathComponent(cleanPath)
guard sourceUrl.exists else {
missingFiles.append(file)
if !externalFiles.contains(file) {
log.add(error: "Missing required file", source: cleanPath)
}
continue
}
let data: Data
@@ -337,15 +371,40 @@ final class FileSystem {
log.add(error: "Failed to read data at \(sourceUrl.path)", source: source, error: error)
continue
}
let destinationUrl = output.appendingPathComponent(file)
write(data, to: destinationUrl)
writeIfChanged(data, to: destinationUrl)
}
for (file, source) in expectedFiles {
guard !externalFiles.contains(file) else {
continue
}
let cleanPath = cleanRelativeURL(file)
let destinationUrl = output.appendingPathComponent(cleanPath)
if !destinationUrl.exists {
log.add(error: "Missing \(cleanPath)", source: source)
}
}
}
private func cleanRelativeURL(_ raw: String) -> String {
let raw = raw.dropAfterLast("#") // Clean links to page content
guard raw.contains("..") else {
return raw
}
var result: [String] = []
for component in raw.components(separatedBy: "/") {
if component == ".." {
_ = result.popLast()
} else {
result.append(component)
}
}
return result.joined(separator: "/")
}
// MARK: Writing files
@discardableResult
func write(_ data: Data, to url: URL) -> Bool {
func writeIfChanged(_ data: Data, to url: URL) -> Bool {
// Only write changed files
if url.exists, let oldContent = try? Data(contentsOf: url), data == oldContent {
return false
@@ -362,7 +421,7 @@ final class FileSystem {
@discardableResult
func write(_ string: String, to url: URL) -> Bool {
let data = string.data(using: .utf8)!
return write(data, to: url)
return writeIfChanged(data, to: url)
}
}