macula_rust/record/
procedure_advertisement.rs1use crate::cbor::Value;
8
9use super::{entry, id_field, malformed, text_field, unsigned, Record, RecordError, RecordType};
10
11#[derive(Debug, Clone, Default, PartialEq, Eq)]
14pub enum Authorization {
15 #[default]
17 None,
18 Delegation {
21 org_directory: Vec<u8>,
22 procedure_delegation: Vec<u8>,
23 },
24 Unsupported,
26 Malformed,
29}
30
31#[derive(Debug, Clone, Default, PartialEq, Eq)]
34pub struct ProcedureAdvertisementOptions {
35 pub authorization: Authorization,
36 pub ttl_ms: u64,
37}
38
39pub fn new_procedure_advertisement(
43 advertiser_node: &[u8; 32],
44 realm_id: &[u8; 32],
45 procedure: &str,
46 serving_station: &[u8; 32],
47 opts: &ProcedureAdvertisementOptions,
48) -> Result<Record, RecordError> {
49 let mut entries = vec![
50 entry("realm_id", Value::Bytes(realm_id.to_vec())),
51 entry("procedure", Value::text(procedure)),
52 entry("advertiser_node", Value::Bytes(advertiser_node.to_vec())),
53 entry("serving_station", Value::Bytes(serving_station.to_vec())),
54 ];
55 match &opts.authorization {
56 Authorization::None => {}
57 Authorization::Delegation {
58 org_directory,
59 procedure_delegation,
60 } => entries.push(entry(
61 "authorization",
62 Value::Map(vec![
63 entry("org_directory", Value::Bytes(org_directory.clone())),
64 entry(
65 "procedure_delegation",
66 Value::Bytes(procedure_delegation.clone()),
67 ),
68 ]),
69 )),
70 Authorization::Unsupported => return Err(RecordError::AuthorizationFormUnsupported),
71 Authorization::Malformed => return Err(malformed("an authorization in no form")),
72 }
73 Ok(unsigned(
74 RecordType::PROCEDURE_ADVERTISEMENT,
75 Value::Map(entries),
76 opts.ttl_ms,
77 ))
78}
79
80#[derive(Debug, Clone, Default, PartialEq, Eq)]
82pub struct ProcedureAdvertisement {
83 pub realm_id: [u8; 32],
84 pub procedure: String,
85 pub advertiser_node: [u8; 32],
86 pub serving_station: [u8; 32],
87 pub authorization: Authorization,
88}
89
90pub fn read_procedure_advertisement(r: &Record) -> Result<ProcedureAdvertisement, RecordError> {
92 if r.record_type != RecordType::PROCEDURE_ADVERTISEMENT {
93 return Err(malformed("not a procedure advertisement"));
94 }
95 let p = &r.payload;
96 Ok(ProcedureAdvertisement {
97 realm_id: id_field(p, "realm_id"),
98 procedure: text_field(p, "procedure"),
99 advertiser_node: id_field(p, "advertiser_node"),
100 serving_station: id_field(p, "serving_station"),
101 authorization: read_authorization(p),
102 })
103}
104
105fn read_authorization(payload: &Value) -> Authorization {
106 let Some(value) = payload.get("authorization") else {
107 return Authorization::None;
108 };
109 let Value::Map(pairs) = value else {
110 return Authorization::Malformed;
111 };
112 let (Some(directory), Some(delegation)) = (
113 value.get("org_directory"),
114 value.get("procedure_delegation"),
115 ) else {
116 return Authorization::Unsupported;
117 };
118 if pairs.len() != 2 {
119 return Authorization::Unsupported;
120 }
121 match (directory, delegation) {
122 (Value::Bytes(d), Value::Bytes(g)) => Authorization::Delegation {
123 org_directory: d.clone(),
124 procedure_delegation: g.clone(),
125 },
126 _ => Authorization::Malformed,
127 }
128}