dampen_cli/commands/
run.rs

1#![allow(clippy::print_stderr, clippy::print_stdout)]
2
3//! Run command - launches development mode with interpreted execution
4//!
5//! This command wraps `cargo run` with the `interpreted` feature flag enabled,
6//! providing fast iteration with hot-reload capabilities.
7
8use std::path::Path;
9use std::process::Command;
10
11/// Run command arguments
12#[derive(clap::Args)]
13pub struct RunArgs {
14    /// Package to run (if workspace has multiple binaries)
15    #[arg(short, long)]
16    package: Option<String>,
17
18    /// Arguments to pass to the application
19    #[arg(last = true)]
20    app_args: Vec<String>,
21
22    /// Verbose output
23    #[arg(short, long)]
24    verbose: bool,
25
26    /// Run with release optimizations
27    #[arg(long)]
28    release: bool,
29
30    /// Additional features to enable (beyond interpreted)
31    #[arg(long, value_delimiter = ',')]
32    features: Vec<String>,
33}
34
35/// Execute the run command
36///
37/// This command launches the application:
38/// - Debug mode (default): Interpreted mode with hot-reload support
39/// - Release mode (--release): Codegen mode with full optimizations
40///
41/// # Mode Behavior
42///
43/// - **Interpreted Mode** (default): Runtime XML parsing with hot-reload support
44/// - **Codegen Mode** (--release): Compile-time code generation, zero runtime overhead
45///
46/// # Examples
47///
48/// ```bash
49/// # Debug mode (interpreted with hot-reload)
50/// dampen run
51///
52/// # Release mode (codegen optimized)
53/// dampen run --release
54///
55/// # Run specific package
56/// dampen run -p my-app
57///
58/// # Pass application arguments
59/// dampen run -- --window-size 800x600
60///
61/// # Enable additional features
62/// dampen run --features tokio,logging
63/// ```
64pub fn execute(args: &RunArgs) -> Result<(), String> {
65    // Check if Cargo.toml exists
66    if !Path::new("Cargo.toml").exists() {
67        return Err("Cargo.toml not found. Are you in a Rust project directory?".to_string());
68    }
69
70    let mode = if args.release {
71        "codegen"
72    } else {
73        "interpreted"
74    };
75
76    if args.verbose {
77        eprintln!("Running in {} mode...", mode);
78        eprintln!(
79            "Profile: {}",
80            if args.release { "release" } else { "debug" }
81        );
82    }
83
84    // For release mode, we need to build first then run
85    if args.release {
86        // Check build.rs exists for codegen mode
87        // If package is specified, also check in examples/ directory
88        let build_rs_exists = Path::new("build.rs").exists()
89            || args
90                .package
91                .as_ref()
92                .is_some_and(|pkg| Path::new("examples").join(pkg).join("build.rs").exists());
93
94        if !build_rs_exists {
95            return Err(
96                "build.rs not found. Codegen mode requires build.rs for code generation.\n\
97                 Tip: Use 'dampen run' (without --release) for interpreted mode,\n\
98                 or ensure you're in the correct project directory."
99                    .to_string(),
100            );
101        }
102
103        // Build in release mode with codegen
104        let mut build_cmd = Command::new("cargo");
105        build_cmd.arg("build");
106
107        if let Some(ref package) = args.package {
108            build_cmd.arg("-p").arg(package);
109        }
110
111        if args.verbose {
112            build_cmd.arg("--verbose");
113        }
114
115        // Release mode: codegen with optimizations
116        build_cmd.arg("--release");
117        build_cmd.arg("--no-default-features");
118
119        let mut features = vec!["codegen".to_string()];
120        features.extend(args.features.clone());
121
122        build_cmd.arg("--features").arg(features.join(","));
123
124        if args.verbose {
125            let features_str = features.join(",");
126            eprintln!(
127                "Building: cargo build --release --no-default-features --features {}",
128                features_str
129            );
130        }
131
132        let build_status = build_cmd
133            .status()
134            .map_err(|e| format!("Failed to execute cargo build: {}", e))?;
135
136        if !build_status.success() {
137            return Err("Build failed".to_string());
138        }
139
140        if args.verbose {
141            eprintln!("Build successful! Now running application...");
142        }
143
144        // Run the built binary
145        let mut run_cmd = Command::new("cargo");
146        run_cmd.arg("run");
147
148        if let Some(ref package) = args.package {
149            run_cmd.arg("-p").arg(package);
150        }
151
152        if args.verbose {
153            run_cmd.arg("--verbose");
154        }
155
156        // In release mode, run release binary
157        run_cmd.arg("--release");
158
159        // Add application arguments if provided
160        if !args.app_args.is_empty() {
161            run_cmd.arg("--");
162            run_cmd.args(&args.app_args);
163        }
164
165        if args.verbose {
166            eprintln!("Running: cargo run --release");
167            if !args.app_args.is_empty() {
168                eprintln!("Application args: {:?}", args.app_args);
169            }
170        }
171
172        let run_status = run_cmd
173            .status()
174            .map_err(|e| format!("Failed to execute cargo run: {}", e))?;
175
176        if !run_status.success() {
177            return Err("Run command failed".to_string());
178        }
179
180        Ok(())
181    } else {
182        // Debug mode: use interpreted mode
183        let mut cmd = Command::new("cargo");
184        cmd.arg("run");
185
186        if let Some(ref package) = args.package {
187            cmd.arg("-p").arg(package);
188        }
189
190        if args.verbose {
191            cmd.arg("--verbose");
192        }
193
194        // Debug mode: use interpreted
195        let mut features = vec!["interpreted".to_string()];
196        features.extend(args.features.clone());
197
198        cmd.arg("--features").arg(features.join(","));
199
200        // Add application arguments if provided
201        if !args.app_args.is_empty() {
202            cmd.arg("--");
203            cmd.args(&args.app_args);
204        }
205
206        // Execute cargo run
207        if args.verbose {
208            let features_str = features.join(",");
209            eprintln!("Executing: cargo run --features {}", features_str);
210            if !args.app_args.is_empty() {
211                eprintln!("Application args: {:?}", args.app_args);
212            }
213        }
214
215        let status = cmd
216            .status()
217            .map_err(|e| format!("Failed to execute cargo: {}", e))?;
218
219        if !status.success() {
220            return Err("Run command failed".to_string());
221        }
222
223        Ok(())
224    }
225}