dampen_cli/commands/
build.rs1#![allow(clippy::print_stderr, clippy::print_stdout)]
2
3use std::path::Path;
6
7#[derive(clap::Args)]
9pub struct BuildArgs {
10 #[arg(short, long, default_value = "ui")]
12 input: String,
13
14 #[arg(short, long, default_value = "src/ui_generated.rs")]
16 output: String,
17
18 #[arg(long, default_value = "Model")]
20 model: String,
21
22 #[arg(long, default_value = "Message")]
24 message: String,
25
26 #[arg(short, long)]
28 verbose: bool,
29
30 #[arg(short, long)]
32 package: Option<String>,
33
34 #[arg(long, value_delimiter = ',')]
36 features: Vec<String>,
37
38 #[arg(long)]
40 release: bool,
41}
42
43pub fn execute(args: &BuildArgs) -> Result<(), String> {
70 execute_production_build(args)
71}
72
73fn execute_production_build(args: &BuildArgs) -> Result<(), String> {
74 use std::process::Command;
75
76 if args.verbose {
78 eprintln!("Running pre-flight checks...");
79 }
80
81 let check_input = if let Some(ref pkg) = args.package {
86 crate::commands::check::resolve_package_ui_path(pkg)
87 .map(|p| p.to_string_lossy().to_string())
88 } else {
89 None
90 };
91
92 if let Err(e) = crate::commands::check::run_checks(check_input, false, args.verbose) {
93 return Err(format!("Pre-flight check failed: {}", e));
94 }
95
96 let mode = if args.release {
97 "codegen"
98 } else {
99 "interpreted"
100 };
101
102 if args.verbose {
103 eprintln!("Running {} build...", mode);
104 }
105
106 let build_rs_exists = Path::new("build.rs").exists()
109 || args
110 .package
111 .as_ref()
112 .is_some_and(|pkg| Path::new("examples").join(pkg).join("build.rs").exists());
113
114 if args.release && !build_rs_exists {
115 return Err(
116 "build.rs not found. Codegen mode requires build.rs for code generation.\n\
117 Tip: Use 'dampen build' (without --release) for interpreted mode,\n\
118 or ensure you're in the correct project directory."
119 .to_string(),
120 );
121 }
122
123 if !Path::new("Cargo.toml").exists() {
124 return Err("Cargo.toml not found. Are you in a Rust project directory?".to_string());
125 }
126
127 let mut cmd = Command::new("cargo");
128 cmd.arg("build");
129
130 if let Some(ref package) = args.package {
131 cmd.arg("-p").arg(package);
132 }
133
134 if args.verbose {
135 cmd.arg("--verbose");
136 }
137
138 let all_features = if args.release {
140 cmd.arg("--release");
142 cmd.arg("--no-default-features");
143 let mut features = vec!["codegen".to_string()];
144 features.extend(args.features.clone());
145 features
146 } else {
147 let mut features = vec!["interpreted".to_string()];
149 features.extend(args.features.clone());
150 features
151 };
152
153 cmd.arg("--features").arg(all_features.join(","));
154
155 if args.verbose {
156 let features_str = all_features.join(",");
157 let cargo_cmd = if args.release {
158 format!(
159 "cargo build --release --no-default-features --features {}",
160 features_str
161 )
162 } else {
163 format!("cargo build --features {}", features_str)
164 };
165 eprintln!("Executing: {}", cargo_cmd);
166 }
167
168 let status = cmd
169 .status()
170 .map_err(|e| format!("Failed to execute cargo: {}", e))?;
171
172 if !status.success() {
173 return Err("Build failed".to_string());
174 }
175
176 if args.verbose {
177 let output_dir = if args.release {
178 "target/release/"
179 } else {
180 "target/debug/"
181 };
182 eprintln!("Build successful! Binary is in {}", output_dir);
183 }
184
185 if args.release {
186 eprintln!("Release build (codegen) completed successfully!");
187 } else {
188 eprintln!("Debug build (interpreted) completed successfully!");
189 eprintln!(
190 "Use 'dampen build --release' or 'dampen release' for optimized production builds."
191 );
192 }
193
194 Ok(())
195}
196
197pub fn execute_release_build(
200 package: Option<String>,
201 features: Vec<String>,
202 verbose: bool,
203 target_dir: Option<String>,
204) -> Result<(), String> {
205 use std::process::Command;
206
207 if verbose {
209 eprintln!("Running pre-flight checks...");
210 }
211
212 let check_input = if let Some(ref pkg) = package {
214 crate::commands::check::resolve_package_ui_path(pkg)
215 .map(|p| p.to_string_lossy().to_string())
216 } else {
217 None
218 };
219
220 if let Err(e) = crate::commands::check::run_checks(check_input, false, verbose) {
221 return Err(format!("Pre-flight check failed: {}", e));
222 }
223
224 let mode = "codegen";
225
226 if verbose {
227 eprintln!("Running {} build...", mode);
228 }
229
230 let build_rs_exists = Path::new("build.rs").exists()
233 || package
234 .as_ref()
235 .is_some_and(|pkg| Path::new("examples").join(pkg).join("build.rs").exists());
236
237 if !build_rs_exists {
238 return Err(
239 "build.rs not found. Codegen mode requires build.rs for code generation.\n\
240 Tip: Use 'dampen build' (without --release) for interpreted mode,\n\
241 or ensure you're in the correct project directory."
242 .to_string(),
243 );
244 }
245
246 if !Path::new("Cargo.toml").exists() {
247 return Err("Cargo.toml not found. Are you in a Rust project directory?".to_string());
248 }
249
250 let mut cmd = Command::new("cargo");
251 cmd.arg("build");
252 cmd.arg("--release");
253
254 if let Some(ref pkg) = package {
255 cmd.arg("-p").arg(pkg);
256 }
257
258 if let Some(ref dir) = target_dir {
259 cmd.arg("--target-dir").arg(dir);
260 }
261
262 if verbose {
263 cmd.arg("--verbose");
264 }
265
266 cmd.arg("--no-default-features");
268 let mut all_features = vec!["codegen".to_string()];
269 all_features.extend(features);
270 cmd.arg("--features").arg(all_features.join(","));
271
272 if verbose {
273 let features_str = all_features.join(",");
274 eprintln!(
275 "Executing: cargo build --release --no-default-features --features {}",
276 features_str
277 );
278 }
279
280 let status = cmd
281 .status()
282 .map_err(|e| format!("Failed to execute cargo: {}", e))?;
283
284 if !status.success() {
285 return Err("Build failed".to_string());
286 }
287
288 if verbose {
289 eprintln!("Build successful! Binary is in target/release/");
290 }
291
292 eprintln!("Release build (codegen) completed successfully!");
293
294 Ok(())
295}