enum GenerationAnomaly { case failedToLoadContent case failedToParseContent case missingFile(file: String, markdown: String) case missingPage(page: String, markdown: String) case missingTag(tag: String, markdown: String) case invalidCommand(command: ShorthandMarkdownKey?, markdown: String) case warning(String) } extension GenerationAnomaly: Identifiable { var id: String { switch self { case .failedToLoadContent: return "load-failed" case .failedToParseContent: return "parse-failed" case .missingFile(let string, _): return "missing-file-\(string)" case .missingPage(let string, _): return "missing-page-\(string)" case .missingTag(let string, _): return "missing-tag-\(string)" case .invalidCommand(_, let markdown): return "invalid-command-\(markdown)" case .warning(let string): return "warning-\(string)" } } } extension GenerationAnomaly: Equatable { static func == (lhs: GenerationAnomaly, rhs: GenerationAnomaly) -> Bool { lhs.id == rhs.id } } extension GenerationAnomaly: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(id) } } extension GenerationAnomaly { enum Severity: String, CaseIterable { case warning case error } var severity: Severity { switch self { case .failedToLoadContent, .failedToParseContent: return .error case .missingFile, .missingPage, .missingTag, .invalidCommand, .warning: return .warning } } } extension GenerationAnomaly: CustomStringConvertible { var description: String { switch self { case .failedToLoadContent: return "Failed to load content" case .failedToParseContent: return "Failed to parse content" case .missingFile(let string, _): return "Missing file: \(string)" case .missingPage(let string, _): return "Missing page: \(string)" case .missingTag(let string, _): return "Missing tag: \(string)" case .invalidCommand(_, let markdown): return "Invalid command: \(markdown)" case .warning(let string): return "Warning: \(string)" } } }