ferrous_forge/safety/
execution.rs

1//! Safety pipeline execution logic
2//!
3//! This module contains the core execution logic for safety checks,
4//! including progress display and parallel/sequential execution.
5
6use super::{
7    checks,
8    config::StageConfig,
9    report::{CheckResult, SafetyReport},
10    CheckType, PipelineStage,
11};
12use crate::Result;
13use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
14use std::path::Path;
15use std::time::Duration;
16
17/// Progress display and execution coordinator
18pub struct ExecutionManager {
19    /// Whether to show progress indicators
20    show_progress: bool,
21    /// Whether to run checks in parallel
22    parallel_checks: bool,
23}
24
25impl ExecutionManager {
26    /// Create a new execution manager
27    pub fn new(show_progress: bool, parallel_checks: bool) -> Self {
28        Self {
29            show_progress,
30            parallel_checks,
31        }
32    }
33
34    /// Setup progress indicators and display header
35    pub fn setup_progress_display(&self, stage: PipelineStage) -> Option<MultiProgress> {
36        let multi_progress = if self.show_progress {
37            Some(MultiProgress::new())
38        } else {
39            None
40        };
41
42        println!(
43            "🛡️  Ferrous Forge Safety Pipeline - {}",
44            stage.display_name()
45        );
46        println!("{}", "=".repeat(50));
47
48        multi_progress
49    }
50
51    /// Execute checks for the stage
52    pub async fn execute_stage_checks(
53        &self,
54        stage_config: &StageConfig,
55        report: &mut SafetyReport,
56        multi_progress: Option<&MultiProgress>,
57        project_path: &Path,
58    ) -> Result<()> {
59        let checks = &stage_config.checks;
60
61        if self.parallel_checks && checks.len() > 1 {
62            self.run_checks_parallel(checks, report, multi_progress, project_path)
63                .await
64        } else {
65            self.run_checks_sequential(checks, report, multi_progress, project_path)
66                .await
67        }
68    }
69
70    /// Run checks sequentially with progress indicators
71    async fn run_checks_sequential(
72        &self,
73        checks: &[CheckType],
74        report: &mut SafetyReport,
75        multi_progress: Option<&MultiProgress>,
76        project_path: &Path,
77    ) -> Result<()> {
78        for check_type in checks {
79            let pb = if let Some(mp) = multi_progress {
80                let pb = mp.add(ProgressBar::new_spinner());
81                pb.set_style(
82                    ProgressStyle::default_spinner()
83                        .template("{spinner:.green} {msg}")
84                        .unwrap_or_else(|_| ProgressStyle::default_spinner()),
85                );
86                pb.set_message(format!("Running {}...", check_type.display_name()));
87                pb.enable_steady_tick(Duration::from_millis(100));
88                Some(pb)
89            } else {
90                None
91            };
92
93            let check_result = execute_check(*check_type, project_path).await?;
94
95            if let Some(pb) = pb {
96                pb.finish_with_message(format!(
97                    "{} {} ({:.2}s)",
98                    check_result.status_emoji(),
99                    check_type.display_name(),
100                    check_result.duration.as_secs_f64()
101                ));
102            } else {
103                println!(
104                    "  {} {} ({:.2}s)",
105                    check_result.status_emoji(),
106                    check_type.display_name(),
107                    check_result.duration.as_secs_f64()
108                );
109            }
110
111            report.add_check(check_result);
112        }
113
114        Ok(())
115    }
116
117    /// Run checks in parallel for better performance
118    async fn run_checks_parallel(
119        &self,
120        checks: &[CheckType],
121        report: &mut SafetyReport,
122        _multi_progress: Option<&MultiProgress>,
123        project_path: &Path,
124    ) -> Result<()> {
125        // For now, implement as sequential until we add proper parallel execution
126        // Parallel execution requires careful handling of stdout/stderr
127        self.run_checks_sequential(checks, report, _multi_progress, project_path)
128            .await
129    }
130}
131
132/// Execute a specific check type
133pub async fn execute_check(check_type: CheckType, project_path: &Path) -> Result<CheckResult> {
134    match check_type {
135        CheckType::Format => checks::format::run(project_path).await,
136        CheckType::Clippy => checks::clippy::run(project_path).await,
137        CheckType::Build => checks::build::run(project_path).await,
138        CheckType::Test => checks::test::run(project_path).await,
139        CheckType::Audit => checks::audit::run(project_path).await,
140        CheckType::Doc => checks::doc::run(project_path).await,
141        CheckType::PublishDryRun => checks::publish::run(project_path).await,
142        CheckType::Standards => checks::standards::run(project_path).await,
143        CheckType::DocCoverage => checks::doc::coverage_check(project_path).await,
144        CheckType::License => checks::license::run(project_path).await,
145        CheckType::Semver => checks::semver::run(project_path).await,
146    }
147}
148
149/// Handle check result and convert errors to check results
150pub fn handle_check_result(
151    check_result: Result<CheckResult>,
152    check_type: CheckType,
153) -> Result<CheckResult> {
154    match check_result {
155        Ok(result) => Ok(result),
156        Err(e) => {
157            let mut result = CheckResult::new(check_type);
158            result.add_error(format!("Check failed: {}", e));
159            result.add_suggestion("Check the error message above for details");
160            Ok(result)
161        }
162    }
163}
164
165/// Get the appropriate stage for a check type
166pub fn get_stage_for_check(check_type: CheckType) -> PipelineStage {
167    // Find the first stage that includes this check
168    for stage in [
169        PipelineStage::PreCommit,
170        PipelineStage::PrePush,
171        PipelineStage::Publish,
172    ] {
173        if CheckType::for_stage(stage).contains(&check_type) {
174            return stage;
175        }
176    }
177    PipelineStage::PreCommit // Default fallback
178}