blazegraph_io_core/graphs/
graph.rs1use super::analytics::GraphAnalytics;
2use crate::types::*;
3use anyhow::Result;
4use std::collections::HashMap;
5
6impl Default for DocumentGraph {
7 fn default() -> Self {
8 Self::new()
9 }
10}
11
12impl DocumentGraph {
13 pub fn new_with_root(root_id: NodeId) -> Self {
15 use crate::types::{DocumentInfo, DocumentMetadata};
16
17 let document_info = DocumentInfo {
18 root_id,
19 document_metadata: DocumentMetadata::default(),
20 bookmark_data: None,
21 };
22
23 Self {
24 nodes: HashMap::new(),
25 document_info,
26 structural_profile: StructuralProfile::default(),
27 }
28 }
29
30 pub fn new() -> Self {
32 use crate::types::{DocumentInfo, DocumentMetadata};
33 use uuid::Uuid;
34
35 let document_info = DocumentInfo {
36 root_id: Uuid::new_v4(),
37 document_metadata: DocumentMetadata::default(),
38 bookmark_data: None,
39 };
40
41 Self {
42 nodes: HashMap::new(),
43 document_info,
44 structural_profile: StructuralProfile::default(),
45 }
46 }
47
48 pub fn max_depth(&self) -> u32 {
49 self.nodes
50 .values()
51 .map(|n| n.location.semantic.depth)
52 .max()
53 .unwrap_or(0)
54 }
55
56 pub fn save_to_json(&self, path: &str) -> Result<()> {
57 let sorted_graph = self.to_sorted_graph();
58 let json = serde_json::to_string_pretty(&sorted_graph)?;
59 std::fs::write(path, json)?;
60 Ok(())
61 }
62
63 pub fn to_sorted_graph(&self) -> SortedDocumentGraph {
64 let mut nodes: Vec<&DocumentNode> = self.nodes.values().collect();
66 nodes.sort_by(|a, b| {
67 match (a.text_order, b.text_order) {
69 (None, None) => std::cmp::Ordering::Equal,
70 (None, Some(_)) => std::cmp::Ordering::Less,
71 (Some(_), None) => std::cmp::Ordering::Greater,
72 (Some(a_order), Some(b_order)) => a_order.cmp(&b_order),
73 }
74 });
75
76 SortedDocumentGraph {
77 schema_version: SCHEMA_VERSION.to_string(),
78 nodes: nodes.into_iter().cloned().collect(),
79 document_info: self.document_info.clone(),
80 structural_profile: self.structural_profile.clone(),
81 }
82 }
83
84 pub fn compute_breadcrumbs(&mut self) {
89 let root_id = self.document_info.root_id;
90
91 let root_breadcrumbs: Vec<String> = self
93 .document_info
94 .document_metadata
95 .title
96 .as_ref()
97 .filter(|t| !t.is_empty())
98 .map(|t| vec![t.clone()])
99 .unwrap_or_default();
100
101 if let Some(doc_node) = self.nodes.get_mut(&root_id) {
103 doc_node.location.semantic.breadcrumbs = root_breadcrumbs.clone();
104 }
105
106 let root_children: Vec<NodeId> = self
108 .nodes
109 .get(&root_id)
110 .map(|n| n.children.clone())
111 .unwrap_or_default();
112
113 for child_id in root_children {
114 self.propagate_breadcrumbs(child_id, &root_breadcrumbs);
115 }
116 }
117
118 fn propagate_breadcrumbs(&mut self, node_id: NodeId, parent_breadcrumbs: &[String]) {
120 let (node_breadcrumbs, children) = {
122 let node = match self.nodes.get(&node_id) {
123 Some(n) => n,
124 None => return,
125 };
126
127 let breadcrumbs = if node.node_type == "Section" {
128 let mut crumbs = parent_breadcrumbs.to_vec();
130 crumbs.push(node.content.text.clone());
131 crumbs
132 } else {
133 parent_breadcrumbs.to_vec()
135 };
136
137 (breadcrumbs, node.children.clone())
138 };
139
140 if let Some(node) = self.nodes.get_mut(&node_id) {
142 node.location.semantic.breadcrumbs = node_breadcrumbs.clone();
143 }
144
145 for child_id in children {
147 self.propagate_breadcrumbs(child_id, &node_breadcrumbs);
148 }
149 }
150
151 pub fn _analyze_subtree(&self, root_node_id: NodeId) -> Option<GraphAnalyticsResult> {
153 let subtree_nodes = self._collect_subtree_nodes(root_node_id);
154 if subtree_nodes.is_empty() {
155 return None;
156 }
157 Some(GraphAnalytics::compute_analytics(&subtree_nodes))
158 }
159
160 fn _collect_subtree_nodes(&self, root_node_id: NodeId) -> Vec<&DocumentNode> {
162 let mut subtree_nodes = Vec::new();
163
164 if let Some(root_node) = self.nodes.get(&root_node_id) {
165 self._collect_subtree_recursive(root_node, &mut subtree_nodes);
166 }
167
168 subtree_nodes
169 }
170
171 fn _collect_subtree_recursive<'a>(
173 &'a self,
174 node: &'a DocumentNode,
175 collected: &mut Vec<&'a DocumentNode>,
176 ) {
177 collected.push(node);
178
179 for child_id in &node.children {
180 if let Some(child_node) = self.nodes.get(child_id) {
181 self._collect_subtree_recursive(child_node, collected);
182 }
183 }
184 }
185}