st/formatters/
relations_formatter.rs1use crate::formatters::Formatter;
5use crate::relations::RelationAnalyzer;
6use crate::scanner::{FileNode, TreeStats};
7use anyhow::Result;
8use std::io::Write;
9use std::path::Path;
10
11pub struct RelationsFormatter {
13 filter: Option<String>,
14 focus: Option<std::path::PathBuf>,
15}
16
17impl RelationsFormatter {
18 pub fn new(filter: Option<String>, focus: Option<std::path::PathBuf>) -> Self {
19 Self { filter, focus }
20 }
21}
22
23impl Formatter for RelationsFormatter {
24 fn format(
25 &self,
26 writer: &mut dyn Write,
27 _nodes: &[FileNode],
28 _stats: &TreeStats,
29 root_path: &Path,
30 ) -> Result<()> {
31 let mut analyzer = RelationAnalyzer::new();
33
34 eprintln!("๐ Analyzing code relationships...");
36 analyzer.analyze_directory(root_path)?;
37
38 if let Some(filter_type) = &self.filter {
40 eprintln!("๐ Filtering by: {}", filter_type);
42 }
43
44 let relations: Vec<&crate::relations::FileRelation> = if let Some(focus_file) = &self.focus
46 {
47 let abs_focus = if focus_file.is_relative() {
49 root_path.join(focus_file)
50 } else {
51 focus_file.clone()
52 };
53
54 let file_relations = analyzer.get_file_relations(&abs_focus);
55 eprintln!(
56 "๐ Found {} relationships for {}",
57 file_relations.len(),
58 focus_file.display()
59 );
60 file_relations
61 } else {
62 analyzer.get_relations().iter().collect()
63 };
64
65 writeln!(writer, "๐ Code Relationship Analysis")?;
67 writeln!(writer, "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ")?;
68 writeln!(writer)?;
69
70 if relations.is_empty() {
72 if let Some(focus_file) = &self.focus {
73 writeln!(
74 writer,
75 "No relationships found for: {}",
76 focus_file.display()
77 )?;
78 } else {
79 writeln!(writer, "No relationships found in the codebase.")?;
80 }
81 return Ok(());
82 }
83
84 use crate::relations::RelationType;
86 let mut imports = Vec::new();
87 let mut calls = Vec::new();
88 let mut types = Vec::new();
89 let mut tests = Vec::new();
90 let mut coupled = Vec::new();
91
92 for relation in &relations {
93 match &relation.relation_type {
94 RelationType::Imports => imports.push(relation),
95 RelationType::FunctionCall => calls.push(relation),
96 RelationType::TypeUsage => types.push(relation),
97 RelationType::TestedBy => tests.push(relation),
98 RelationType::Coupled => coupled.push(relation),
99 RelationType::Exports => {} }
101 }
102
103 if !imports.is_empty() {
105 writeln!(writer, "๐ฆ Imports ({}):", imports.len())?;
106 for rel in imports {
107 writeln!(
108 writer,
109 " {} โ {}",
110 rel.source.display(),
111 rel.target.display()
112 )?;
113 }
114 writeln!(writer)?;
115 }
116
117 if !calls.is_empty() {
118 writeln!(writer, "๐ Function Calls ({}):", calls.len())?;
119 for rel in calls {
120 writeln!(
121 writer,
122 " {} โ {}",
123 rel.source.display(),
124 rel.target.display()
125 )?;
126 }
127 writeln!(writer)?;
128 }
129
130 if !types.is_empty() {
131 writeln!(writer, "๐ท๏ธ Type Usage ({}):", types.len())?;
132 for rel in types {
133 writeln!(
134 writer,
135 " {} โ {}",
136 rel.source.display(),
137 rel.target.display()
138 )?;
139 }
140 writeln!(writer)?;
141 }
142
143 if !tests.is_empty() {
144 writeln!(writer, "๐งช Tests ({}):", tests.len())?;
145 for rel in tests {
146 writeln!(
147 writer,
148 " {} โ {}",
149 rel.source.display(),
150 rel.target.display()
151 )?;
152 }
153 writeln!(writer)?;
154 }
155
156 if !coupled.is_empty() {
157 writeln!(writer, "๐ Coupled Changes ({}):", coupled.len())?;
158 for rel in coupled {
159 writeln!(
160 writer,
161 " {} โ {}",
162 rel.source.display(),
163 rel.target.display()
164 )?;
165 }
166 writeln!(writer)?;
167 }
168
169 writeln!(writer, "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ")?;
171 writeln!(writer, "Total relationships: {}", relations.len())?;
172 if let Some(focus_file) = &self.focus {
173 writeln!(writer, "Focused on: {}", focus_file.display())?;
174 } else {
175 writeln!(writer, "Files analyzed: {}", root_path.display())?;
176 }
177
178 Ok(())
179 }
180}