Skip to main content

auths_sdk/workflows/
diagnostics.rs

1//! Diagnostics workflow — orchestrates system health checks via injected providers.
2
3use crate::ports::diagnostics::{
4    CheckResult, ConfigIssue, CryptoDiagnosticProvider, DiagnosticError, DiagnosticReport,
5    GitDiagnosticProvider,
6};
7
8/// Orchestrates diagnostic checks without subprocess calls.
9///
10/// Args:
11/// * `G`: A [`GitDiagnosticProvider`] implementation.
12/// * `C`: A [`CryptoDiagnosticProvider`] implementation.
13///
14/// Usage:
15/// ```ignore
16/// let workflow = DiagnosticsWorkflow::new(posix_adapter.clone(), posix_adapter);
17/// let report = workflow.run()?;
18/// ```
19pub struct DiagnosticsWorkflow<G: GitDiagnosticProvider, C: CryptoDiagnosticProvider> {
20    git: G,
21    crypto: C,
22}
23
24impl<G: GitDiagnosticProvider, C: CryptoDiagnosticProvider> DiagnosticsWorkflow<G, C> {
25    /// Create a new diagnostics workflow with the given providers.
26    pub fn new(git: G, crypto: C) -> Self {
27        Self { git, crypto }
28    }
29
30    /// Run all diagnostic checks and return the aggregated report.
31    ///
32    /// Usage:
33    /// ```ignore
34    /// let report = workflow.run()?;
35    /// assert!(report.checks.iter().all(|c| c.passed));
36    /// ```
37    pub fn run(&self) -> Result<DiagnosticReport, DiagnosticError> {
38        let mut checks = Vec::new();
39
40        checks.push(self.git.check_git_version()?);
41        checks.push(self.crypto.check_ssh_keygen_available()?);
42
43        self.check_git_signing_config(&mut checks)?;
44
45        Ok(DiagnosticReport { checks })
46    }
47
48    fn check_git_signing_config(
49        &self,
50        checks: &mut Vec<CheckResult>,
51    ) -> Result<(), DiagnosticError> {
52        let required = [
53            ("gpg.format", "ssh"),
54            ("commit.gpgsign", "true"),
55            ("tag.gpgsign", "true"),
56        ];
57        let presence_only = ["user.signingkey", "gpg.ssh.program"];
58
59        let mut issues: Vec<ConfigIssue> = Vec::new();
60
61        for (key, expected) in &required {
62            match self.git.get_git_config(key)? {
63                Some(val) if val == *expected => {}
64                Some(actual) => {
65                    issues.push(ConfigIssue::Mismatch {
66                        key: key.to_string(),
67                        expected: expected.to_string(),
68                        actual,
69                    });
70                }
71                None => {
72                    issues.push(ConfigIssue::Absent(key.to_string()));
73                }
74            }
75        }
76
77        for key in &presence_only {
78            if self.git.get_git_config(key)?.is_none() {
79                issues.push(ConfigIssue::Absent(key.to_string()));
80            }
81        }
82
83        let passed = issues.is_empty();
84
85        checks.push(CheckResult {
86            name: "Git signing config".to_string(),
87            passed,
88            message: None,
89            config_issues: issues,
90        });
91
92        Ok(())
93    }
94}