Skip to main content

harmos_runtime/evidence/
fact.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17
18// Third Party Imports
19use serde::de::DeserializeOwned;
20use serde::{Deserialize, Serialize};
21use serde_json::value::RawValue;
22// Local Imports
23use crate::evidence::EvidenceError;
24use crate::{DefinitionId, Identified, state::At, state::FactSource, state::Position, state::SystemFact};
25
26/* === Constants === */
27
28pub(crate) const SOURCE: &str = "artifact";
29
30pub(crate) const FINALIZED: &str = "finalized";
31
32pub(crate) const VERSION: u32 = 1;
33
34/* === Macros === */
35
36/* === Type Aliases === */
37
38/* === Structs === */
39
40#[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
64/* === Enums === */
65
66/* === Traits === */
67
68pub trait EvidenceKind: Identified {
69    type Details: Serialize + DeserializeOwned;
70}
71
72/* === Implementations === */
73
74impl 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
125/* === Public Functions === */
126
127/* === Private Functions === */
128
129fn 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/* === Tests === */