1pub mod config;
2pub mod dependency_manager;
3pub mod models;
4pub mod utils;
5
6use std::path::PathBuf;
7
8use anyhow::Result;
9pub use config::Config;
10
11pub struct CargoAutodd {
12 #[allow(dead_code)]
13 project_root: PathBuf,
14 analyzer: dependency_manager::DependencyAnalyzer,
15 updater: dependency_manager::DependencyUpdater,
16 reporter: dependency_manager::DependencyReporter,
17 config: Config,
18 debug: bool,
19 dry_run: bool,
20}
21
22impl CargoAutodd {
23 pub fn new(project_root: PathBuf) -> Self {
24 let config = Config::load_default(&project_root).unwrap_or_default();
25 Self {
26 project_root: project_root.clone(),
27 analyzer: dependency_manager::DependencyAnalyzer::new(project_root.clone()),
28 updater: dependency_manager::DependencyUpdater::new(project_root.clone()),
29 reporter: dependency_manager::DependencyReporter::new(project_root),
30 config,
31 debug: false,
32 dry_run: false,
33 }
34 }
35
36 pub fn with_debug(project_root: PathBuf, debug: bool) -> Self {
37 let config = Config::load_default(&project_root).unwrap_or_default();
38 Self {
39 project_root: project_root.clone(),
40 analyzer: dependency_manager::DependencyAnalyzer::with_debug(
41 project_root.clone(),
42 debug,
43 ),
44 updater: dependency_manager::DependencyUpdater::new(project_root.clone()),
45 reporter: dependency_manager::DependencyReporter::new(project_root),
46 config,
47 debug,
48 dry_run: false,
49 }
50 }
51
52 pub fn with_options(project_root: PathBuf, debug: bool, dry_run: bool, config: Config) -> Self {
53 Self {
54 project_root: project_root.clone(),
55 analyzer: dependency_manager::DependencyAnalyzer::with_debug(
56 project_root.clone(),
57 debug,
58 ),
59 updater: dependency_manager::DependencyUpdater::new(project_root.clone()),
60 reporter: dependency_manager::DependencyReporter::new(project_root),
61 config,
62 debug,
63 dry_run,
64 }
65 }
66
67 pub fn analyze_and_update(&self) -> Result<()> {
68 if self.debug {
69 println!("š Starting dependency analysis in debug mode...");
70 }
71 if self.dry_run {
72 println!("š Running in dry-run mode (no changes will be made)...");
73 }
74
75 println!("š Analyzing project dependencies...");
76 let mut crate_refs = self.analyzer.analyze_dependencies()?;
77
78 crate_refs.retain(|name, _| !self.config.should_exclude(name));
80
81 if self.dry_run {
82 self.print_dry_run_summary(&crate_refs)?;
83 return Ok(());
84 }
85
86 if self.debug {
87 println!("\nš Updating Cargo.toml with found dependencies...");
88 }
89 println!("š Updating Cargo.toml...");
90 self.updater.update_cargo_toml(&crate_refs)?;
91
92 println!("ā
Dependencies updated successfully!");
93 Ok(())
94 }
95
96 fn print_dry_run_summary(
97 &self,
98 crate_refs: &std::collections::HashMap<String, models::CrateReference>,
99 ) -> Result<()> {
100 println!("\nš Dry-run summary:");
101 println!("==================");
102
103 let (regular, dev): (Vec<_>, Vec<_>) = crate_refs
104 .iter()
105 .partition(|(_, crate_ref)| !crate_ref.is_dev_dependency);
106
107 if !regular.is_empty() {
108 println!("\n[dependencies] would add:");
109 for (name, crate_ref) in regular {
110 if crate_ref.is_path_dependency {
111 println!(
112 " {} = {{ path = \"{}\" }}",
113 name,
114 crate_ref.path.as_ref().unwrap_or(&"?".to_string())
115 );
116 } else {
117 println!(" {} = \"<latest>\"", name);
118 }
119 }
120 }
121
122 if !dev.is_empty() {
123 println!("\n[dev-dependencies] would add:");
124 for (name, crate_ref) in dev {
125 if crate_ref.is_path_dependency {
126 println!(
127 " {} = {{ path = \"{}\" }}",
128 name,
129 crate_ref.path.as_ref().unwrap_or(&"?".to_string())
130 );
131 } else {
132 println!(" {} = \"<latest>\"", name);
133 }
134 }
135 }
136
137 if !self.config.exclude.is_empty() {
139 println!("\nExcluded by config:");
140 for name in &self.config.exclude {
141 println!(" - {}", name);
142 }
143 }
144
145 println!("\nā
No changes were made (dry-run mode)");
146 Ok(())
147 }
148
149 pub fn update_dependencies(&self) -> Result<()> {
150 println!("š Checking for dependency updates...");
151 let crate_refs = self.analyzer.analyze_dependencies()?;
152 self.updater.update_cargo_toml(&crate_refs)?;
153 println!("\nš Verifying dependencies...");
154 self.updater.verify_dependencies()?;
155 println!("ā
Dependencies updated successfully!");
156 Ok(())
157 }
158
159 pub fn generate_report(&self) -> Result<()> {
160 println!("š Analyzing dependency usage...");
161 let crate_refs = self.analyzer.analyze_dependencies()?;
162 self.reporter.generate_dependency_report(&crate_refs)
163 }
164
165 pub fn check_security(&self) -> Result<()> {
166 println!("š Running security check...");
167 self.reporter.generate_security_report()
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use std::fs::File;
175 use std::io::Write;
176 use tempfile::TempDir;
177
178 fn create_test_environment() -> Result<TempDir> {
179 let temp_dir = TempDir::new()?;
180
181 let cargo_toml = temp_dir.path().join("Cargo.toml");
183 let content = r#"
184[package]
185name = "test-package"
186version = "0.1.0"
187edition = "2021"
188
189[dependencies]
190serde = "1.0"
191"#;
192 let mut file = File::create(&cargo_toml)?;
193 writeln!(file, "{}", content)?;
194
195 std::fs::create_dir(temp_dir.path().join("src"))?;
197 let main_rs = temp_dir.path().join("src/main.rs");
198 let content = r#"
199use serde;
200use tokio;
201"#;
202 let mut file = File::create(main_rs)?;
203 writeln!(file, "{}", content)?;
204
205 Ok(temp_dir)
206 }
207
208 #[test]
209 fn test_analyze_and_update() -> Result<()> {
210 let temp_dir = create_test_environment()?;
211 let autodd = CargoAutodd::new(temp_dir.path().to_path_buf());
212 autodd.analyze_and_update()?;
213 Ok(())
214 }
215
216 #[test]
217 fn test_generate_report() -> Result<()> {
218 let temp_dir = create_test_environment()?;
219 let autodd = CargoAutodd::new(temp_dir.path().to_path_buf());
220 autodd.generate_report()?;
221 Ok(())
222 }
223
224 #[test]
225 fn test_check_security() -> Result<()> {
226 let temp_dir = create_test_environment()?;
227 let autodd = CargoAutodd::new(temp_dir.path().to_path_buf());
228 autodd.check_security()?;
229 Ok(())
230 }
231}