185 lines
6.5 KiB
Swift
185 lines
6.5 KiB
Swift
import Foundation
|
|
import ArgumentParser
|
|
|
|
|
|
@main
|
|
struct CHGenerator: ParsableCommand {
|
|
|
|
@Argument(help: "The path to the generator configuration file")
|
|
var configPath: String
|
|
|
|
mutating func run() throws {
|
|
try generate(configPath: configPath)
|
|
}
|
|
}
|
|
|
|
private func loadConfiguration(at configPath: String) -> Configuration? {
|
|
print("--- CONFIGURATION ----------------------------------")
|
|
print(" ")
|
|
print(" Configuration file: \(configPath)")
|
|
let configUrl = URL(fileURLWithPath: configPath)
|
|
guard configUrl.exists else {
|
|
print(" Error: Configuration file not found")
|
|
return nil
|
|
}
|
|
var config: Configuration
|
|
do {
|
|
let data = try Data(contentsOf: configUrl)
|
|
config = try JSONDecoder().decode(from: data)
|
|
config.adjustPathsRelative(to: configUrl.deletingLastPathComponent())
|
|
} catch {
|
|
print(" Configuration error: \(error)")
|
|
return nil
|
|
}
|
|
config.printOverview()
|
|
print(" ")
|
|
return config
|
|
}
|
|
|
|
private func loadSiteData(in folder: URL, runFolder: URL) throws -> (root: Element, pageMap: PageMap)? {
|
|
print("--- SOURCE FILES -----------------------------------")
|
|
print(" ")
|
|
|
|
let log = MetadataInfoLogger(input: folder)
|
|
let root = Element(atRoot: folder, log: log)
|
|
|
|
let file = runFolder.appendingPathComponent("metadata.txt")
|
|
defer {
|
|
log.writeResults(to: file)
|
|
print(" ")
|
|
}
|
|
guard let root else {
|
|
log.printMetadataScanOverview(languages: 0)
|
|
print(" Error: No site root loaded, aborting generation")
|
|
return nil
|
|
}
|
|
let pageMap = root.languages.map { (language: $0.language, pages: root.getExternalPageMap(language: $0.language)) }
|
|
log.printMetadataScanOverview(languages: root.languages.count)
|
|
return (root, pageMap)
|
|
}
|
|
|
|
private func generatePages(from root: Element, configuration: Configuration, fileUpdates: FileUpdateChecker, pageMap: PageMap, runFolder: URL) -> (ImageData, FileData)? {
|
|
print("--- GENERATION -------------------------------------")
|
|
print(" ")
|
|
|
|
let pageCount = pageMap.reduce(0) { $0 + $1.pages.count }
|
|
let results = GenerationResultsHandler(
|
|
in: configuration.contentDirectory,
|
|
to: configuration.outputDirectory,
|
|
configuration: configuration,
|
|
fileUpdates: fileUpdates,
|
|
pageMap: pageMap,
|
|
pageCount: pageCount)
|
|
|
|
defer { results.printOverview() }
|
|
|
|
let siteGenerator: SiteGenerator
|
|
do {
|
|
siteGenerator = try SiteGenerator(results: results)
|
|
} catch {
|
|
return nil
|
|
}
|
|
siteGenerator.generate(site: root)
|
|
|
|
let url = runFolder.appendingPathComponent("pages.txt")
|
|
results.writeResults(to: url)
|
|
|
|
if let error = fileUpdates.writeDetectedFileChanges(to: runFolder) {
|
|
print(" Hashes not saved: \(error)")
|
|
}
|
|
return (results.images, results.files)
|
|
}
|
|
|
|
private func generateImages(_ images: ImageData, configuration: Configuration, runFolder: URL, fileUpdates: FileUpdateChecker) {
|
|
print("--- IMAGES -----------------------------------------")
|
|
print(" ")
|
|
let reader = ImageReader(in: configuration.contentDirectory, runFolder: runFolder, fileUpdates: fileUpdates)
|
|
let generator = ImageGenerator(
|
|
input: configuration.contentDirectory,
|
|
output: configuration.outputDirectory,
|
|
reader: reader, images: images)
|
|
generator.generateImages()
|
|
print(" ")
|
|
let file = runFolder.appendingPathComponent("images.txt")
|
|
generator.writeResults(to: file)
|
|
}
|
|
|
|
private func copyFiles(files: FileData, configuration: Configuration, runFolder: URL) {
|
|
print("--- FILES ------------------------------------------")
|
|
print(" ")
|
|
let generator = FileGenerator(
|
|
input: configuration.contentDirectory,
|
|
output: configuration.outputDirectory,
|
|
runFolder: runFolder,
|
|
files: files)
|
|
generator.generate()
|
|
}
|
|
|
|
private func finish(start: Date, complete: Bool) {
|
|
print("--- SUMMARY ----------------------------------------")
|
|
print(" ")
|
|
let duration = Int(-start.timeIntervalSinceNow.rounded())
|
|
if duration < 60 {
|
|
print(" Duration: \(duration) s")
|
|
} else if duration < 3600 {
|
|
print(String(format: " Duration: %d:%02d", duration / 60, duration % 60))
|
|
} else {
|
|
print(String(format: " Duration: %d:%02d:%02d", duration / 3600, (duration / 60) % 60, duration % 60))
|
|
}
|
|
print(" Complete: \(complete ? "Yes" : "No")")
|
|
print(" ")
|
|
print("----------------------------------------------------")
|
|
}
|
|
|
|
private func generate(configPath: String) throws {
|
|
let start = Date()
|
|
var complete = false
|
|
|
|
defer {
|
|
// 6. Print summary
|
|
finish(start: start, complete: complete)
|
|
}
|
|
|
|
print(" ")
|
|
|
|
guard checkDependencies() else {
|
|
return
|
|
}
|
|
|
|
// 1. Load configuration
|
|
guard let configuration = loadConfiguration(at: configPath) else {
|
|
return
|
|
}
|
|
|
|
let runFolder = configuration.contentDirectory.appendingPathComponent("run")
|
|
|
|
// 2. Scan site elements
|
|
guard let (siteRoot, pageMap) = try loadSiteData(in: configuration.contentDirectory, runFolder: runFolder) else {
|
|
return
|
|
}
|
|
|
|
let fileUpdates = FileUpdateChecker(input: configuration.contentDirectory)
|
|
switch fileUpdates.loadPreviousRun(from: runFolder) {
|
|
case .notLoaded:
|
|
print("Regarding all files as new (no hashes loaded)")
|
|
case .loaded:
|
|
break
|
|
case .failed(let error):
|
|
print("Regarding all files as new (\(error))")
|
|
}
|
|
|
|
// 3. Generate pages
|
|
|
|
guard let (images, files) = generatePages(from: siteRoot, configuration: configuration, fileUpdates: fileUpdates, pageMap: pageMap, runFolder: runFolder) else {
|
|
return
|
|
}
|
|
|
|
// 4. Generate images
|
|
generateImages(images, configuration: configuration, runFolder: runFolder, fileUpdates: fileUpdates)
|
|
|
|
// 5. Copy/minify files
|
|
copyFiles(files: files, configuration: configuration, runFolder: runFolder)
|
|
|
|
complete = true
|
|
}
|