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(" ") print("--- CONFIGURATION ----------------------------------") print(" ") print(" Configuration file: \(configPath)") let configUrl = URL(fileURLWithPath: configPath) let config: Configuration do { let data = try Data(contentsOf: configUrl) config = try JSONDecoder().decode(from: data) } catch { print(" Configuration error: \(error)") return nil } config.printOverview() print(" ") return config } private func loadSiteData(in folder: URL, runFolder: URL) throws -> (root: Element, ids: [String : String])? { 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 ids = root.getContainedIds(log: log) log.printMetadataScanOverview(languages: root.languages.count) return (root, ids) } private func generatePages(from root: Element, configuration: Configuration, fileUpdates: FileUpdateChecker, ids: [String: String], runFolder: URL) -> (ImageData, FileData)? { print("--- GENERATION -------------------------------------") print(" ") let pageCount = ids.count * root.languages.count let results = GenerationResultsHandler( in: configuration.contentDirectory, to: configuration.outputDirectory, configuration: configuration, fileUpdates: fileUpdates, pagePaths: ids, 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) { print("----------------------------------------------------") 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("") } private func generate(configPath: String) throws { let start = Date() // 1. Load configuration guard let configuration = loadConfiguration(at: configPath) else { return } let runFolder = configuration.contentDirectory.appendingPathComponent("run") // 2. Scan site elements guard let (siteRoot, ids) = 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, ids: ids, 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) // 6. Print summary finish(start: start) }