ferrous_forge/safety/
execution.rs1use super::{
7 CheckType, PipelineStage, checks,
8 config::StageConfig,
9 report::{CheckResult, SafetyReport},
10};
11use crate::Result;
12use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
13use std::path::Path;
14use std::time::Duration;
15
16pub struct ExecutionManager {
18 show_progress: bool,
20 parallel_checks: bool,
22}
23
24impl ExecutionManager {
25 pub fn new(show_progress: bool, parallel_checks: bool) -> Self {
27 Self {
28 show_progress,
29 parallel_checks,
30 }
31 }
32
33 pub fn setup_progress_display(&self, stage: PipelineStage) -> Option<MultiProgress> {
35 let multi_progress = if self.show_progress {
36 Some(MultiProgress::new())
37 } else {
38 None
39 };
40
41 println!(
42 "🛡️ Ferrous Forge Safety Pipeline - {}",
43 stage.display_name()
44 );
45 println!("{}", "=".repeat(50));
46
47 multi_progress
48 }
49
50 pub async fn execute_stage_checks(
52 &self,
53 stage_config: &StageConfig,
54 report: &mut SafetyReport,
55 multi_progress: Option<&MultiProgress>,
56 project_path: &Path,
57 ) -> Result<()> {
58 let checks = &stage_config.checks;
59
60 if self.parallel_checks && checks.len() > 1 {
61 self.run_checks_parallel(checks, report, multi_progress, project_path)
62 .await
63 } else {
64 self.run_checks_sequential(checks, report, multi_progress, project_path)
65 .await
66 }
67 }
68
69 async fn run_checks_sequential(
71 &self,
72 checks: &[CheckType],
73 report: &mut SafetyReport,
74 multi_progress: Option<&MultiProgress>,
75 project_path: &Path,
76 ) -> Result<()> {
77 for check_type in checks {
78 let pb = if let Some(mp) = multi_progress {
79 let pb = mp.add(ProgressBar::new_spinner());
80 pb.set_style(
81 ProgressStyle::default_spinner()
82 .template("{spinner:.green} {msg}")
83 .unwrap_or_else(|_| ProgressStyle::default_spinner()),
84 );
85 pb.set_message(format!("Running {}...", check_type.display_name()));
86 pb.enable_steady_tick(Duration::from_millis(100));
87 Some(pb)
88 } else {
89 None
90 };
91
92 let check_result = execute_check(*check_type, project_path).await?;
93
94 if let Some(pb) = pb {
95 pb.finish_with_message(format!(
96 "{} {} ({:.2}s)",
97 check_result.status_emoji(),
98 check_type.display_name(),
99 check_result.duration.as_secs_f64()
100 ));
101 } else {
102 println!(
103 " {} {} ({:.2}s)",
104 check_result.status_emoji(),
105 check_type.display_name(),
106 check_result.duration.as_secs_f64()
107 );
108 }
109
110 report.add_check(check_result);
111 }
112
113 Ok(())
114 }
115
116 async fn run_checks_parallel(
118 &self,
119 checks: &[CheckType],
120 report: &mut SafetyReport,
121 _multi_progress: Option<&MultiProgress>,
122 project_path: &Path,
123 ) -> Result<()> {
124 self.run_checks_sequential(checks, report, _multi_progress, project_path)
127 .await
128 }
129}
130
131pub async fn execute_check(check_type: CheckType, project_path: &Path) -> Result<CheckResult> {
133 match check_type {
134 CheckType::Format => checks::format::run(project_path).await,
135 CheckType::Clippy => checks::clippy::run(project_path).await,
136 CheckType::Build => checks::build::run(project_path).await,
137 CheckType::Test => checks::test::run(project_path).await,
138 CheckType::Audit => checks::audit::run(project_path).await,
139 CheckType::Doc => checks::doc::run(project_path).await,
140 CheckType::PublishDryRun => checks::publish::run(project_path).await,
141 CheckType::Standards => checks::standards::run(project_path).await,
142 CheckType::DocCoverage => checks::doc::coverage_check(project_path).await,
143 CheckType::License => checks::license::run(project_path).await,
144 CheckType::Semver => checks::semver::run(project_path).await,
145 }
146}
147
148pub fn handle_check_result(
150 check_result: Result<CheckResult>,
151 check_type: CheckType,
152) -> Result<CheckResult> {
153 match check_result {
154 Ok(result) => Ok(result),
155 Err(e) => {
156 let mut result = CheckResult::new(check_type);
157 result.add_error(format!("Check failed: {}", e));
158 result.add_suggestion("Check the error message above for details");
159 Ok(result)
160 }
161 }
162}
163
164pub fn get_stage_for_check(check_type: CheckType) -> PipelineStage {
166 for stage in [
168 PipelineStage::PreCommit,
169 PipelineStage::PrePush,
170 PipelineStage::Publish,
171 ] {
172 if CheckType::for_stage(stage).contains(&check_type) {
173 return stage;
174 }
175 }
176 PipelineStage::PreCommit }