drft/builders/
frontmatter.rs1use globset::GlobSet;
7
8use crate::builders::link_edges;
9use crate::model::{Graph, Node};
10use crate::parsers::Parser;
11use crate::parsers::frontmatter::FrontmatterParser;
12
13pub fn build(
19 label: &str,
20 texts: &[(String, String)],
21 filter: Option<GlobSet>,
22 keys: Option<Vec<String>>,
23) -> Graph {
24 let parser = FrontmatterParser {
25 file_filter: filter,
26 keys,
27 };
28 let mut graph = Graph::labeled(label);
29
30 for (path, content) in texts {
31 if !parser.matches(path) {
32 continue;
33 }
34 let result = parser.parse(path, content);
35
36 if let Some(serde_json::Value::Object(block)) = result.metadata {
37 graph.set_node(path.clone(), Node::new(block));
38 }
39
40 for edge in link_edges(path, &result.links) {
41 graph.add_edge(edge);
42 }
43 }
44
45 graph.sort_edges();
46 graph
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 fn texts(pairs: &[(&str, &str)]) -> Vec<(String, String)> {
54 pairs
55 .iter()
56 .map(|(p, c)| (p.to_string(), c.to_string()))
57 .collect()
58 }
59
60 #[test]
61 fn emits_metadata_and_edges() {
62 let t = texts(&[(
63 "analysis.md",
64 "---\ntitle: Analysis\nstatus: draft\nsources:\n - ./data/notes.md\n---\n\n# Body\n",
65 )]);
66 let graph = build("frontmatter", &t, None, None);
67 assert_eq!(graph.label.as_deref(), Some("frontmatter"));
68
69 let meta = &graph.nodes["analysis.md"].metadata;
70 assert_eq!(meta["title"], serde_json::json!("Analysis"));
71 assert_eq!(meta["status"], serde_json::json!("draft"));
72
73 let targets: Vec<&str> = graph.edges.iter().map(|e| e.target.as_str()).collect();
74 assert_eq!(targets, vec!["data/notes.md"]);
75 }
76
77 #[test]
78 fn no_node_without_frontmatter() {
79 let t = texts(&[("plain.md", "# Just a heading\n")]);
80 let graph = build("frontmatter", &t, None, None);
81 assert!(graph.nodes.is_empty());
82 assert!(graph.edges.is_empty());
83 }
84}