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).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
140/* === Public Functions === */
141
142/* === Private Functions === */
143
144fn 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/* === Tests === */