// YAML-like frontmatter parser // Parses the --- delimited frontmatter block from markdown files pub type Frontmatter = | Frontmatter(String, String, String, String) // title date desc tagsRaw pub type ParseResult = | ParseResult(Frontmatter, String) // front body pub fn parse(content: String): ParseResult = { let lines = String.lines(content); // Skip first --- line, collect key: value pairs until next --- let result = List.fold(lines, (false, false, "", "", "", "", ""), fn(acc: (Bool, Bool, String, String, String, String, String), line: String): (Bool, Bool, String, String, String, String, String) => { let inFront = acc.0; let pastFront = acc.1; let title = acc.2; let date = acc.3; let desc = acc.4; let tags = acc.5; let body = acc.6; if pastFront then (inFront, pastFront, title, date, desc, tags, body + line + "\n") else if String.trim(line) == "---" then if inFront then // End of frontmatter (false, true, title, date, desc, tags, body) else // Start of frontmatter (true, false, title, date, desc, tags, body) else if inFront then { // Parse key: value match String.indexOf(line, ": ") { Some(idx) => { let key = String.trim(String.substring(line, 0, idx)); let rawVal = String.trim(String.substring(line, idx + 2, String.length(line))); // Strip surrounding quotes if present let val = if String.startsWith(rawVal, "\"") then String.substring(rawVal, 1, String.length(rawVal) - 1) else rawVal; if key == "title" then (inFront, pastFront, val, date, desc, tags, body) else if key == "date" then (inFront, pastFront, title, val, desc, tags, body) else if key == "description" then (inFront, pastFront, title, date, val, tags, body) else if key == "tags" then (inFront, pastFront, title, date, desc, val, body) else (inFront, pastFront, title, date, desc, tags, body) }, None => (inFront, pastFront, title, date, desc, tags, body) } } else (inFront, pastFront, title, date, desc, tags, body) }); let front = Frontmatter(result.2, result.3, result.4, result.5); ParseResult(front, result.6) } pub fn getTitle(f: Frontmatter): String = match f { Frontmatter(t, _, _, _) => t } pub fn getDate(f: Frontmatter): String = match f { Frontmatter(_, d, _, _) => d } pub fn getDesc(f: Frontmatter): String = match f { Frontmatter(_, _, d, _) => d } pub fn getTagsRaw(f: Frontmatter): String = match f { Frontmatter(_, _, _, t) => t } pub fn getTags(f: Frontmatter): List = match f { Frontmatter(_, _, _, t) => if t == "" then [] else String.split(t, " ") }