macula_rust/frame/
check_payload.rs1use crate::cbor::{self, Value, MAX_ELEMENTS, MAX_NESTING_DEPTH};
9
10use super::{FrameError, MAX_FRAME_BYTES};
11
12pub const MAX_PAYLOAD_NESTING: usize = MAX_NESTING_DEPTH - 1;
15
16pub const FRAME_RESERVED_ELEMENTS: usize = 64;
19
20pub const MAX_PAYLOAD_ELEMENTS: usize = MAX_ELEMENTS - FRAME_RESERVED_ELEMENTS;
22
23pub fn check_payload(payload: &Value) -> Result<(), FrameError> {
26 let mut check = RuleCheck {
27 subject: "payload",
28 max_items: MAX_PAYLOAD_ELEMENTS,
29 max_nesting: MAX_PAYLOAD_NESTING,
30 items: 0,
31 };
32 check
33 .value(payload, &mut Vec::new())
34 .map_err(FrameError::Payload)?;
35 let encoded = cbor::encode(payload).map_err(|e| FrameError::Payload(e.to_string()))?;
36 if encoded.len() > MAX_FRAME_BYTES {
37 return Err(FrameError::Payload(format!(
38 "the payload encodes to {} bytes, over the {MAX_FRAME_BYTES}-byte frame cap",
39 encoded.len()
40 )));
41 }
42 Ok(())
43}
44
45pub fn check_frame(frame: &Value) -> Result<(), FrameError> {
48 let mut check = RuleCheck {
49 subject: "frame",
50 max_items: MAX_ELEMENTS,
51 max_nesting: MAX_NESTING_DEPTH,
52 items: 0,
53 };
54 check
55 .value(frame, &mut Vec::new())
56 .map_err(FrameError::BreaksDecodingRule)
57}
58
59struct RuleCheck {
62 subject: &'static str,
63 max_items: usize,
64 max_nesting: usize,
65 items: usize,
66}
67
68impl RuleCheck {
69 fn value(&mut self, v: &Value, path: &mut Vec<String>) -> Result<(), String> {
70 self.items += 1;
71 if self.items > self.max_items {
72 return Err(format!(
73 "the {} holds more than {} items, at {}",
74 self.subject,
75 self.max_items,
76 self.at(path)
77 ));
78 }
79 match v {
80 Value::Float(f) if !f.is_finite() => {
81 Err(format!("a float that is not finite at {}", self.at(path)))
82 }
83 Value::Int(n) if i64::try_from(*n).is_err() => Err(format!(
84 "an integer outside -2^63 to 2^63-1 at {}",
85 self.at(path)
86 )),
87 Value::List(items) => {
88 self.nesting(path)?;
89 for (i, item) in items.iter().enumerate() {
90 path.push(i.to_string());
91 self.value(item, path)?;
92 path.pop();
93 }
94 Ok(())
95 }
96 Value::Map(pairs) => {
97 self.nesting(path)?;
98 let mut seen = std::collections::HashSet::with_capacity(pairs.len());
99 for (key, value) in pairs {
100 if !matches!(key, Value::Text(_) | Value::Int(_)) {
101 return Err(format!(
102 "a map key that is not text or an integer at {}",
103 self.at(path)
104 ));
105 }
106 self.value(key, path)?;
107 let encoded = cbor::encode(key).map_err(|e| e.to_string())?;
108 if !seen.insert(encoded) {
109 return Err(format!(
110 "two keys of the map at {} encode alike",
111 self.at(path)
112 ));
113 }
114 path.push(match key {
115 Value::Text(t) => t.clone(),
116 other => format!("{other:?}"),
117 });
118 self.value(value, path)?;
119 path.pop();
120 }
121 Ok(())
122 }
123 _ => Ok(()),
124 }
125 }
126
127 fn nesting(&self, path: &[String]) -> Result<(), String> {
129 if path.len() >= self.max_nesting {
130 return Err(format!(
131 "lists and maps at {} nest more than {} levels",
132 self.at(path),
133 self.max_nesting
134 ));
135 }
136 Ok(())
137 }
138
139 fn at(&self, path: &[String]) -> String {
140 if path.is_empty() {
141 format!("the {} root", self.subject)
142 } else {
143 path.join(".")
144 }
145 }
146}