spec_driven_docs/services/
status.rs1use camino::Utf8Path;
10use serde::Serialize;
11
12use crate::domain::profile::{DocsRoot, ProfileId};
13use crate::domain::version::CanonVersion;
14use crate::error::AppError;
15use crate::services::verifier;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
19#[serde(rename_all = "kebab-case")]
20pub enum Alignment {
21 Aligned,
23 BinaryNewer,
25 InstanceNewer,
27}
28
29#[derive(Debug, Serialize)]
31pub struct StatusReport {
32 pub instance: bool,
34 pub profile: Option<ProfileId>,
36 pub docs_root: Option<DocsRoot>,
38 pub canon_version: Option<CanonVersion>,
40 pub binary_version: CanonVersion,
42 pub alignment: Option<Alignment>,
44 pub managed_drift: usize,
46 pub adopted_drift: usize,
48 pub failures: usize,
50 pub ok: Option<bool>,
52}
53
54fn absent() -> StatusReport {
55 StatusReport {
56 instance: false,
57 profile: None,
58 docs_root: None,
59 canon_version: None,
60 binary_version: CanonVersion::current(),
61 alignment: None,
62 managed_drift: 0,
63 adopted_drift: 0,
64 failures: 0,
65 ok: None,
66 }
67}
68
69pub fn status(target: &Utf8Path) -> Result<StatusReport, AppError> {
77 let manifest = match verifier::read_manifest(target) {
78 Ok(manifest) => manifest,
79 Err(AppError::ManifestMissing(_)) => return Ok(absent()),
80 Err(error) => return Err(error),
81 };
82 let report = verifier::verify(target)?;
83 let binary = CanonVersion::current();
84 let alignment = match manifest.canon_version.cmp(&binary) {
85 std::cmp::Ordering::Equal => Alignment::Aligned,
86 std::cmp::Ordering::Less => Alignment::BinaryNewer,
87 std::cmp::Ordering::Greater => Alignment::InstanceNewer,
88 };
89 Ok(StatusReport {
90 instance: true,
91 profile: Some(manifest.profile),
92 docs_root: Some(manifest.docs_root),
93 canon_version: Some(manifest.canon_version),
94 binary_version: binary,
95 alignment: Some(alignment),
96 managed_drift: report.managed_drift,
97 adopted_drift: report.adopted_drift,
98 failures: report.failures,
99 ok: Some(report.failures == 0),
100 })
101}