///|
/// A YAML payload and its parsed mapping. Construct with `Frontmatter::Frontmatter`.
/// The representation is hidden so text and parsed YAML always agree.
struct Frontmatter {
raw : String
value : @yaml.Yaml
} derive(Debug)
///|
pub extend Frontmatter with @debug.Debug::{to_repr}
///|
/// Validate a YAML payload without delimiter lines and preserve its exact text.
/// Empty and comment-only payloads become empty mappings. Reject invalid YAML,
/// multiple documents and non-mappings. Error positions are one-based and
/// relative to this payload; `parse` translates them to document positions.
pub fn Frontmatter::Frontmatter(raw : String) -> Frontmatter raise ParseError {
let values = @yaml.Yaml::load_from_string(raw[:]) catch {
@yaml.YamlError(mark~, info~) =>
raise InvalidFrontmatter(
line=mark.line,
column=mark.col + 1,
message=info,
)
}
let value = if values.is_empty() {
@yaml.Yaml::Map({})
} else if values.length() == 1 && values[0] is Map(_) {
values[0]
} else {
raise InvalidFrontmatter(
line=1,
column=1,
message="frontmatter must be a YAML mapping",
)
}
{ raw, value, }
}
///|
/// The original YAML payload, excluding delimiter lines.
pub fn Frontmatter::raw(self : Frontmatter) -> String {
self.raw
}
///|
/// A deep copy of the parsed YAML mapping. Changes to returned maps and arrays
/// cannot change this frontmatter or make its stored YAML disagree with `raw`.
pub fn Frontmatter::value(self : Frontmatter) -> @yaml.Yaml {
copy_yaml(self.value)
}
///|
fn copy_yaml(value : @yaml.Yaml) -> @yaml.Yaml {
match value {
Map(fields) => {
let copy = Map([])
for key, value in fields {
copy[key] = copy_yaml(value)
}
Map(copy)
}
Array(values) => Array(values.map(copy_yaml))
Real(_) | Integer(_) | String(_) | Boolean(_) | Null | BadValue => value
}
}