Skip to main content

cargo_mate/tools/
mod.rs

1use std::collections::HashMap;
2use clap::{ArgMatches, Command};
3use colored::*;
4use thiserror::Error;
5use quote::ToTokens;
6pub mod bench_diff;
7pub mod dep_audit;
8pub mod test_gen;
9pub mod workspace_sync;
10pub mod panic_analyzer;
11pub mod cross_test;
12pub mod refactor_engine;
13pub mod rust_mentor;
14pub mod release_automation;
15pub mod bloat_check;
16pub mod cache_analyzer;
17pub mod async_lint;
18pub mod crud_gen;
19pub mod proto_bind;
20pub mod error_derive;
21pub mod builder_gen;
22pub mod example_gen;
23pub mod api_changelog;
24pub mod trait_explorer;
25pub mod env_check;
26pub mod feature_map;
27pub mod vendorize;
28pub mod macro_expand;
29pub mod lifetime_visualizer;
30pub mod compile_time_tracker;
31pub mod mock_derive;
32pub mod coverage_guard;
33#[cfg(test)]
34pub mod snapshot_test;
35pub mod wasm_optimize;
36pub mod installer_gen;
37pub mod migration_gen;
38pub mod serde_validator;
39pub mod sql_macro_check;
40pub mod secret_scanner;
41pub mod unsafe_analyzer;
42pub mod license_bundler;
43pub mod code_analyzer;
44#[derive(Error, Debug)]
45pub enum ToolError {
46    #[error("Tool '{0}' not found")]
47    ToolNotFound(String),
48    #[error("Tool execution failed: {0}")]
49    ExecutionFailed(String),
50    #[error("Configuration error: {0}")]
51    ConfigError(String),
52    #[error("Invalid arguments: {0}")]
53    InvalidArguments(String),
54    #[error("IO error: {0}")]
55    IoError(#[from] std::io::Error),
56    #[error("TOML parsing error: {0}")]
57    TomlError(#[from] toml::de::Error),
58    #[error("JSON parsing error: {0}")]
59    JsonError(#[from] serde_json::Error),
60    #[error("Syn parsing error: {0}")]
61    SynError(String),
62}
63impl From<syn::Error> for ToolError {
64    fn from(err: syn::Error) -> Self {
65        ToolError::SynError(
66            format!(
67                "Rust syntax parsing failed: {}. This usually indicates invalid Rust code or an unsupported language feature.",
68                err
69            ),
70        )
71    }
72}
73pub type Result<T> = std::result::Result<T, ToolError>;
74pub trait Tool {
75    fn name(&self) -> &'static str;
76    /// Returns a description of what the tool does
77    fn description(&self) -> &'static str;
78    fn command(&self) -> Command;
79    fn execute(&self, matches: &ArgMatches) -> Result<()>;
80}
81pub struct ToolRegistry {
82    tools: HashMap<String, Box<dyn Tool>>,
83}
84impl ToolRegistry {
85    pub fn new() -> Self {
86        Self { tools: HashMap::new() }
87    }
88    pub fn register<T: Tool + 'static>(mut self, tool: T) -> Self {
89        self.tools.insert(tool.name().to_string(), Box::new(tool));
90        self
91    }
92    pub fn get(&self, name: &str) -> Option<&Box<dyn Tool>> {
93        self.tools.get(name)
94    }
95    pub fn list_tools(&self) -> Vec<(&str, &str)> {
96        let mut tools: Vec<_> = self
97            .tools
98            .values()
99            .map(|tool| (tool.name(), tool.description()))
100            .collect();
101        tools.sort_by(|a, b| a.0.cmp(b.0));
102        tools
103    }
104    pub fn has_tool(&self, name: &str) -> bool {
105        self.tools.contains_key(name)
106    }
107}
108/// Configuration for tools
109#[derive(Debug, Clone)]
110pub struct ToolConfig {
111    pub verbose: bool,
112    pub dry_run: bool,
113    pub output_format: OutputFormat,
114}
115#[derive(Debug, Clone)]
116pub enum OutputFormat {
117    Human,
118    Json,
119    Table,
120}
121impl Default for ToolConfig {
122    fn default() -> Self {
123        Self {
124            verbose: false,
125            dry_run: false,
126            output_format: OutputFormat::Human,
127        }
128    }
129}
130/// Initialize the tool registry with all available tools
131pub fn create_registry() -> ToolRegistry {
132    let registry = ToolRegistry::new();
133    registry
134        .register(bench_diff::BenchDiffTool::new())
135        .register(dep_audit::DepAuditTool::new())
136        .register(test_gen::TestGenTool::new())
137        .register(workspace_sync::WorkspaceSyncTool::new())
138        .register(panic_analyzer::PanicAnalyzerTool::new())
139        .register(cross_test::CrossTestTool::new())
140        .register(release_automation::ReleaseAutomationTool::new())
141        .register(crud_gen::CrudGenTool::new())
142        .register(proto_bind::ProtoBindTool::new())
143        .register(error_derive::ErrorDeriveTool::new())
144        .register(builder_gen::BuilderGenTool::new())
145        .register(example_gen::ExampleGenTool::new())
146        .register(api_changelog::ApiChangelogTool::new())
147        .register(trait_explorer::TraitExplorerTool::new())
148        .register(rust_mentor::RustMentorTool::new())
149        .register(refactor_engine::RefactorEngineTool::new())
150        .register(env_check::EnvCheckTool::new())
151        .register(bloat_check::BloatCheckTool::new())
152        .register(cache_analyzer::CacheAnalyzerTool::new())
153        .register(async_lint::AsyncLintTool::new())
154        .register(feature_map::FeatureMapTool::new())
155        .register(vendorize::VendorizeTool::new())
156        .register(macro_expand::MacroExpandTool::new())
157        .register(lifetime_visualizer::LifetimeVisualizerTool::new())
158        .register(compile_time_tracker::CompileTimeTrackerTool::new())
159        .register(mock_derive::MockDeriveTool::new())
160        .register(coverage_guard::CoverageGuardTool::new())
161        .register(wasm_optimize::WasmOptimizeTool::new())
162        .register(installer_gen::InstallerGenTool::new())
163        .register(migration_gen::MigrationGenTool::new())
164        .register(serde_validator::SerdeValidatorTool::new())
165        .register(sql_macro_check::SqlMacroCheckTool::new())
166        .register(secret_scanner::SecretScannerTool::new())
167        .register(unsafe_analyzer::UnsafeAnalyzerTool::new())
168        .register(license_bundler::LicenseBundlerTool::new())
169        .register(code_analyzer::CodeAnalyzer::new())
170}
171static mut REGISTRY: Option<ToolRegistry> = None;
172/// Get the global tool registry (lazy initialized)
173pub fn get_registry() -> &'static ToolRegistry {
174    unsafe {
175        if REGISTRY.is_none() {
176            REGISTRY = Some(create_registry());
177        }
178        REGISTRY.as_ref().unwrap()
179    }
180}
181pub fn list_tools() {
182    let registry = get_registry();
183    let tools = registry.list_tools();
184    if tools.is_empty() {
185        println!("{}", "No tools available yet.".yellow());
186        println!("Tools are being developed and will be added soon!");
187        return;
188    }
189    println!("{}", "🔧 Available Tools".bold().blue());
190    println!("{}", "═".repeat(50).blue());
191    for (name, description) in tools {
192        println!("  {} - {}", name.green().bold(), description);
193    }
194    println!();
195    println!("{}", "Usage:".bold());
196    println!("  cm tool list                          # List all tools");
197    println!("  cm tool help <name>                   # Show help for a tool");
198    println!("  cm tool <name> [options]              # Run a tool");
199    println!("  cm tool run <name> [options]          # Run a tool (explicit)");
200}
201pub fn show_tool_help(name: &str) {
202    let registry = get_registry();
203    if let Some(tool) = registry.get(name) {
204        let mut command = tool.command();
205        println!("{}", format!("Help for tool: {}", name) .bold().blue());
206        println!("{}", "═".repeat(50).blue());
207        println!("{}", tool.description());
208        println!();
209        let _ = command.print_help();
210    } else {
211        println!("{}", format!("❌ Tool '{}' not found", name) .red());
212        println!();
213        println!("{}", "Available tools:".bold());
214        list_tools();
215    }
216}
217pub fn run_tool(name: &str, args: &[String]) -> Result<()> {
218    let registry = get_registry();
219    let tool = registry
220        .get(name)
221        .ok_or_else(|| ToolError::ToolNotFound(name.to_string()))?;
222    let command = tool.command();
223    let clap_args = std::iter::once(name.to_string())
224        .chain(args.iter().cloned())
225        .collect::<Vec<_>>();
226    let arg_refs: Vec<&str> = clap_args.iter().map(|s| s.as_str()).collect();
227    let matches = command
228        .try_get_matches_from(&arg_refs)
229        .map_err(|e| ToolError::InvalidArguments(e.to_string()))?;
230    tool.execute(&matches)
231}
232pub fn common_options() -> Vec<clap::Arg> {
233    vec![
234        clap::Arg::new("verbose").long("verbose").short('v')
235        .help("Enable verbose output").action(clap::ArgAction::SetTrue),
236        clap::Arg::new("dry-run").long("dry-run")
237        .help("Show what would be done without executing")
238        .action(clap::ArgAction::SetTrue), clap::Arg::new("output").long("output")
239        .short('o').help("Output format").value_parser(["human", "json", "table"])
240        .default_value("human"),
241    ]
242}
243pub fn parse_output_format(matches: &ArgMatches) -> OutputFormat {
244    match matches.get_one::<String>("output").map(|s| s.as_str()) {
245        Some("json") => OutputFormat::Json,
246        Some("table") => OutputFormat::Table,
247        _ => OutputFormat::Human,
248    }
249}
250#[cfg(test)]
251mod tests {
252    use super::*;
253    #[test]
254    fn test_registry_creation() {
255        let registry = create_registry();
256        assert!(! registry.has_tool("nonexistent"));
257    }
258    #[test]
259    fn test_list_tools_empty() {
260        let registry = create_registry();
261        let tools = registry.list_tools();
262        assert!(tools.is_empty() || ! tools.is_empty());
263    }
264}