import Foundation typealias LinkedElement = (previous: Element?, element: Element, next: Element?) struct SiteGenerator { let templates: TemplateFactory let results: GenerationResultsHandler init(results: GenerationResultsHandler) throws { self.results = results let templatesFolder = results.contentFolder.appendingPathComponent("templates") self.templates = try TemplateFactory(templateFolder: templatesFolder, results: results) } func generate(site: Element) { site.languages.forEach { generate(site: site, metadata: $0) } } private func generate(site: Element, metadata: Element.LocalizedMetadata) { let language = metadata.language let template = LocalizedSiteTemplate( factory: templates, language: language, site: site, results: results) // Generate sections let overviewGenerator = OverviewPageGenerator(factory: template, results: results) let pageGenerator = PageGenerator(factory: template, siteRoot: site, results: results) var elementsToProcess: [LinkedElement] = [(nil, site, nil)] while let (previous, element, next) = elementsToProcess.popLast() { // Move recursively down to all pages elementsToProcess.append(contentsOf: element.linkedElements) processAllFiles(for: element) if !element.elements.isEmpty { overviewGenerator.generate(section: element, language: language) } else { pageGenerator.generate( page: element, language: language, previousPage: previous, nextPage: next) } } } private func processAllFiles(for element: Element) { element.externalFiles.forEach { results.exclude(file: $0, source: element.path) } element.requiredFiles.forEach { results.require(file: $0, source: element.path) } element.images.forEach { results.requireSingleImage( source: $0.sourcePath, destination: $0.destinationPath, requiredBy: element.path, width: $0.desiredWidth, desiredHeight: $0.desiredHeight) } } }