Refactor page content generators

This commit is contained in:
Christoph Hagen
2025-01-06 10:00:51 +01:00
parent 245534e989
commit 301dbad0a5
36 changed files with 760 additions and 566 deletions

View File

@ -1,86 +0,0 @@
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)
}
}

View File

@ -1,33 +0,0 @@
struct CodeBlockProcessor {
private let codeHighlightFooter = "<script>hljs.highlightAll();</script>"
let results: PageGenerationResults
private let blocks: [ContentBlock : BlockProcessor]
private let other: OtherCodeProcessor
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
self.results = results
self.other = .init(results: results)
self.blocks = ContentBlock.allCases.reduce(into: [:]) { blocks, block in
blocks[block] = block.processor.init(content: content, results: results, language: language)
}
}
func process(_ html: String, markdown: Substring) -> String {
let input = String(markdown)
let rawBlockId = input.dropAfterFirst("\n").dropBeforeFirst("```").trimmed
guard let blockId = ContentBlock(rawValue: rawBlockId) else {
return other.process(html: html)
}
guard let processor = self.blocks[blockId] else {
results.invalid(block: blockId, markdown)
return ""
}
return processor.process(markdown)
}
}

View File

@ -0,0 +1,14 @@
enum ContentBlock: String, CaseIterable {
case audio
case swift
var processor: BlockProcessor.Type {
switch self {
case .audio: return AudioBlockProcessor.self
case .swift: return SwiftBlockProcessor.self
}
}
}

View File

@ -0,0 +1,15 @@
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)
}
}

View File

@ -0,0 +1,18 @@
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)
}
}

View File

@ -0,0 +1,13 @@
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)
}
}

View File

@ -0,0 +1,26 @@
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)
}
}