syncable_cli/
cli.rs

1use clap::{Parser, Subcommand, ValueEnum};
2use std::path::PathBuf;
3
4#[derive(Parser)]
5#[command(name = "sync-ctl")]
6#[command(version = env!("CARGO_PKG_VERSION"))]
7#[command(about = "Generate Infrastructure as Code from your codebase")]
8#[command(
9    long_about = "A powerful CLI tool that analyzes your codebase and automatically generates optimized Infrastructure as Code configurations including Dockerfiles, Docker Compose files, and Terraform configurations"
10)]
11pub struct Cli {
12    #[command(subcommand)]
13    pub command: Commands,
14
15    /// Path to configuration file
16    #[arg(short, long, global = true, value_name = "FILE")]
17    pub config: Option<PathBuf>,
18
19    /// Enable verbose logging (-v for info, -vv for debug, -vvv for trace)
20    #[arg(short, long, global = true, action = clap::ArgAction::Count)]
21    pub verbose: u8,
22
23    /// Suppress all output except errors
24    #[arg(short, long, global = true)]
25    pub quiet: bool,
26
27    /// Output in JSON format where applicable
28    #[arg(long, global = true)]
29    pub json: bool,
30
31    /// Clear the update check cache and force a new check
32    #[arg(long, global = true)]
33    pub clear_update_cache: bool,
34}
35
36#[derive(Subcommand)]
37pub enum Commands {
38    /// Analyze a project and display detected components
39    Analyze {
40        /// Path to the project directory to analyze
41        #[arg(value_name = "PROJECT_PATH")]
42        path: PathBuf,
43
44        /// Output analysis results in JSON format
45        #[arg(short, long)]
46        json: bool,
47
48        /// Show detailed analysis information (legacy format)
49        #[arg(short, long, conflicts_with = "display")]
50        detailed: bool,
51
52        /// Display format for analysis results
53        #[arg(long, value_enum, default_value = "matrix")]
54        display: Option<DisplayFormat>,
55
56        /// Only analyze specific aspects (languages, frameworks, dependencies)
57        #[arg(long, value_delimiter = ',')]
58        only: Option<Vec<String>>,
59    },
60
61    /// Generate IaC files for a project
62    Generate {
63        /// Path to the project directory to analyze
64        #[arg(value_name = "PROJECT_PATH")]
65        path: PathBuf,
66
67        /// Output directory for generated files
68        #[arg(short, long, value_name = "OUTPUT_DIR")]
69        output: Option<PathBuf>,
70
71        /// Generate Dockerfile
72        #[arg(long)]
73        dockerfile: bool,
74
75        /// Generate Docker Compose file
76        #[arg(long)]
77        compose: bool,
78
79        /// Generate Terraform configuration
80        #[arg(long)]
81        terraform: bool,
82
83        /// Generate all supported IaC files
84        #[arg(long, conflicts_with_all = ["dockerfile", "compose", "terraform"])]
85        all: bool,
86
87        /// Perform a dry run without creating files
88        #[arg(long)]
89        dry_run: bool,
90
91        /// Overwrite existing files
92        #[arg(long)]
93        force: bool,
94    },
95
96    /// Validate existing IaC files against best practices
97    Validate {
98        /// Path to the directory containing IaC files
99        #[arg(value_name = "PATH")]
100        path: PathBuf,
101
102        /// Types of files to validate
103        #[arg(long, value_delimiter = ',')]
104        types: Option<Vec<String>>,
105
106        /// Fix issues automatically where possible
107        #[arg(long)]
108        fix: bool,
109    },
110
111    /// Show supported languages and frameworks
112    Support {
113        /// Show only languages
114        #[arg(long)]
115        languages: bool,
116
117        /// Show only frameworks
118        #[arg(long)]
119        frameworks: bool,
120
121        /// Show detailed information
122        #[arg(short, long)]
123        detailed: bool,
124    },
125
126    /// Analyze project dependencies in detail
127    Dependencies {
128        /// Path to the project directory to analyze
129        #[arg(value_name = "PROJECT_PATH")]
130        path: PathBuf,
131
132        /// Show license information for dependencies
133        #[arg(long)]
134        licenses: bool,
135
136        /// Check for known vulnerabilities
137        #[arg(long)]
138        vulnerabilities: bool,
139
140        /// Show only production dependencies
141        #[arg(long, conflicts_with = "dev_only")]
142        prod_only: bool,
143
144        /// Show only development dependencies
145        #[arg(long, conflicts_with = "prod_only")]
146        dev_only: bool,
147
148        /// Output format
149        #[arg(long, value_enum, default_value = "table")]
150        format: OutputFormat,
151    },
152
153    /// Check dependencies for known vulnerabilities
154    Vulnerabilities {
155        /// Check vulnerabilities in a specific path
156        #[arg(default_value = ".")]
157        path: PathBuf,
158
159        /// Show only vulnerabilities with severity >= threshold
160        #[arg(long, value_enum)]
161        severity: Option<SeverityThreshold>,
162
163        /// Output format
164        #[arg(long, value_enum, default_value = "table")]
165        format: OutputFormat,
166
167        /// Export report to file
168        #[arg(long)]
169        output: Option<PathBuf>,
170    },
171
172    /// Perform comprehensive security analysis
173    Security {
174        /// Path to the project directory to analyze
175        #[arg(value_name = "PROJECT_PATH", default_value = ".")]
176        path: PathBuf,
177
178        /// Security scan mode (lightning, fast, balanced, thorough, paranoid)
179        #[arg(long, value_enum, default_value = "thorough")]
180        mode: SecurityScanMode,
181
182        /// Include low severity findings
183        #[arg(long)]
184        include_low: bool,
185
186        /// Skip secrets detection
187        #[arg(long)]
188        no_secrets: bool,
189
190        /// Skip code pattern analysis
191        #[arg(long)]
192        no_code_patterns: bool,
193
194        /// Skip infrastructure analysis (not implemented yet)
195        #[arg(long, hide = true)]
196        no_infrastructure: bool,
197
198        /// Skip compliance checks (not implemented yet)
199        #[arg(long, hide = true)]
200        no_compliance: bool,
201
202        /// Compliance frameworks to check (not implemented yet)
203        #[arg(long, value_delimiter = ',', hide = true)]
204        frameworks: Vec<String>,
205
206        /// Output format
207        #[arg(long, value_enum, default_value = "table")]
208        format: OutputFormat,
209
210        /// Export report to file
211        #[arg(long)]
212        output: Option<PathBuf>,
213
214        /// Exit with error code on security findings
215        #[arg(long)]
216        fail_on_findings: bool,
217    },
218
219    /// Manage vulnerability scanning tools
220    Tools {
221        #[command(subcommand)]
222        command: ToolsCommand,
223    },
224}
225
226#[derive(Subcommand)]
227pub enum ToolsCommand {
228    /// Check which vulnerability scanning tools are installed
229    Status {
230        /// Output format
231        #[arg(long, value_enum, default_value = "table")]
232        format: OutputFormat,
233
234        /// Check tools for specific languages only
235        #[arg(long, value_delimiter = ',')]
236        languages: Option<Vec<String>>,
237    },
238
239    /// Install missing vulnerability scanning tools
240    Install {
241        /// Install tools for specific languages only
242        #[arg(long, value_delimiter = ',')]
243        languages: Option<Vec<String>>,
244
245        /// Also install OWASP Dependency Check (large download)
246        #[arg(long)]
247        include_owasp: bool,
248
249        /// Perform a dry run to show what would be installed
250        #[arg(long)]
251        dry_run: bool,
252
253        /// Skip confirmation prompts
254        #[arg(short, long)]
255        yes: bool,
256    },
257
258    /// Verify that installed tools are working correctly
259    Verify {
260        /// Test tools for specific languages only
261        #[arg(long, value_delimiter = ',')]
262        languages: Option<Vec<String>>,
263
264        /// Show detailed verification output
265        #[arg(short, long)]
266        verbose: bool,
267    },
268
269    /// Show tool installation guides for manual setup
270    Guide {
271        /// Show guide for specific languages only
272        #[arg(long, value_delimiter = ',')]
273        languages: Option<Vec<String>>,
274
275        /// Show platform-specific instructions
276        #[arg(long)]
277        platform: Option<String>,
278    },
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
282pub enum OutputFormat {
283    Table,
284    Json,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
288pub enum DisplayFormat {
289    /// Compact matrix/dashboard view (modern, easy to scan)
290    Matrix,
291    /// Detailed vertical view (legacy format with all details)
292    Detailed,
293    /// Brief summary only
294    Summary,
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
298pub enum SeverityThreshold {
299    Low,
300    Medium,
301    High,
302    Critical,
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
306pub enum SecurityScanMode {
307    /// Lightning fast scan - critical files only (.env, configs)
308    Lightning,
309    /// Fast scan - smart sampling with priority patterns
310    Fast,
311    /// Balanced scan - good coverage with performance optimizations (recommended)
312    Balanced,
313    /// Thorough scan - comprehensive analysis of all files
314    Thorough,
315    /// Paranoid scan - most comprehensive including low-severity findings
316    Paranoid,
317}
318
319impl Cli {
320    /// Initialize logging based on verbosity level
321    pub fn init_logging(&self) {
322        if self.quiet {
323            return;
324        }
325
326        let level = match self.verbose {
327            0 => log::LevelFilter::Warn,
328            1 => log::LevelFilter::Info,
329            2 => log::LevelFilter::Debug,
330            _ => log::LevelFilter::Trace,
331        };
332
333        env_logger::Builder::from_default_env()
334            .filter_level(level)
335            .init();
336    }
337}