ChWebsiteApp/CHDataManagement/Generator/GenerationResults.swift
2025-01-05 09:21:21 +01:00

197 lines
5.9 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 warnings: Set<String> = []
@Published
var unsavedOutputFiles: Set<String> = []
@Published
var failedImages: Set<ImageVersion> = []
@Published
var emptyPages: Set<LocalizedPageId> = []
/// The cache of previously used GenerationResults
private var cache: [ItemId : PageGenerationResults] = [:]
private(set) var general: PageGenerationResults!
@Published
var resultCount: Int = 0
// MARK: Life cycle
init() {
let id = ItemId(language: .english, itemType: .general)
let general = PageGenerationResults(itemId: id, delegate: self)
self.general = general
cache[id] = general
self.resultCount = 1
}
func makeResults(_ itemId: ItemId) -> 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: ItemType, in language: ContentLanguage) -> PageGenerationResults {
let itemId = ItemId(language: language, itemType: type)
return makeResults(itemId)
}
func makeResults(for page: Page, in language: ContentLanguage) -> PageGenerationResults {
let itemId = ItemId(language: language, itemType: .page(page))
return makeResults(itemId)
}
func makeResults(for tag: Tag, in language: ContentLanguage) -> PageGenerationResults {
let itemId = ItemId(language: language, itemType: .tagPage(tag))
return makeResults(itemId)
}
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 warnings = cache.values.map { $0.warnings }.union()
update { self.warnings = warnings }
let unsavedOutputFiles = cache.values.map { $0.unsavedOutputFiles.keys }.union()
update { self.unsavedOutputFiles = unsavedOutputFiles }
}
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 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) }
}
}
private extension Dictionary where Value == Set<ItemId> {
mutating func remove<S>(keys: S, of item: ItemId) 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
}
}
}
}