1use std::path::Path;
7use std::process::Command;
8
9use anyhow::{Context, Result};
10
11use crate::CodegenError;
12
13#[derive(Debug, Clone)]
15pub struct QualityConfig {
16 pub run_format: bool,
18 pub run_compile_check: bool,
20 pub run_clippy: bool,
22}
23
24impl Default for QualityConfig {
25 fn default() -> Self {
26 Self {
27 run_format: true,
28 run_compile_check: true,
29 run_clippy: false, }
31 }
32}
33
34pub fn run_quality_checks(crate_path: &Path, config: &QualityConfig) -> Result<()> {
36 println!("🔍 Running quality checks on generated crate...");
37
38 if config.run_format {
39 run_format_check(crate_path)?;
40 }
41
42 if config.run_compile_check {
43 run_compile_check(crate_path)?;
44 }
45
46 if config.run_clippy {
47 run_clippy_check(crate_path)?;
48 }
49
50 println!("✅ All quality checks passed");
51 Ok(())
52}
53
54pub fn run_format_check(crate_path: &Path) -> Result<()> {
56 println!("🎨 Formatting generated crate...");
57
58 let output = Command::new("cargo")
60 .arg("fmt")
61 .arg("--all")
62 .current_dir(crate_path)
63 .output()
64 .with_context(|| format!("Failed to execute cargo fmt in {}", crate_path.display()))?;
65
66 if !output.status.success() {
67 let stderr = String::from_utf8_lossy(&output.stderr);
68
69 if stderr.contains("Failed to find targets") || stderr.contains("no targets") {
71 println!("⚠️ cargo fmt found no targets, trying direct rustfmt...");
72
73 let lib_rs = crate_path.join("src").join("lib.rs");
74 if lib_rs.exists() {
75 let rustfmt_output = Command::new("rustfmt")
76 .arg("--edition")
77 .arg("2021")
78 .arg(&lib_rs)
79 .output()
80 .with_context(|| "Failed to execute rustfmt directly")?;
81
82 if !rustfmt_output.status.success() {
83 let rustfmt_stderr = String::from_utf8_lossy(&rustfmt_output.stderr);
84 return Err(anyhow::anyhow!("rustfmt failed: {rustfmt_stderr}"));
85 }
86
87 println!("✅ Formatting completed successfully (using rustfmt directly)");
88 return Ok(());
89 }
90 }
91
92 return Err(anyhow::anyhow!("cargo fmt failed: {stderr}"));
93 }
94
95 println!("✅ Formatting completed successfully");
96 Ok(())
97}
98
99pub fn run_compile_check(crate_path: &Path) -> Result<()> {
101 println!("🔧 Running cargo check on generated crate...");
102
103 let output = Command::new("cargo")
104 .arg("check")
105 .current_dir(crate_path)
106 .output()
107 .with_context(|| format!("Failed to execute cargo check in {}", crate_path.display()))?;
108
109 if !output.status.success() {
110 let stderr = String::from_utf8_lossy(&output.stderr);
111 let stdout = String::from_utf8_lossy(&output.stdout);
112
113 eprintln!("❌ Cargo check failed for generated crate:");
114 if !stdout.is_empty() {
115 eprintln!("stdout: {stdout}");
116 }
117 if !stderr.is_empty() {
118 eprintln!("stderr: {stderr}");
119 }
120
121 return Err(anyhow::anyhow!(
122 "Generated crate failed cargo check. See output above for details."
123 ));
124 }
125
126 println!("✅ Cargo check passed for generated crate");
127 Ok(())
128}
129
130pub fn run_clippy_check(crate_path: &Path) -> Result<()> {
132 println!("📎 Running cargo clippy on generated crate...");
133
134 let output = Command::new("cargo")
135 .arg("clippy")
136 .arg("--all-targets")
137 .arg("--all-features")
138 .arg("--")
139 .arg("-D")
140 .arg("warnings")
141 .current_dir(crate_path)
142 .output()
143 .with_context(|| format!("Failed to execute cargo clippy in {}", crate_path.display()))?;
144
145 if !output.status.success() {
146 let stderr = String::from_utf8_lossy(&output.stderr);
147 let stdout = String::from_utf8_lossy(&output.stdout);
148
149 eprintln!("❌ Cargo clippy failed for generated crate:");
150 if !stdout.is_empty() {
151 eprintln!("stdout: {stdout}");
152 }
153 if !stderr.is_empty() {
154 eprintln!("stderr: {stderr}");
155 }
156
157 return Err(anyhow::anyhow!(
158 "Generated crate failed cargo clippy. See output above for details."
159 ));
160 }
161
162 println!("✅ Cargo clippy passed for generated crate");
163 Ok(())
164}
165
166pub fn format_generated_crate<P: AsRef<Path>>(crate_path: P) -> Result<(), CodegenError> {
168 run_format_check(crate_path.as_ref()).map_err(|e| CodegenError::Generation {
169 message: e.to_string(),
170 })
171}