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