use std::collections::BTreeMap;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct RulesetManifest {
pub bundle_version: String,
pub effective_date: DateTime<Utc>,
#[serde(default)]
pub act_citations: Vec<String>,
#[serde(default)]
pub schema_versions: BTreeMap<String, String>,
pub content_sha256: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignedBundle {
pub manifest_jws: String,
pub content: serde_json::Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RulesetProvenance {
Verified,
LocalBaseline,
}
#[derive(Debug, Clone)]
pub struct RulesetAcceptance {
manifest: RulesetManifest,
content: serde_json::Value,
provenance: RulesetProvenance,
}
impl RulesetAcceptance {
#[must_use]
pub fn unverified_baseline(manifest: RulesetManifest, content: serde_json::Value) -> Self {
Self {
manifest,
content,
provenance: RulesetProvenance::LocalBaseline,
}
}
pub(crate) fn verified(manifest: RulesetManifest, content: serde_json::Value) -> Self {
Self {
manifest,
content,
provenance: RulesetProvenance::Verified,
}
}
#[must_use]
pub fn provenance(&self) -> RulesetProvenance {
self.provenance
}
#[must_use]
pub fn manifest(&self) -> &RulesetManifest {
&self.manifest
}
#[must_use]
pub fn content(&self) -> &serde_json::Value {
&self.content
}
#[must_use]
pub fn version(&self) -> &str {
&self.manifest.bundle_version
}
}
#[derive(Debug, Clone, Copy)]
pub struct AcceptancePolicy<'a> {
pub now: DateTime<Utc>,
pub in_force: Option<&'a RulesetManifest>,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum RulesetError {
#[error("bundle signature invalid or not signed by the pinned publisher key")]
BadSignature,
#[error("bundle content hash mismatch — content does not match the signed manifest")]
ContentHashMismatch,
#[error(
"bundle '{bundle_version}' takes effect at {effective_date} and it is now {now} — \
not yet applicable"
)]
NotYetEffective {
bundle_version: String,
effective_date: DateTime<Utc>,
now: DateTime<Utc>,
},
#[error(
"bundle '{offered_version}' is effective {offered} but '{in_force_version}' effective \
{in_force} is already in force — refusing to roll back"
)]
Superseded {
offered_version: String,
offered: DateTime<Utc>,
in_force_version: String,
in_force: DateTime<Utc>,
},
#[error("malformed bundle: {0}")]
Malformed(String),
}