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).map_err(|error| EvidenceError::Malformed {
82 kind: self.kind.clone(),
83 cause: error.to_string(),
84 })
85 }
86
87 pub(crate) fn new(kind: DefinitionId, version: u32, scope: String, key: String, length: u64, described: Vec<u8>) -> Self {
88 Self {
89 kind,
90 version,
91 scope,
92 key,
93 length,
94 finalized: None,
95 described,
96 }
97 }
98
99 pub(crate) fn folded(fact: &SystemFact, position: Position) -> Option<Self> {
100 let FactSource::Work(source) = &fact.source else {
101 return None;
102 };
103 if source != SOURCE || fact.name != FINALIZED {
104 return None;
105 }
106 let recorded: Recorded = serde_json::from_slice(&fact.payload).ok()?;
107
108 Some(Self {
109 kind: DefinitionId::new(recorded.kind),
110 version: recorded.version,
111 scope: recorded.scope,
112 key: recorded.key,
113 length: recorded.length,
114 finalized: Some(At { value: recorded.digest, position }),
115 described: recorded.details.get().as_bytes().to_vec(),
116 })
117 }
118
119 pub(crate) fn recorded(&self, digest: u64) -> SystemFact {
120 let payload = serde_json::to_vec(&Recorded {
121 kind: self.kind.as_str().to_owned(),
122 version: self.version,
123 scope: self.scope.clone(),
124 key: self.key.clone(),
125 digest,
126 length: self.length,
127 details: described(&self.described),
128 })
129 .expect("the fixed evidence fact shape serializes");
130
131 SystemFact {
132 source: FactSource::Work(SOURCE.to_owned()),
133 name: FINALIZED.to_owned(),
134 version: VERSION,
135 payload,
136 }
137 }
138}
139
140fn nothing() -> Box<RawValue> {
145 described(b"null")
146}
147
148fn described(details: &[u8]) -> Box<RawValue> {
149 let text = String::from_utf8(details.to_vec()).expect("a serialized payload is JSON text");
150
151 RawValue::from_string(text).expect("a serialized payload is JSON")
152}
153
154