turbovault_tools/
graph_tools.rs1use serde::{Deserialize, Serialize};
4use std::sync::Arc;
5use turbovault_core::prelude::*;
6use turbovault_graph::HealthAnalyzer;
7use turbovault_vault::VaultManager;
8
9pub struct GraphTools {
11 pub manager: Arc<VaultManager>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct BrokenLinkInfo {
17 pub source_file: String,
18 pub target: String,
19 pub line: usize,
20 pub suggestions: Vec<String>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct HealthInfo {
26 pub total_notes: usize,
27 pub total_links: usize,
28 pub broken_links_count: usize,
29 pub orphaned_notes_count: usize,
30 pub dead_end_notes_count: usize,
31 pub hub_notes_count: usize,
32 pub health_score: u8,
33 pub is_healthy: bool,
34}
35
36impl GraphTools {
37 pub fn new(manager: Arc<VaultManager>) -> Self {
39 Self { manager }
40 }
41
42 pub async fn get_broken_links(&self) -> Result<Vec<BrokenLinkInfo>> {
44 let graph_lock = self.manager.link_graph();
45 let graph = graph_lock.read().await;
46 let analyzer = HealthAnalyzer::new(&graph);
47
48 let report = analyzer.analyze()?;
49
50 Ok(report
51 .broken_links
52 .into_iter()
53 .map(|bl| BrokenLinkInfo {
54 source_file: bl.source_file.to_string_lossy().to_string(),
55 target: bl.target,
56 line: bl.line,
57 suggestions: bl.suggestions,
58 })
59 .collect())
60 }
61
62 pub async fn quick_health_check(&self) -> Result<HealthInfo> {
64 let graph_lock = self.manager.link_graph();
65 let graph = graph_lock.read().await;
66 let analyzer = HealthAnalyzer::new(&graph);
67
68 let report = analyzer.quick_check()?;
69
70 Ok(HealthInfo {
71 total_notes: report.total_notes,
72 total_links: report.total_links,
73 broken_links_count: report.broken_links.len(),
74 orphaned_notes_count: report.orphaned_notes.len(),
75 dead_end_notes_count: 0,
76 hub_notes_count: 0,
77 health_score: report.health_score,
78 is_healthy: report.is_healthy(),
79 })
80 }
81
82 pub async fn full_health_analysis(&self) -> Result<HealthInfo> {
84 let graph_lock = self.manager.link_graph();
85 let graph = graph_lock.read().await;
86 let analyzer = HealthAnalyzer::new(&graph);
87
88 let report = analyzer.analyze()?;
89
90 Ok(HealthInfo {
91 total_notes: report.total_notes,
92 total_links: report.total_links,
93 broken_links_count: report.broken_links.len(),
94 orphaned_notes_count: report.orphaned_notes.len(),
95 dead_end_notes_count: report.dead_end_notes.len(),
96 hub_notes_count: report.hub_notes.len(),
97 health_score: report.health_score,
98 is_healthy: report.is_healthy(),
99 })
100 }
101
102 pub async fn get_hub_notes(&self, limit: usize) -> Result<Vec<(String, usize)>> {
104 let graph_lock = self.manager.link_graph();
105 let graph = graph_lock.read().await;
106 let analyzer = HealthAnalyzer::new(&graph);
107
108 let report = analyzer.analyze()?;
109
110 Ok(report
111 .hub_notes
112 .into_iter()
113 .take(limit)
114 .map(|(path, count)| (path.to_string_lossy().to_string(), count))
115 .collect())
116 }
117
118 pub async fn get_dead_end_notes(&self) -> Result<Vec<String>> {
120 let graph_lock = self.manager.link_graph();
121 let graph = graph_lock.read().await;
122 let analyzer = HealthAnalyzer::new(&graph);
123
124 let report = analyzer.analyze()?;
125
126 Ok(report
127 .dead_end_notes
128 .into_iter()
129 .map(|p| p.to_string_lossy().to_string())
130 .collect())
131 }
132
133 pub async fn detect_cycles(&self) -> Result<Vec<Vec<String>>> {
135 let graph_lock = self.manager.link_graph();
136 let graph = graph_lock.read().await;
137 let cycles = graph.cycles();
138
139 Ok(cycles
140 .into_iter()
141 .map(|cycle| {
142 cycle
143 .into_iter()
144 .map(|p| p.to_string_lossy().to_string())
145 .collect()
146 })
147 .collect())
148 }
149
150 pub async fn get_connected_components(&self) -> Result<Vec<Vec<String>>> {
152 let graph_lock = self.manager.link_graph();
153 let graph = graph_lock.read().await;
154 let components = graph.connected_components()?;
155
156 Ok(components
157 .into_iter()
158 .map(|component| {
159 component
160 .into_iter()
161 .map(|p| p.to_string_lossy().to_string())
162 .collect()
163 })
164 .collect())
165 }
166
167 pub async fn get_isolated_clusters(&self) -> Result<Vec<Vec<String>>> {
169 let graph_lock = self.manager.link_graph();
170 let graph = graph_lock.read().await;
171 let analyzer = HealthAnalyzer::new(&graph);
172
173 let report = analyzer.analyze()?;
174
175 Ok(report
176 .isolated_clusters
177 .into_iter()
178 .map(|cluster| {
179 cluster
180 .into_iter()
181 .map(|p| p.to_string_lossy().to_string())
182 .collect()
183 })
184 .collect())
185 }
186}