tokmd_types/
tokmd_packets.rs1use std::collections::BTreeMap;
7
8use serde::{Deserialize, Serialize};
9
10pub const TOKMD_PACKETS_SCHEMA: &str = "tokmd.packets/v1";
12
13pub const BUN_UB_PACKET_PRESETS: &[&str] = &[
15 "bun-ub-handoff",
16 "bun-ub-pr-body",
17 "bun-ub-ledger-note",
18 "bun-ub-review-map",
19 "bun-ub-next-pick",
20];
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct TokmdPacketsManifest {
25 pub schema: String,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub producer: Option<TokmdPacketsProducer>,
28 #[serde(default, skip_serializing_if = "Vec::is_empty")]
29 pub inputs_present: Vec<String>,
30 #[serde(default, skip_serializing_if = "Vec::is_empty")]
31 pub inputs_absent: Vec<String>,
32 #[serde(default, skip_serializing_if = "Vec::is_empty")]
33 pub non_claims: Vec<String>,
34 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
35 pub preset_inputs: BTreeMap<String, PacketPresetInput>,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct TokmdPacketsProducer {
41 pub name: String,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub version: Option<String>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub tokmd_run: Option<bool>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
50pub struct PacketPresetInput {
51 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
52 pub sections: BTreeMap<String, String>,
53 #[serde(default, skip_serializing_if = "Vec::is_empty")]
54 pub limitations: Vec<String>,
55 #[serde(default, skip_serializing_if = "Vec::is_empty")]
56 pub missing_sections: Vec<String>,
57}
58
59impl TokmdPacketsManifest {
60 pub fn schema_matches(&self) -> bool {
62 self.schema == TOKMD_PACKETS_SCHEMA
63 }
64
65 pub fn preset_is_known(preset: &str) -> bool {
67 BUN_UB_PACKET_PRESETS.contains(&preset)
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn tokmd_packets_schema_constant() {
77 assert_eq!(TOKMD_PACKETS_SCHEMA, "tokmd.packets/v1");
78 }
79
80 #[test]
81 fn bun_ub_presets_are_stable() {
82 assert_eq!(BUN_UB_PACKET_PRESETS.len(), 5);
83 assert!(BUN_UB_PACKET_PRESETS.contains(&"bun-ub-handoff"));
84 }
85
86 #[test]
87 fn manifest_roundtrip_preserves_preset_inputs() {
88 let mut sections = BTreeMap::new();
89 sections.insert("candidate_identity".into(), "seed-42".into());
90 let manifest = TokmdPacketsManifest {
91 schema: TOKMD_PACKETS_SCHEMA.into(),
92 producer: Some(TokmdPacketsProducer {
93 name: "unsafe-review".into(),
94 version: Some("0.1.0".into()),
95 tokmd_run: Some(false),
96 }),
97 inputs_present: vec!["manual-candidates.json".into()],
98 inputs_absent: vec!["comment-plan.json".into()],
99 non_claims: vec!["Does not prove UB.".into()],
100 preset_inputs: BTreeMap::from([(
101 "bun-ub-handoff".into(),
102 PacketPresetInput {
103 sections,
104 limitations: vec!["witness-plan.md absent".into()],
105 missing_sections: vec!["test or witness target".into()],
106 },
107 )]),
108 };
109 let json = serde_json::to_string(&manifest).unwrap();
110 let back: TokmdPacketsManifest = serde_json::from_str(&json).unwrap();
111 assert_eq!(back, manifest);
112 assert!(back.schema_matches());
113 }
114}