248 lines
7.8 KiB
Swift
248 lines
7.8 KiB
Swift
import Foundation
|
|
|
|
final class GenerationResults: ObservableObject {
|
|
|
|
/// The files that could not be accessed
|
|
@Published
|
|
var inaccessibleFiles: Set<FileResource> = []
|
|
|
|
/// The files that could not be parsed, with the error message produced
|
|
@Published
|
|
var unparsableFiles: Set<FileResource> = []
|
|
|
|
@Published
|
|
var missingFiles: Set<String> = []
|
|
|
|
@Published
|
|
var missingTags: Set<String> = []
|
|
|
|
@Published
|
|
var missingPages: Set<String> = []
|
|
|
|
@Published
|
|
var externalLinks: Set<String> = []
|
|
|
|
@Published
|
|
var requiredFiles: Set<FileResource> = []
|
|
|
|
@Published
|
|
var imagesToGenerate: Set<ImageVersion> = []
|
|
|
|
@Published
|
|
var invalidCommands: Set<String> = []
|
|
|
|
@Published
|
|
var invalidBlocks: Set<String> = []
|
|
|
|
@Published
|
|
var warnings: Set<String> = []
|
|
|
|
@Published
|
|
var unsavedOutputFiles: Set<String> = []
|
|
|
|
@Published
|
|
var failedImages: Set<ImageVersion> = []
|
|
|
|
@Published
|
|
var emptyPages: Set<LocalizedPageId> = []
|
|
|
|
/**
|
|
The url redirects to install to prevent broken links.
|
|
|
|
The key is the original url, and the value the new url of the content, relative to the output folder.
|
|
The dictionary is used to create a map of redirects for Nginx.
|
|
*/
|
|
@Published
|
|
var redirects: [String : String] = [:]
|
|
|
|
/// The cache of previously used GenerationResults
|
|
private var cache: [LocalizedItemId : PageGenerationResults] = [:]
|
|
|
|
private(set) var general: PageGenerationResults!
|
|
|
|
@Published
|
|
var resultCount: Int = 0
|
|
|
|
// MARK: Life cycle
|
|
|
|
init() {
|
|
let id = LocalizedItemId(language: .english, itemType: .general)
|
|
let general = PageGenerationResults(itemId: id, delegate: self)
|
|
self.general = general
|
|
cache[id] = general
|
|
self.resultCount = 0
|
|
}
|
|
|
|
func makeResults(_ itemId: LocalizedItemId) -> PageGenerationResults {
|
|
guard let result = cache[itemId] else {
|
|
let result = PageGenerationResults(itemId: itemId, delegate: self)
|
|
cache[itemId] = result
|
|
update { self.resultCount += 1 }
|
|
return result
|
|
}
|
|
return result
|
|
}
|
|
|
|
func makeResults(for type: ItemReference, in language: ContentLanguage) -> PageGenerationResults {
|
|
let itemId = LocalizedItemId(language: language, itemType: type)
|
|
return makeResults(itemId)
|
|
}
|
|
|
|
func makeResults(for page: Page, in language: ContentLanguage) -> PageGenerationResults {
|
|
let itemId = LocalizedItemId(language: language, itemType: .page(page))
|
|
return makeResults(itemId)
|
|
}
|
|
|
|
func makeResults(for tag: Tag, in language: ContentLanguage) -> PageGenerationResults {
|
|
let itemId = LocalizedItemId(language: language, itemType: .tagPage(tag))
|
|
return makeResults(itemId)
|
|
}
|
|
|
|
func reset() {
|
|
update {
|
|
self.inaccessibleFiles = []
|
|
self.unparsableFiles = []
|
|
self.missingFiles = []
|
|
self.missingTags = []
|
|
self.missingPages = []
|
|
self.externalLinks = []
|
|
self.requiredFiles = []
|
|
self.imagesToGenerate = []
|
|
self.invalidCommands = []
|
|
self.invalidBlocks = []
|
|
self.warnings = []
|
|
self.unsavedOutputFiles = []
|
|
self.emptyPages = []
|
|
self.redirects = [:]
|
|
}
|
|
for result in cache.values {
|
|
result.reset()
|
|
}
|
|
}
|
|
|
|
func recalculate() {
|
|
let inaccessibleFiles = cache.values.map { $0.inaccessibleFiles }.union()
|
|
update { self.inaccessibleFiles = inaccessibleFiles }
|
|
let unparsableFiles = cache.values.map { $0.unparsableFiles.keys }.union()
|
|
update { self.unparsableFiles = unparsableFiles }
|
|
let missingFiles = cache.values.map { $0.missingFiles.keys }.union()
|
|
update { self.missingFiles = missingFiles }
|
|
let missingTags = cache.values.map { $0.missingLinkedTags.keys }.union()
|
|
update { self.missingTags = missingTags }
|
|
let missingPages = cache.values.map { $0.missingLinkedPages.keys }.union()
|
|
update { self.missingPages = missingPages }
|
|
let externalLinks = cache.values.map { $0.externalLinks }.union()
|
|
update { self.externalLinks = externalLinks }
|
|
let requiredFiles = cache.values.map { $0.requiredFiles }.union()
|
|
update { self.requiredFiles = requiredFiles }
|
|
let imagesToGenerate = cache.values.map { $0.imagesToGenerate }.union()
|
|
update { self.imagesToGenerate = imagesToGenerate }
|
|
let invalidCommands = cache.values.map { $0.invalidCommands.map { $0.markdown }}.union()
|
|
update { self.invalidCommands = invalidCommands }
|
|
let invalidBlocks = cache.values.map { $0.invalidBlocks.map { $0.markdown }}.union()
|
|
update { self.invalidBlocks = invalidBlocks }
|
|
let warnings = cache.values.map { $0.warnings }.union()
|
|
update { self.warnings = warnings }
|
|
let unsavedOutputFiles = cache.values.map { $0.unsavedOutputFiles.keys }.union()
|
|
update { self.unsavedOutputFiles = unsavedOutputFiles }
|
|
let emptyPages = cache.values.filter { $0.pageIsEmpty }.map { $0.itemId }.compactMap { id -> LocalizedPageId? in
|
|
guard case .page(let page) = id.itemType else { return nil }
|
|
return LocalizedPageId(language: id.language, pageId: page.id)
|
|
}.asSet()
|
|
update { self.emptyPages = emptyPages }
|
|
let redirects = cache.values.compactMap { $0.redirect }.reduce(into: [:]) { $0[$1.originalUrl] = $1.newUrl }
|
|
update { self.redirects = redirects }
|
|
}
|
|
|
|
private func update(_ operation: @escaping () -> Void) {
|
|
DispatchQueue.main.async {
|
|
operation()
|
|
}
|
|
}
|
|
|
|
// MARK: Adding entries
|
|
|
|
func inaccessibleContent(file: FileResource) {
|
|
update { self.inaccessibleFiles.insert(file) }
|
|
}
|
|
|
|
func unparsable(file: FileResource) {
|
|
update { self.unparsableFiles.insert(file) }
|
|
}
|
|
|
|
func missing(file: String) {
|
|
update { self.missingFiles.insert(file) }
|
|
}
|
|
|
|
func missing(tag: String) {
|
|
update { self.missingTags.insert(tag) }
|
|
}
|
|
|
|
func missing(page: String) {
|
|
update { self.missingPages.insert(page) }
|
|
}
|
|
|
|
func externalLink(_ url: String) {
|
|
update { self.externalLinks.insert(url) }
|
|
}
|
|
|
|
func require(file: FileResource) {
|
|
update { self.requiredFiles.insert(file) }
|
|
}
|
|
|
|
func require<S>(files: S) where S: Sequence, S.Element == FileResource {
|
|
update { self.requiredFiles.formUnion(files) }
|
|
}
|
|
|
|
func generate(_ image: ImageVersion) {
|
|
update { self.imagesToGenerate.insert(image) }
|
|
}
|
|
|
|
func generate<S>(_ images: S) where S: Sequence, S.Element == ImageVersion {
|
|
update { self.imagesToGenerate.formUnion(images) }
|
|
}
|
|
|
|
func invalidCommand(_ markdown: String) {
|
|
update { self.invalidCommands.insert(markdown) }
|
|
}
|
|
|
|
func invalidBlock(_ markdown: String) {
|
|
update { self.invalidBlocks.insert(markdown) }
|
|
}
|
|
|
|
func warning(_ warning: String) {
|
|
update { self.warnings.insert(warning) }
|
|
}
|
|
|
|
func failed(image: ImageVersion) {
|
|
update { self.failedImages.insert(image) }
|
|
}
|
|
|
|
func unsaved(_ path: String) {
|
|
update { self.unsavedOutputFiles.insert(path) }
|
|
}
|
|
|
|
func empty(_ page: LocalizedPageId) {
|
|
update { self.emptyPages.insert(page) }
|
|
}
|
|
|
|
func redirect(from originalUrl: String, to newUrl: String) {
|
|
update { self.redirects[originalUrl] = newUrl }
|
|
}
|
|
}
|
|
|
|
private extension Dictionary where Value == Set<LocalizedItemId> {
|
|
|
|
mutating func remove<S>(keys: S, of item: LocalizedItemId) where S: Sequence, S.Element == Key {
|
|
for key in keys {
|
|
guard var value = self[key] else { continue }
|
|
value.remove(item)
|
|
if value.isEmpty {
|
|
self[key] = nil
|
|
} else {
|
|
self[key] = value
|
|
}
|
|
}
|
|
}
|
|
}
|