// // Document.swift // GeminiFormat // // Created by Shadowfacts on 7/12/20. // import Foundation public struct Document { public let url: URL public var lines: [Line] public init(url: URL, lines: [Line] = []) { self.url = url self.lines = lines } /// Renders this Document to Gemini formatted text. This is not guaranteed to be the original input text. public func toGeminiText() -> String { // todo: should this be \r\n return lines.map { $0.geminiText() }.joined(separator: "\n") } public var title: String? { for case let .heading(text, level: _) in lines { return text } return nil } } public extension Document { enum Line: Equatable { case text(String) case link(URL, text: String?) case preformattedToggle(alt: String?) case preformattedText(String) case heading(String, level: HeadingLevel) case unorderedListItem(String) case quote(String) func geminiText() -> String { switch self { case let .text(text): return text case let .link(url, text: text): if let text = text { return "=> \(url.absoluteString) \(text)" } else { return "=> \(url.absoluteString)" } case let .preformattedToggle(alt: alt): if let alt = alt { return "```\(alt)" } else { return "```" } case let .preformattedText(text): return text case let .heading(text, level: level): return "\(level.geminiText) \(text)" case let .unorderedListItem(text): return "* \(text)" case let .quote(text): return "> \(text)" } } } } public extension Document { enum HeadingLevel: Int, Comparable { case h1 = 1, h2 = 2, h3 = 3 var geminiText: String { switch self { case .h1: return "#" case .h2: return "##" case .h3: return "###" } } public static func < (lhs: Document.HeadingLevel, rhs: Document.HeadingLevel) -> Bool { return lhs.rawValue < rhs.rawValue } } }