1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use sha2::{Digest, Sha256};
4
5use crate::error::ActorError;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct Fact {
11 pub seq: u64,
12 #[serde(rename = "type")]
13 pub kind: String,
14 pub key: String,
15 pub cause: Option<String>,
16 pub payload: Value,
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct NewFact {
21 pub kind: String,
22 pub key: String,
23 pub payload: Value,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct LogCut {
28 pub seq: u64,
29 pub digest: String,
30}
31
32#[derive(Debug, Deserialize)]
33#[serde(deny_unknown_fields)]
34struct FactDto {
35 seq: u64,
36 #[serde(rename = "type")]
37 kind: String,
38 key: String,
39 cause: Option<String>,
40 payload: Value,
41}
42
43pub fn parse_fact_json(value: &str) -> Result<Fact, ActorError> {
44 let dto: FactDto =
45 serde_json::from_str(value).map_err(|error| ActorError::Schema(error.to_string()))?;
46 if dto.kind.is_empty() || dto.key.is_empty() {
47 return Err(ActorError::Schema("type and key are required".into()));
48 }
49 Ok(Fact {
50 seq: dto.seq,
51 kind: dto.kind,
52 key: dto.key,
53 cause: dto.cause,
54 payload: dto.payload,
55 })
56}
57
58pub fn digest_facts(facts: &[Fact]) -> String {
59 let mut hasher = Sha256::new();
60 for fact in facts {
61 hasher.update(fact.seq.to_string());
62 hasher.update(fact.kind.as_bytes());
63 hasher.update(fact.key.as_bytes());
64 hasher.update(fact.cause.as_deref().unwrap_or(""));
65 hasher.update(fact.payload.to_string());
66 hasher.update([0xff]);
67 }
68 format!("{:x}", hasher.finalize())
69}
70
71pub fn cut_log(facts: &[Fact]) -> LogCut {
72 LogCut {
73 seq: facts.last().map(|fact| fact.seq).unwrap_or(0),
74 digest: digest_facts(facts),
75 }
76}