2025-01-06 01:17:06 +01:00

87 lines
2.3 KiB
Swift

enum ContentBlock: String, CaseIterable {
case audio
case swift
var processor: BlockProcessor.Type {
switch self {
case .audio: return AudioBlockProcessor.self
case .swift: return SwiftBlockProcessor.self
}
}
}
protocol BlockProcessor {
static var blockId: ContentBlock { get }
var results: PageGenerationResults { get }
init(content: Content, results: PageGenerationResults, language: ContentLanguage)
func process(_ markdown: Substring) -> String
}
extension BlockProcessor {
func invalid(_ markdown: Substring) {
results.invalid(block: Self.blockId, markdown)
}
}
protocol BlockLineProcessor: BlockProcessor {
func process(_ lines: [String], markdown: Substring) -> String
}
extension BlockLineProcessor {
func process(_ markdown: Substring) -> String {
let lines = markdown
.between("```\(Self.blockId.self)", and: "```")
.components(separatedBy: "\n")
return process(lines, markdown: markdown)
}
}
protocol OrderedKeyBlockProcessor: BlockLineProcessor {
associatedtype Key: Hashable, RawRepresentable where Key.RawValue == String
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String
}
extension OrderedKeyBlockProcessor {
func process(_ lines: [String], markdown: Substring) -> String {
let result: [(key: Key, value: String)] = lines.compactMap { line in
guard line.trimmed != "" else {
return nil
}
let (rawKey, rawValue) = line.splitAtFirst(":")
guard let key = Key(rawValue: rawKey.trimmed) else {
print("Invalid key \(rawKey)")
invalid(markdown)
return nil
}
return (key, rawValue.trimmed)
}
return process(result, markdown: markdown)
}
}
protocol KeyedBlockProcessor: OrderedKeyBlockProcessor {
func process(_ arguments: [Key : String], markdown: Substring) -> String
}
extension KeyedBlockProcessor {
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String {
let result = arguments.reduce(into: [:]) { $0[$1.key] = $1.value }
return process(result, markdown: markdown)
}
}