use std::collections::BTreeMap;
use crate::doctree::{kinds, Doctree, Node};
fn is_pre_bibliographic(node: &Node) -> bool {
matches!(
node.kind,
kinds::COMMENT | kinds::TARGET | kinds::SYSTEM_MESSAGE | kinds::TITLE | kinds::SUBTITLE
) || node.kind == "substitution_definition"
|| node.kind == "pending"
|| node.kind == "raw"
|| node.kind == "meta"
|| node.kind == "decoration"
}
pub fn document_metadata(doctree: &Doctree) -> BTreeMap<String, String> {
let mut metadata = BTreeMap::new();
let Some(first) = doctree
.root
.children
.iter()
.find(|node| !is_pre_bibliographic(node))
else {
return metadata;
};
if first.kind != kinds::FIELD_LIST {
return metadata;
}
for field in &first.children {
if field.kind != kinds::FIELD {
continue;
}
let name = field
.children
.iter()
.find(|child| child.kind == kinds::FIELD_NAME);
let body = field
.children
.iter()
.find(|child| child.kind == kinds::FIELD_BODY);
if let Some(name) = name {
metadata.insert(name.astext(), body.map(Node::astext).unwrap_or_default());
}
}
metadata
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rst;
fn parse(source: &str) -> Doctree {
rst::parse_rst(source, &rst::ParseOptions::default())
}
#[test]
fn leading_field_list_becomes_metadata() {
let metadata = document_metadata(&parse(":orphan:\n\nTitle\n=====\n\nBody.\n"));
assert_eq!(metadata.get("orphan").map(String::as_str), Some(""));
}
#[test]
fn field_bodies_are_captured() {
let metadata = document_metadata(&parse(":tocdepth: 2\n:nocomments:\n\nBody.\n"));
assert_eq!(metadata.get("tocdepth").map(String::as_str), Some("2"));
assert!(metadata.contains_key("nocomments"));
}
#[test]
fn comments_and_targets_do_not_hide_the_field_list() {
let metadata = document_metadata(&parse(".. a comment\n\n:orphan:\n\nBody.\n"));
assert!(metadata.contains_key("orphan"), "{metadata:?}");
}
#[test]
fn a_leading_raw_block_does_not_hide_the_field_list() {
let metadata = document_metadata(&parse(".. raw:: html\n\n <hr>\n\n:orphan:\n\nBody.\n"));
assert_eq!(metadata.get("orphan").map(String::as_str), Some(""));
}
#[test]
fn a_field_list_below_the_first_body_element_is_not_metadata() {
let metadata = document_metadata(&parse("Body.\n\n:orphan:\n"));
assert!(metadata.is_empty(), "{metadata:?}");
}
#[test]
fn a_document_without_fields_has_no_metadata() {
assert!(document_metadata(&parse("Title\n=====\n")).is_empty());
}
}