Skip to main content

ferrous_forge/commands/
mod.rs

1//! Command implementations for Ferrous Forge
2
3use clap::Subcommand;
4
5/// Available commands for Ferrous Forge
6#[derive(Subcommand)]
7pub enum Commands {
8    /// Initialize Ferrous Forge system-wide, or set up a project with --project
9    Init {
10        /// Force initialization even if already configured
11        #[arg(short, long)]
12        force: bool,
13        /// Set up project-level tooling (rustfmt.toml, clippy.toml, Cargo.toml lints,
14        /// .vscode/settings.json, CI workflow, and git hooks) in the current directory.
15        /// Requires an existing Rust project with Cargo.toml.
16        #[arg(short, long)]
17        project: bool,
18    },
19    /// Show status of Ferrous Forge installation and configuration
20    Status,
21    /// Update Ferrous Forge to the latest version
22    Update {
23        /// Update channel to use (stable, beta, nightly)
24        #[arg(long, default_value = "stable")]
25        channel: String,
26        /// Only update rules, not the binary
27        #[arg(short, long)]
28        rules_only: bool,
29        /// Show what would be updated without actually updating
30        #[arg(short, long)]
31        dry_run: bool,
32    },
33    /// Manage configuration settings
34    Config {
35        /// Config subcommand
36        #[command(subcommand)]
37        command: Option<ConfigCommand>,
38    },
39    /// Validate a Rust project against standards
40    Validate {
41        /// Path to the project to validate (defaults to current directory)
42        path: Option<std::path::PathBuf>,
43        /// Generate AI-friendly compliance report
44        #[arg(long)]
45        ai_report: bool,
46        /// Compare with previous report
47        #[arg(long)]
48        compare_previous: bool,
49        /// Only check locked settings (edition, rust-version) — exits 1 if any locked violation
50        #[arg(long)]
51        locked_only: bool,
52    },
53    /// Rollback to a previous version
54    Rollback {
55        /// Version to rollback to
56        version: String,
57    },
58    /// Uninstall Ferrous Forge from the system
59    Uninstall {
60        /// Confirm uninstallation without prompting
61        #[arg(short, long)]
62        confirm: bool,
63    },
64    /// Rust version management
65    Rust {
66        /// Rust version management subcommand
67        #[command(subcommand)]
68        command: RustCommand,
69    },
70    /// Edition management
71    Edition {
72        /// Edition management subcommand
73        #[command(subcommand)]
74        command: EditionCommand,
75    },
76    /// Safety pipeline management
77    Safety {
78        /// Safety pipeline subcommand
79        #[command(subcommand)]
80        command: SafetyCommand,
81    },
82    /// Project template management
83    Template {
84        /// Template subcommand
85        #[command(subcommand)]
86        command: template::TemplateCommand,
87    },
88    /// Automatically fix code violations
89    Fix {
90        /// Path to the project to fix (defaults to current directory)
91        path: Option<std::path::PathBuf>,
92        /// Only fix specific violation types (comma-separated)
93        #[arg(long)]
94        only: Option<String>,
95        /// Skip specific violation types (comma-separated)
96        #[arg(long)]
97        skip: Option<String>,
98        /// Show what would be fixed without making changes
99        #[arg(long)]
100        dry_run: bool,
101        /// Fix at most this many violations (for testing)
102        #[arg(long)]
103        limit: Option<usize>,
104        /// Enable AI-powered analysis for complex violations
105        #[arg(long)]
106        ai_analysis: bool,
107    },
108}
109
110/// Rust version management subcommands
111#[derive(Subcommand)]
112pub enum RustCommand {
113    /// Check current Rust version and available updates
114    Check {
115        /// Show verbose output
116        #[arg(short, long)]
117        verbose: bool,
118        /// Exit with error if version doesn't meet locked requirements
119        #[arg(long)]
120        enforce: bool,
121    },
122    /// Get update recommendations
123    Recommend {
124        /// Only consider stable releases
125        #[arg(short, long)]
126        stable_only: bool,
127    },
128    /// List recent Rust releases or installed toolchains
129    List {
130        /// Number of releases to show
131        #[arg(short, long, default_value = "10")]
132        count: usize,
133        /// List installed toolchains instead of releases
134        #[arg(long)]
135        toolchains: bool,
136    },
137    /// List recent Rust releases (alias for 'list')
138    Releases {
139        /// Number of releases to show
140        #[arg(short, long, default_value = "10")]
141        count: usize,
142    },
143    /// Check if Rust updates are available
144    CheckUpdates {
145        /// Show detailed information about available updates
146        #[arg(short, long)]
147        verbose: bool,
148    },
149    /// Show release notes for a specific version
150    ReleaseNotes {
151        /// Version to show notes for (e.g., "1.70.0" or "v1.70.0")
152        version: String,
153        /// Show full parsed details including security/breaking changes
154        #[arg(short, long)]
155        detailed: bool,
156    },
157    /// Check for security advisories affecting current Rust version
158    Security {
159        /// Exit with error if security issues found
160        #[arg(long)]
161        fail_on_issues: bool,
162    },
163    /// Update Rust via rustup
164    Update {
165        /// Show what would be updated without making changes
166        #[arg(long)]
167        dry_run: bool,
168        /// Skip confirmation prompt
169        #[arg(short, long)]
170        yes: bool,
171        /// Also update rustup itself
172        #[arg(long)]
173        self_update: bool,
174    },
175    /// Install a specific toolchain
176    InstallToolchain {
177        /// Toolchain channel to install (stable, beta, nightly, or version like 1.70.0)
178        channel: String,
179        /// Set as default toolchain after installation
180        #[arg(short, long)]
181        default: bool,
182    },
183    /// Uninstall a toolchain
184    UninstallToolchain {
185        /// Toolchain channel to uninstall
186        channel: String,
187    },
188    /// Switch to a different toolchain
189    Switch {
190        /// Toolchain channel to switch to
191        channel: String,
192    },
193}
194
195/// Edition management subcommands
196#[derive(Subcommand)]
197pub enum EditionCommand {
198    /// Check edition compliance
199    Check {
200        /// Project path
201        #[arg(default_value = ".")]
202        path: std::path::PathBuf,
203    },
204    /// Migrate to a new edition
205    Migrate {
206        /// Target edition (2018, 2021, 2024)
207        #[arg(default_value = "2024")]
208        edition: String,
209        /// Skip backup creation
210        #[arg(long)]
211        no_backup: bool,
212        /// Run tests after migration
213        #[arg(long)]
214        test: bool,
215        /// Apply edition idioms
216        #[arg(long)]
217        idioms: bool,
218    },
219    /// Analyze edition compatibility
220    Analyze {
221        /// Project path
222        #[arg(default_value = ".")]
223        path: std::path::PathBuf,
224        /// Target edition
225        #[arg(default_value = "2024")]
226        edition: String,
227    },
228}
229
230/// Safety pipeline management subcommands
231#[derive(Subcommand)]
232pub enum SafetyCommand {
233    /// Check safety pipeline status
234    Status,
235    /// Install git hooks for safety pipeline
236    Install {
237        /// Force reinstall even if hooks already exist
238        #[arg(short, long)]
239        force: bool,
240        /// Project path
241        #[arg(default_value = ".")]
242        path: std::path::PathBuf,
243        /// Install cargo publish interception
244        #[arg(long)]
245        cargo: bool,
246    },
247    /// Run safety checks manually
248    Check {
249        /// Pipeline stage to check
250        #[arg(long, default_value = "pre-commit")]
251        stage: String,
252        /// Project path
253        #[arg(default_value = ".")]
254        path: std::path::PathBuf,
255        /// Show verbose output
256        #[arg(short, long)]
257        verbose: bool,
258    },
259    /// Test individual safety checks
260    Test {
261        /// Project path
262        #[arg(default_value = ".")]
263        path: std::path::PathBuf,
264    },
265    /// Create emergency bypass for safety checks
266    ///
267    /// Use this to temporarily skip safety checks with audit logging.
268    /// Requires explicit justification. Bypasses expire after 24 hours.
269    Bypass {
270        /// Pipeline stage to bypass (pre-commit, pre-push, publish)
271        #[arg(long, value_enum)]
272        stage: SafetyBypassStage,
273        /// Reason for bypass (required)
274        #[arg(long)]
275        reason: String,
276        /// Bypass duration in hours (default: 24)
277        #[arg(long, default_value = "24")]
278        duration: u64,
279        /// User creating the bypass (defaults to current user)
280        #[arg(long)]
281        user: Option<String>,
282    },
283    /// View bypass audit log
284    Audit {
285        /// Number of entries to show
286        #[arg(short, long, default_value = "20")]
287        limit: usize,
288    },
289    /// Check if a bypass is active (used by git hooks)
290    CheckBypass {
291        /// Pipeline stage to check
292        #[arg(long, value_enum)]
293        stage: SafetyBypassStage,
294    },
295    /// Uninstall git hooks
296    Uninstall {
297        /// Project path
298        #[arg(default_value = ".")]
299        path: std::path::PathBuf,
300        /// Confirm uninstall without prompting
301        #[arg(short, long)]
302        confirm: bool,
303    },
304    /// Manage safety configuration
305    Config {
306        /// Show current configuration
307        #[arg(long, group = "config_action")]
308        show: bool,
309        /// Set a configuration value (key=value)
310        #[arg(long, group = "config_action")]
311        set: Option<String>,
312        /// Get a specific configuration value
313        #[arg(long, group = "config_action")]
314        get: Option<String>,
315    },
316    /// View safety reports
317    Report {
318        /// Number of reports to show
319        #[arg(short, long, default_value = "10")]
320        last: usize,
321        /// Include bypass audit log
322        #[arg(long)]
323        audit: bool,
324        /// Filter by stage (pre-commit, pre-push, publish)
325        #[arg(long)]
326        stage: Option<String>,
327    },
328    /// Display safety statistics and metrics
329    Stats {
330        /// Number of days to include in statistics
331        #[arg(short, long, default_value = "30")]
332        days: u32,
333    },
334}
335
336/// Safety bypass stage options
337#[derive(clap::ValueEnum, Clone, Debug)]
338pub enum SafetyBypassStage {
339    /// Pre-commit checks
340    PreCommit,
341    /// Pre-push checks
342    PrePush,
343    /// Publish checks
344    Publish,
345}
346
347impl SafetyBypassStage {
348    /// Convert to `PipelineStage`
349    pub fn to_pipeline_stage(&self) -> crate::safety::PipelineStage {
350        match self {
351            Self::PreCommit => crate::safety::PipelineStage::PreCommit,
352            Self::PrePush => crate::safety::PipelineStage::PrePush,
353            Self::Publish => crate::safety::PipelineStage::Publish,
354        }
355    }
356}
357
358/// Configuration subcommands
359#[derive(Subcommand)]
360pub enum ConfigCommand {
361    /// Get a configuration value
362    Get {
363        /// The configuration key to retrieve
364        key: String,
365    },
366    /// Set a configuration value (key=value)
367    Set {
368        /// The key=value pair to set
369        value: String,
370    },
371    /// List all configuration values
372    List,
373    /// Reset configuration to defaults
374    Reset,
375    /// Show configuration sources from hierarchy
376    Sources,
377    /// Migrate old configuration to hierarchical system
378    Migrate,
379    /// Lock a configuration value to prevent changes
380    Lock {
381        /// The configuration key to lock
382        key: String,
383        /// The value to lock (defaults to current value)
384        #[arg(long)]
385        value: Option<String>,
386        /// Reason for locking (required)
387        #[arg(long)]
388        reason: String,
389        /// Configuration level to lock at (project, user, system)
390        #[arg(long, default_value = "project")]
391        level: ConfigLevelArg,
392    },
393    /// Unlock a configuration value to allow changes
394    Unlock {
395        /// The configuration key to unlock
396        key: String,
397        /// Reason for unlocking (required)
398        #[arg(long)]
399        reason: String,
400        /// Configuration level to unlock at
401        #[arg(long, default_value = "project")]
402        level: ConfigLevelArg,
403    },
404    /// Show lock status for all configuration values
405    LockStatus,
406    /// View lock audit log
407    LockAudit {
408        /// Number of entries to show
409        #[arg(short, long, default_value = "20")]
410        limit: usize,
411    },
412    /// Export configuration for sharing
413    Export {
414        /// Configuration level to export (project, user, system)
415        #[arg(long, default_value = "user")]
416        level: ConfigLevelArg,
417        /// Output file path (defaults to stdout)
418        #[arg(short, long)]
419        output: Option<std::path::PathBuf>,
420        /// Description of this shared config
421        #[arg(long)]
422        description: Option<String>,
423    },
424    /// Import configuration from shared file
425    Import {
426        /// Path to the shared config file
427        file: std::path::PathBuf,
428        /// Target level to import to (project, user, system)
429        #[arg(long, default_value = "project")]
430        level: ConfigLevelArg,
431        /// Skip importing locks
432        #[arg(long)]
433        no_locks: bool,
434        /// Force overwrite of existing values
435        #[arg(long)]
436        force: bool,
437    },
438}
439
440/// Configuration level argument for CLI
441#[derive(clap::ValueEnum, Clone, Debug)]
442pub enum ConfigLevelArg {
443    /// System-wide configuration
444    System,
445    /// User-specific configuration
446    User,
447    /// Project-specific configuration
448    Project,
449}
450
451impl ConfigLevelArg {
452    /// Convert to `ConfigLevel`
453    pub fn to_config_level(&self) -> crate::config::ConfigLevel {
454        match self {
455            Self::System => crate::config::ConfigLevel::System,
456            Self::User => crate::config::ConfigLevel::User,
457            Self::Project => crate::config::ConfigLevel::Project,
458        }
459    }
460}
461
462/// Configuration management command handlers.
463pub mod config;
464/// Edition management command handlers.
465pub mod edition;
466/// Automatic code violation fix command handlers.
467pub mod fix;
468/// Project and system initialization command handlers.
469pub mod init;
470/// Version rollback command handlers.
471pub mod rollback;
472/// Rust version management command handlers.
473pub mod rust;
474/// Safety pipeline command handlers.
475pub mod safety;
476/// Installation status display command handlers.
477pub mod status;
478/// Project template command handlers.
479pub mod template;
480/// Uninstall command handlers.
481pub mod uninstall;
482/// Self-update command handlers.
483pub mod update;
484/// Project validation command handlers.
485pub mod validate;