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