pub mod apply;
pub mod classify;
pub mod compatibility;
pub mod decision;
pub mod derive;
pub mod evidence;
pub mod finding;
pub mod fingerprint;
pub mod guidance;
pub mod observe;
pub mod operation;
pub mod planner;
pub mod readiness;
pub mod session;
pub mod store;
use serde::{Deserialize, Serialize};
use crate::domain::ownership::Sha256;
use crate::domain::profile::ProfileId;
use crate::plan::classify::Classification;
use crate::plan::decision::Decision;
use crate::plan::evidence::Evidence;
use crate::plan::finding::{Finding, StyleCandidate};
use crate::plan::observe::{Corpus, Host, Installation, Repository};
use crate::plan::operation::Operation;
use crate::plan::readiness::{Precondition, Readiness};
pub const PLAN_SCHEMA: &str = "sdd.plan/1";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Identity {
pub schema: String,
pub plan_id: String,
pub created_at: String,
pub engine_version: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DesiredState {
pub selector: String,
pub release: String,
pub release_sha256: Sha256,
pub profile: Option<ProfileId>,
#[serde(default)]
pub reserved: Vec<String>,
pub declared: Declared,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Declared {
pub payload_schema: u32,
pub managed: usize,
pub adopted: usize,
pub sentinels: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ObservedState {
pub repository: Repository,
pub installation: Option<Installation>,
pub host: Host,
pub corpus: Corpus,
pub evidence_refs: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReleaseSource {
pub version: String,
pub provenance: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub registry_checksum: Option<Sha256>,
pub yanked: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub minimum_engine: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub guidance_coverage: Option<crate::plan::guidance::Coverage>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub guidance_steps: Vec<String>,
#[serde(default)]
pub guidance_excluded: usize,
pub evidence_refs: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Postcondition {
pub id: String,
pub statement: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Plan {
pub identity: Identity,
pub classification: Classification,
pub findings: Vec<Finding>,
pub style_candidates: Vec<StyleCandidate>,
pub desired_state: DesiredState,
pub observed_state: ObservedState,
pub release: ReleaseSource,
pub operations: Vec<Operation>,
pub preconditions: Vec<Precondition>,
pub decisions: Vec<Decision>,
pub postconditions: Vec<Postcondition>,
pub evidence: Vec<Evidence>,
pub readiness: Readiness,
pub input_fingerprint: Sha256,
}
impl Plan {
#[must_use]
pub const fn is_ready(&self) -> bool {
matches!(self.readiness, Readiness::Ready)
}
#[must_use]
pub fn findings_of(&self, kind: finding::FindingKind) -> Vec<&Finding> {
self.findings
.iter()
.filter(|found| found.kind == kind)
.collect()
}
#[must_use]
pub fn structural_count(&self) -> usize {
self.findings_of(finding::FindingKind::Structural).len()
}
}