ferrous_forge/cargo_intercept/
mod.rs1pub mod validation;
19pub mod wrapper;
21
22use crate::Result;
23use crate::validation::ViolationType;
24use std::env;
25use std::path::Path;
26
27pub struct CargoInterceptor {
29 enforce_dogfooding: bool,
31 bypass_style: bool,
33 force_bypass: bool,
35}
36
37impl Default for CargoInterceptor {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43impl CargoInterceptor {
44 pub fn new() -> Self {
46 let bypass_style = env::var("FERROUS_FORGE_BYPASS")
47 .unwrap_or_default()
48 .eq_ignore_ascii_case("true");
49
50 let force_bypass = env::var("FERROUS_FORGE_FORCE_BYPASS")
51 .unwrap_or_default()
52 .eq_ignore_ascii_case("true");
53
54 Self {
55 enforce_dogfooding: true,
56 bypass_style,
57 force_bypass,
58 }
59 }
60
61 pub fn with_dogfooding(enforce_dogfooding: bool) -> Self {
63 let mut interceptor = Self::new();
64 interceptor.enforce_dogfooding = enforce_dogfooding;
65 interceptor
66 }
67}
68
69pub async fn intercept_publish_command(project_path: &Path) -> Result<()> {
77 let interceptor = CargoInterceptor::new();
78
79 if interceptor.force_bypass {
80 eprintln!(
81 "\n⚠️ FERROUS FORGE FORCE BYPASSED — FERROUS_FORGE_FORCE_BYPASS=true\n\
82 All validation skipped. This should NEVER happen in production.\n"
83 );
84 return Ok(());
85 }
86
87 if interceptor.bypass_style {
88 tracing::warn!(
89 "FERROUS_FORGE_BYPASS enabled — style checks skipped, locked settings still enforced"
90 );
91 }
92
93 tracing::info!("Intercepting cargo publish — running validation");
94
95 validation::pre_publish_validation(project_path).await?;
97 validation::version_consistency_check(project_path)?;
98
99 if interceptor.enforce_dogfooding {
100 validation::enforce_dogfooding(project_path).await?;
101 }
102
103 tracing::info!("Pre-publish validation passed");
104 Ok(())
105}
106
107pub async fn intercept_dev_command(project_path: &Path) -> Result<()> {
116 let interceptor = CargoInterceptor::new();
117
118 if interceptor.force_bypass {
119 eprintln!(
120 "\n⚠️ FERROUS FORGE FORCE BYPASSED — FERROUS_FORGE_FORCE_BYPASS=true\n\
121 All validation skipped. This should NEVER happen in production.\n"
122 );
123 return Ok(());
124 }
125
126 let locked_violations = validation::check_locked_settings(project_path).await?;
128 if !locked_violations.is_empty() {
129 eprintln!("\n❌ FERROUS FORGE — Locked Setting Violations Detected\n");
130 for v in &locked_violations {
131 eprintln!("{}\n", v.message);
132 }
133 return Err(crate::Error::validation(
134 "Locked setting violations must be resolved before building. \
135 See messages above. DO NOT change locked values — escalate to human.",
136 ));
137 }
138
139 if !interceptor.bypass_style {
141 let style_violations = validation::check_style_violations(project_path).await?;
142 if !style_violations.is_empty() {
143 eprintln!(
144 "\n⚠️ Ferrous Forge style warnings ({} violations):",
145 style_violations.len()
146 );
147 for v in style_violations.iter().take(5) {
148 eprintln!(
149 " {:?}: {}:{} — {}",
150 v.violation_type,
151 v.file.display(),
152 v.line,
153 v.message.lines().next().unwrap_or("")
154 );
155 }
156 if style_violations.len() > 5 {
157 eprintln!(
158 " ... and {} more (run 'ferrous-forge validate' \
159 for full list)",
160 style_violations.len() - 5
161 );
162 }
163 eprintln!(" (These will block 'cargo publish'. Fix before publishing.)");
164 eprintln!(" (Set FERROUS_FORGE_BYPASS=true to suppress these warnings.)\n");
165 }
166 } else {
167 tracing::info!(
168 "FERROUS_FORGE_BYPASS — style warnings suppressed (locked settings still checked)"
169 );
170 }
171
172 Ok(())
173}
174
175pub fn has_locked_violations(violations: &[crate::validation::Violation]) -> bool {
177 violations.iter().any(|v| {
178 matches!(
179 v.violation_type,
180 ViolationType::WrongEdition
181 | ViolationType::OldRustVersion
182 | ViolationType::LockedSetting
183 )
184 })
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_cargo_interceptor_creation() {
193 let interceptor = CargoInterceptor::new();
194 assert!(interceptor.enforce_dogfooding);
195 }
196}