harmos_runtime/evidence/
fact.rs1use serde::de::DeserializeOwned;
20use serde::{Deserialize, Serialize};
21use serde_json::value::RawValue;
22use crate::evidence::EvidenceError;
24use crate::{DefinitionId, Identified, state::At, state::FactSource, state::Position, state::SystemFact};
25
26pub(crate) const SOURCE: &str = "artifact";
29
30pub(crate) const FINALIZED: &str = "finalized";
31
32pub(crate) const VERSION: u32 = 1;
33
34#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct EvidenceRecord {
42 pub kind: DefinitionId,
43 pub version: u32,
44 pub scope: String,
45 pub key: String,
46 pub length: u64,
47 pub finalized: Option<At<u64>>,
48 described: Vec<u8>,
49}
50
51#[derive(Serialize, Deserialize)]
52struct Recorded {
53 kind: String,
54 version: u32,
55 scope: String,
56 key: String,
57 digest: u64,
58 length: u64,
59
60 #[serde(default = "nothing")]
61 details: Box<RawValue>,
62}
63
64pub trait EvidenceKind: Identified {
69 type Details: Serialize + DeserializeOwned;
70}
71
72impl EvidenceRecord {
75 pub fn details<K: EvidenceKind>(&self) -> Result<K::Details, EvidenceError> {
76 let (named, _) = K::definition();
77 if named != self.kind {
78 return Err(EvidenceError::Mismatched { kind: self.kind.clone(), named });
79 }
80
81 serde_json::from_slice(&self.described)
82 .map_err(|error| EvidenceError::Malformed { kind: self.kind.clone(), cause: error.to_string() })
83 }
84
85 pub(crate) fn new(kind: DefinitionId, version: u32, scope: String, key: String, length: u64, described: Vec<u8>) -> Self {
86 Self { kind, version, scope, key, length, finalized: None, described }
87 }
88
89 pub(crate) fn folded(fact: &SystemFact, position: Position) -> Option<Self> {
90 let FactSource::Work(source) = &fact.source else {
91 return None;
92 };
93 if source != SOURCE || fact.name != FINALIZED {
94 return None;
95 }
96 let recorded: Recorded = serde_json::from_slice(&fact.payload).ok()?;
97
98 Some(Self {
99 kind: DefinitionId::new(recorded.kind),
100 version: recorded.version,
101 scope: recorded.scope,
102 key: recorded.key,
103 length: recorded.length,
104 finalized: Some(At { value: recorded.digest, position }),
105 described: recorded.details.get().as_bytes().to_vec(),
106 })
107 }
108
109 pub(crate) fn recorded(&self, digest: u64) -> SystemFact {
110 let payload = serde_json::to_vec(&Recorded {
111 kind: self.kind.as_str().to_owned(),
112 version: self.version,
113 scope: self.scope.clone(),
114 key: self.key.clone(),
115 digest,
116 length: self.length,
117 details: described(&self.described),
118 })
119 .expect("the fixed evidence fact shape serializes");
120
121 SystemFact { source: FactSource::Work(SOURCE.to_owned()), name: FINALIZED.to_owned(), version: VERSION, payload }
122 }
123}
124
125fn nothing() -> Box<RawValue> {
130 described(b"null")
131}
132
133fn described(details: &[u8]) -> Box<RawValue> {
134 let text = String::from_utf8(details.to_vec()).expect("a serialized payload is JSON text");
135
136 RawValue::from_string(text).expect("a serialized payload is JSON")
137}
138
139