codeprism_dev_tools/
lib.rs1use anyhow::Result;
29use std::path::Path;
30
31pub mod ast_visualizer;
32pub mod dev_repl;
33pub mod diff_comparison;
34pub mod graphviz_export;
35pub mod parser_validator;
36pub mod performance_profiler;
37
38pub use ast_visualizer::{AstVisualizer, VisualizationFormat};
40pub use dev_repl::{DevRepl, ReplCommand, ReplResult};
41pub use diff_comparison::{AstDiff, DiffReport, DiffType};
42pub use graphviz_export::{EdgeStyle, GraphVizExporter, GraphVizOptions, NodeStyle};
43pub use parser_validator::{ParserValidator, ValidationError, ValidationReport};
44pub use performance_profiler::{MetricType, PerformanceProfiler, ProfilingReport};
45
46pub struct DevTools {
48 visualizer: AstVisualizer,
49 validator: ParserValidator,
50 profiler: PerformanceProfiler,
51 exporter: GraphVizExporter,
52}
53
54impl DevTools {
55 pub fn new() -> Self {
57 Self {
58 visualizer: AstVisualizer::new(),
59 validator: ParserValidator::new(),
60 profiler: PerformanceProfiler::new(),
61 exporter: GraphVizExporter::new(),
62 }
63 }
64
65 pub fn with_config(config: DevToolsConfig) -> Self {
67 Self {
68 visualizer: AstVisualizer::with_config(config.visualization),
69 validator: ParserValidator::with_config(config.validation),
70 profiler: PerformanceProfiler::with_config(config.profiling),
71 exporter: GraphVizExporter::with_config(config.graphviz),
72 }
73 }
74
75 pub fn visualizer(&self) -> &AstVisualizer {
77 &self.visualizer
78 }
79
80 pub fn validator(&self) -> &ParserValidator {
82 &self.validator
83 }
84
85 pub fn profiler(&self) -> &PerformanceProfiler {
87 &self.profiler
88 }
89
90 pub fn exporter(&self) -> &GraphVizExporter {
92 &self.exporter
93 }
94
95 pub async fn start_repl(&self, language: Option<&str>) -> Result<()> {
97 let mut repl = DevRepl::new(language)?;
98 repl.set_visualizer(self.visualizer.clone());
99 repl.set_validator(self.validator.clone());
100 repl.set_profiler(self.profiler.clone());
101 repl.set_exporter(self.exporter.clone());
102 repl.run().await
103 }
104
105 pub fn analyze_parse_result(
107 &self,
108 parse_result: &codeprism_core::ParseResult,
109 source: &str,
110 ) -> Result<AnalysisReport> {
111 let mut report = AnalysisReport::new();
112
113 let validation = self.validator.validate_complete(parse_result, source)?;
115 report.validation = Some(validation);
116
117 let visualization = self.visualizer.visualize_tree(&parse_result.tree, source)?;
119 report.visualization = Some(visualization);
120
121 if !parse_result.nodes.is_empty() {
123 let graphviz = self
124 .exporter
125 .export_nodes_and_edges(&parse_result.nodes, &parse_result.edges)?;
126 report.graphviz = Some(graphviz);
127 }
128
129 Ok(report)
130 }
131
132 pub fn compare_parse_results(
134 &self,
135 old_result: &codeprism_core::ParseResult,
136 new_result: &codeprism_core::ParseResult,
137 source: &str,
138 ) -> Result<DiffReport> {
139 let diff = AstDiff::new();
140 diff.compare(old_result, new_result, source)
141 }
142}
143
144impl Default for DevTools {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150#[derive(Debug, Clone, Default)]
152pub struct DevToolsConfig {
153 pub visualization: ast_visualizer::VisualizationConfig,
154 pub validation: parser_validator::ValidationConfig,
155 pub profiling: performance_profiler::ProfilingConfig,
156 pub graphviz: graphviz_export::GraphVizConfig,
157}
158
159#[derive(Debug)]
161pub struct AnalysisReport {
162 pub validation: Option<ValidationReport>,
163 pub visualization: Option<String>,
164 pub graphviz: Option<String>,
165 pub profiling: Option<ProfilingReport>,
166 pub diff: Option<DiffReport>,
167}
168
169impl AnalysisReport {
170 pub fn new() -> Self {
171 Self {
172 validation: None,
173 visualization: None,
174 graphviz: None,
175 profiling: None,
176 diff: None,
177 }
178 }
179
180 pub fn has_issues(&self) -> bool {
182 if let Some(validation) = &self.validation {
183 if !validation.is_valid() {
184 return true;
185 }
186 }
187 false
188 }
189
190 pub fn issues_summary(&self) -> Vec<String> {
192 let mut issues = Vec::new();
193
194 if let Some(validation) = &self.validation {
195 if !validation.is_valid() {
196 issues.extend(validation.errors().iter().map(|e| e.to_string()));
197 }
198 }
199
200 issues
201 }
202
203 pub fn format_report(&self) -> String {
205 let mut output = String::new();
206
207 output.push_str("=== CodePrism Parser Analysis Report ===\n\n");
208
209 if let Some(validation) = &self.validation {
210 output.push_str("## Validation Results\n");
211 if validation.is_valid() {
212 output.push_str("✅ All validation checks passed\n");
213 } else {
214 output.push_str("❌ Validation errors found:\n");
215 for error in validation.errors() {
216 output.push_str(&format!(" - {error}\n"));
217 }
218 }
219 output.push('\n');
220 }
221
222 if let Some(visualization) = &self.visualization {
223 output.push_str("## AST Visualization\n");
224 output.push_str(visualization);
225 output.push_str("\n\n");
226 }
227
228 if let Some(profiling) = &self.profiling {
229 output.push_str("## Performance Metrics\n");
230 output.push_str(&profiling.format_summary());
231 output.push('\n');
232 }
233
234 if let Some(diff) = &self.diff {
235 output.push_str("## AST Differences\n");
236 output.push_str(&diff.format_report());
237 output.push('\n');
238 }
239
240 output
241 }
242}
243
244impl Default for AnalysisReport {
245 fn default() -> Self {
246 Self::new()
247 }
248}
249
250#[derive(thiserror::Error, Debug)]
252pub enum DevToolsError {
253 #[error("Visualization error: {0}")]
254 Visualization(String),
255
256 #[error("Validation error: {0}")]
257 Validation(String),
258
259 #[error("REPL error: {0}")]
260 Repl(String),
261
262 #[error("GraphViz export error: {0}")]
263 GraphViz(String),
264
265 #[error("Performance profiling error: {0}")]
266 Profiling(String),
267
268 #[error("IO error: {0}")]
269 Io(#[from] std::io::Error),
270
271 #[error("Parse error: {0}")]
272 Parse(#[from] codeprism_core::Error),
273}
274
275pub mod utils {
277 use super::*;
278
279 pub fn load_source_file<P: AsRef<Path>>(path: P) -> Result<String> {
281 std::fs::read_to_string(path.as_ref())
282 .map_err(|e| anyhow::anyhow!("Failed to load source file: {}", e))
283 }
284
285 pub fn detect_language_from_extension<P: AsRef<Path>>(path: P) -> Option<&'static str> {
287 match path.as_ref().extension()?.to_str()? {
288 "rs" => Some("rust"),
289 "py" => Some("python"),
290 "js" | "mjs" => Some("javascript"),
291 "ts" => Some("typescript"),
292 "java" => Some("java"),
293 _ => None,
294 }
295 }
296
297 pub fn format_file_size(bytes: u64) -> String {
299 const UNITS: &[&str] = &["B", "KB", "MB", "GB"];
300 let mut size = bytes as f64;
301 let mut unit_index = 0;
302
303 while size >= 1024.0 && unit_index < UNITS.len() - 1 {
304 size /= 1024.0;
305 unit_index += 1;
306 }
307
308 if unit_index == 0 {
309 format!("{} {}", bytes, UNITS[unit_index])
310 } else {
311 format!("{:.1} {}", size, UNITS[unit_index])
312 }
313 }
314
315 pub fn format_duration(duration: std::time::Duration) -> String {
317 let total_ms = duration.as_millis();
318
319 if total_ms < 1000 {
320 format!("{total_ms}ms")
321 } else if total_ms < 60_000 {
322 format!("{:.2}s", duration.as_secs_f64())
323 } else {
324 let minutes = total_ms / 60_000;
325 let seconds = (total_ms % 60_000) as f64 / 1000.0;
326 format!("{minutes}m {seconds:.1}s")
327 }
328 }
329}