
Meta.pudu
Pudu150 lines5.4 KB
1/** @Docgen.Meta.Module — metadata values from configuration, files, and front matter */2module PuduLangDocgen.Meta34import Std.Glob as Glob5import Std.Json as Json6import Std.Yaml as Yaml7import PuduLangDocgen as Docgen89/** @Docgen.Meta.Rule — one metadata key assigned to every path a glob matches */10export type Rule = { pattern: Str, key: Str, value: Docgen.Meta }111213export fn fromJson(value: &Json.Json) -> Docgen.Meta {14 match value {15 case Json.Null => Docgen.Nothing16 case Json.Boolean(held) => Docgen.Flag(held)17 case Json.Number(count) => Docgen.Whole(count)18 case Json.Fractional(written) => Docgen.Text(written)19 case Json.Text(written) => Docgen.Text(written)20 case Json.List(members) => Docgen.Items(members.map(|member: Json.Json| fromJson(&member)))21 case Json.Object(entries) => Docgen.Fields(entries.map(fn(entry: (Str, Json.Json)) -> (Str, Docgen.Meta) {22 let (key, held) = entry23 (key, fromJson(&held))24 }))25 }26}272829export fn fromYaml(value: &Yaml.Yaml) -> Docgen.Meta {30 match value {31 case Yaml.Empty => Docgen.Nothing32 case Yaml.Boolean(held) => Docgen.Flag(held)33 case Yaml.Whole(count) => Docgen.Whole(count)34 case Yaml.Fractional(written) => Docgen.Text(written)35 case Yaml.Text(written) => Docgen.Text(written)36 case Yaml.List(members) => Docgen.Items(members.map(|member: Yaml.Yaml| fromYaml(&member)))37 case Yaml.Mapping(entries) => Docgen.Fields(entries.map(fn(entry: (Str, Yaml.Yaml)) -> (Str, Docgen.Meta) {38 let (key, held) = entry39 (key, fromYaml(&held))40 }))41 }42}434445export fn toJson(value: &Docgen.Meta) -> Json.Json {46 match value {47 case Docgen.Nothing => Json.Null48 case Docgen.Flag(held) => Json.Boolean(held)49 case Docgen.Whole(count) => Json.Number(count)50 case Docgen.Text(written) => Json.Text(written)51 case Docgen.Items(members) => Json.List(members.map(|member: Docgen.Meta| toJson(&member)))52 case Docgen.Fields(entries) => Json.Object(entries.map(fn(entry: (Str, Docgen.Meta)) -> (Str, Json.Json) {53 let (key, held) = entry54 (key, toJson(&held))55 }))56 }57}585960export fn get(fields: &Array[(Str, Docgen.Meta)], key: Str) -> Option[Docgen.Meta] {61 for (name, held) in *fields {62 if name == key { return Some(held) }63 }64 None65}666768export fn text(fields: &Array[(Str, Docgen.Meta)], key: Str) -> Option[Str] {69 match get(fields, key) {70 case Some(Docgen.Text(written)) => Some(written)71 case Some(Docgen.Whole(count)) => Some(show(count))72 case Some(Docgen.Flag(held)) => Some(if held { "true" } else { "false" })73 case _ => None74 }75}767778export fn textOr(fields: &Array[(Str, Docgen.Meta)], key: Str, fallback: Str) -> Str {79 match text(fields, key) {80 case Some(written) => written81 case None => fallback82 }83}848586export fn flag(fields: &Array[(Str, Docgen.Meta)], key: Str) -> Bool {87 match get(fields, key) {88 case Some(Docgen.Flag(held)) => held89 case Some(Docgen.Text(written)) => written == "true"90 case _ => false91 }92}939495export fn fieldsOf(fields: &Array[(Str, Docgen.Meta)], key: Str) -> Array[(Str, Docgen.Meta)] {96 match get(fields, key) {97 case Some(Docgen.Fields(entries)) => entries98 case _ => []99 }100}101102103export fn put(fields: &Array[(Str, Docgen.Meta)], key: Str, value: Docgen.Meta) -> Array[(Str, Docgen.Meta)] {104 var result: Array[(Str, Docgen.Meta)] = []105 var replaced = false106 for (name, held) in *fields {107 if name == key {108 result = result.push((key, value))109 replaced = true110 } else { result = result.push((name, held)) }111 }112 if replaced { result } else { result.push((key, value)) }113}114115116export fn merge(base: &Array[(Str, Docgen.Meta)], over: &Array[(Str, Docgen.Meta)]) -> Array[(Str, Docgen.Meta)] {117 var result = *base118 for (key, held) in *over {119 let combined = match (get(&result, key), held) {120 case (Some(Docgen.Fields(left)), Docgen.Fields(right)) => Docgen.Fields(merge(&left, &right))121 case (_, other) => other122 }123 result = put(&result, key, combined)124 }125 result126}127128129export fn rulesOf(fields: &Array[(Str, Docgen.Meta)]) -> Result[Array[Rule], Str] {130 var rules: Array[Rule] = []131 for (key, held) in *fields {132 match held {133 case Docgen.Fields(patterns) => {134 for (pattern, value) in patterns { rules = rules.push(Rule{pattern: pattern, key: key, value: value}) }135 }136 case _ => { return Err("file metadata " + key + " must map glob patterns to values") }137 }138 }139 Ok(rules)140}141142143export fn forPath(rules: &Array[Rule], path: Str) -> Array[(Str, Docgen.Meta)] {144 var result: Array[(Str, Docgen.Meta)] = []145 for rule in *rules {146 if Glob.matches(rule.pattern, path) { result = merge(&result, &[(rule.key, rule.value)]) }147 }148 result149}150