1use sim_codec_json::expr_to_json;
2use sim_kernel::{ContentId, Datum, Expr, Result, Symbol};
3
4use crate::warrant::content_id_datum;
5use crate::{BridgePacket, BridgePart};
6
7pub fn canonical_packet_datum(packet: &BridgePacket) -> Datum {
9 let packet = packet.canonicalized();
10 Datum::Node {
11 tag: Symbol::qualified("bridge", "Packet"),
12 fields: vec![
13 (
14 Symbol::new("header"),
15 Datum::Node {
16 tag: Symbol::qualified("bridge", "Header"),
17 fields: vec![
18 (Symbol::new("cid"), Datum::Nil),
19 (Symbol::new("move"), Datum::Symbol(packet.header.move_kind)),
20 (Symbol::new("from"), Datum::String(packet.header.from)),
21 (
22 Symbol::new("to"),
23 Datum::Vector(
24 packet.header.to.into_iter().map(Datum::String).collect(),
25 ),
26 ),
27 (Symbol::new("role"), Datum::Symbol(packet.header.role)),
28 (
29 Symbol::new("parents"),
30 Datum::Vector(
31 packet
32 .header
33 .parents
34 .into_iter()
35 .map(Datum::String)
36 .collect(),
37 ),
38 ),
39 (Symbol::new("task"), Datum::Symbol(packet.header.task)),
40 (Symbol::new("output"), Datum::Symbol(packet.header.output)),
41 (
42 Symbol::new("ceiling"),
43 Datum::Vector(
44 packet
45 .header
46 .ceiling
47 .into_iter()
48 .map(Datum::Symbol)
49 .collect(),
50 ),
51 ),
52 (
53 Symbol::new("context"),
54 Datum::Vector(
55 packet
56 .header
57 .context
58 .into_iter()
59 .map(Datum::Symbol)
60 .collect(),
61 ),
62 ),
63 (
64 Symbol::new("provenance"),
65 Datum::Node {
66 tag: Symbol::qualified("bridge", "Provenance"),
67 fields: vec![
68 (
69 Symbol::new("author"),
70 Datum::Symbol(packet.header.provenance.author),
71 ),
72 (
73 Symbol::new("card"),
74 packet
75 .header
76 .provenance
77 .card
78 .map(Datum::String)
79 .unwrap_or(Datum::Nil),
80 ),
81 ],
82 },
83 ),
84 ],
85 },
86 ),
87 (
88 Symbol::new("body"),
89 Datum::Vector(packet.body.iter().map(part_datum).collect()),
90 ),
91 (
92 Symbol::new("warrant"),
93 packet
94 .warrant
95 .map(|warrant| Datum::Node {
96 tag: Symbol::qualified("bridge", "Warrant"),
97 fields: vec![
98 (Symbol::new("moves"), content_id_datum(&warrant.moves)),
99 (Symbol::new("frames"), content_id_datum(&warrant.frames)),
100 (
101 Symbol::new("parts"),
102 Datum::Vector(
103 warrant
104 .parts
105 .into_iter()
106 .map(|(kind, id)| Datum::Node {
107 tag: Symbol::qualified("bridge", "WarrantPart"),
108 fields: vec![
109 (Symbol::new("kind"), Datum::Symbol(kind)),
110 (Symbol::new("cid"), content_id_datum(&id)),
111 ],
112 })
113 .collect(),
114 ),
115 ),
116 ],
117 })
118 .unwrap_or(Datum::Nil),
119 ),
120 ],
121 }
122}
123
124pub fn packet_content_id(packet: &BridgePacket) -> Result<ContentId> {
126 canonical_packet_datum(packet).content_id()
127}
128
129pub fn content_id_string(id: &ContentId) -> String {
131 format!("{}:{}", id.algorithm.as_qualified_str(), hex(&id.bytes))
132}
133
134pub fn stamp_packet_cid(packet: &BridgePacket) -> Result<BridgePacket> {
136 let mut packet = packet.canonicalized();
137 packet.header.cid = Some(content_id_string(&packet_content_id(&packet)?));
138 Ok(packet)
139}
140
141pub fn verify_packet_cid(packet: &BridgePacket) -> Result<()> {
143 let expected = content_id_string(&packet_content_id(packet)?);
144 match &packet.header.cid {
145 Some(actual) if actual == &expected => Ok(()),
146 Some(actual) => Err(sim_kernel::Error::Eval(format!(
147 "BRIDGE packet cid mismatch: expected {expected}, found {actual}"
148 ))),
149 None => Err(sim_kernel::Error::Eval(
150 "BRIDGE packet has no cid to verify".to_owned(),
151 )),
152 }
153}
154
155fn part_datum(part: &BridgePart) -> Datum {
156 Datum::Node {
157 tag: Symbol::qualified("bridge", "Part"),
158 fields: vec![
159 (Symbol::new("id"), Datum::Symbol(part.id.clone())),
160 (Symbol::new("kind"), Datum::Symbol(part.kind.clone())),
161 (Symbol::new("payload"), expr_datum(&part.payload)),
162 ],
163 }
164}
165
166fn expr_datum(expr: &Expr) -> Datum {
167 Datum::Node {
168 tag: Symbol::qualified("bridge", "ExprJson"),
169 fields: vec![(
170 Symbol::new("json"),
171 Datum::String(serde_json::to_string(&expr_to_json(expr)).expect("JSON value encodes")),
172 )],
173 }
174}
175
176fn hex(bytes: &[u8]) -> String {
177 bytes.iter().map(|byte| format!("{byte:02x}")).collect()
178}