cddl_cat/validate.rs
1//! This module contains code to validate serialized data.
2//!
3//! More precisely, it validates data that can be represented by [`Value`] trees.
4
5use crate::context::LookupContext;
6use crate::ivt::*;
7use crate::util::{mismatch, ValidateError, ValidateResult};
8use crate::value::Value;
9use std::collections::BTreeMap; // used in Value::Map
10use std::collections::HashMap;
11use std::collections::VecDeque;
12use std::convert::TryInto;
13use std::mem::discriminant;
14
15// A map from generic parameter name to the type being used here.
16#[derive(Clone, Debug, Default)]
17struct GenericMap<'a> {
18 map: HashMap<String, &'a Node>,
19 // Because we captured this map at a previous time, we may need to look up
20 // generic types from that previous context. We carry a copy of that
21 // Context with us to do those lookups.
22 past_ctx: Option<&'a Context<'a>>,
23}
24
25#[derive(Clone)]
26struct Context<'a> {
27 lookup: &'a dyn LookupContext,
28 generic_map: GenericMap<'a>,
29 depth: u32,
30}
31
32impl std::fmt::Debug for Context<'_> {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.debug_struct("Context")
35 .field("generic_map", &self.generic_map)
36 .finish()
37 }
38}
39
40struct NodeContext<'a> {
41 node: &'a Node,
42 ctx: Context<'a>,
43}
44
45impl<'a> Context<'a> {
46 // Set a maximum depth, to avoid infinite recursion in the case of
47 // circular rule references.
48 const MAX_DEPTH: u32 = 50;
49
50 fn inc_depth(&self) -> TempResult<u32> {
51 if self.depth >= Self::MAX_DEPTH {
52 Err(ValidateError::Structural("hit recursion limit".into()))
53 } else {
54 Ok(self.depth + 1)
55 }
56 }
57
58 // `lookup_rule` will first do a search for a local generic parameter name
59 // first, followed by a lookup for a rule by that name.
60
61 // To give an example:
62 // IP = [u8, u8, u8, u8]
63 // PORT = u16
64 // socket = [IP, PORT]
65 // conn<IP> = [name, socket, IP] # This is the generic parm IP not the rule IP.
66 //
67 // While validating "conn" we have a Context with a generic mapping for "IP".
68 // As soon as we traverse into "socket", we need for there to be a new context,
69 // where no "IP" exists (so we'll do the rule lookup instead).
70 //
71 fn lookup_rule(&'a self, rule: &'a Rule) -> TempResult<NodeContext<'a>> {
72 // First, check to see if the "rule name" is actually a generic parameter.
73 // TODO: this would be a lot easier if this were pre-processed by the flattener
74 // so that "rule lookup" and "generic type lookup" were two separate Node variants.
75 let generic_node: Option<&'a Node> = self.generic_map.map.get(&rule.name).cloned();
76 if let Some(node) = generic_node {
77 // If we stored a past_ctx along with the generic args, then pass that
78 // context along with the node that is substituting for this generic
79 // parameter name. Otherwise, return a new blank Context.
80 let ctx = match self.generic_map.past_ctx {
81 Some(ctx) => ctx.clone(),
82 None => self.blank()?,
83 };
84 return Ok(NodeContext { node, ctx });
85 }
86
87 let rule_def: &RuleDef = self.lookup.lookup_rule(&rule.name)?;
88
89 // Create a new context containing a new generic parameter map.
90 let ctx = self.derive(rule_def, rule)?;
91
92 Ok(NodeContext {
93 node: &rule_def.node,
94 ctx,
95 })
96 }
97
98 // Create a new blank Context (with no parameter map)
99 fn blank(&self) -> TempResult<Context<'a>> {
100 Ok(Context {
101 lookup: self.lookup,
102 generic_map: GenericMap::default(),
103 depth: self.inc_depth()?,
104 })
105 }
106
107 // Create a new Context, with optional generic parameter map.
108 fn derive(&'a self, rule_def: &'a RuleDef, rule: &'a Rule) -> TempResult<Context<'a>> {
109 let parms_len = rule_def.generic_parms.len();
110 let args_len = rule.generic_args.len();
111 if parms_len != args_len {
112 // Wrong number of generic arguments
113 return Err(ValidateError::GenericError);
114 }
115
116 // Zip the two Vecs:
117 // 1. rule_def.generic_parms
118 // 2. rule.generic_args
119 // and put them into a HashMap so we can do lookups from parm -> arg.
120 let map: HashMap<String, &'a Node> = rule_def
121 .generic_parms
122 .iter()
123 .cloned()
124 .zip(rule.generic_args.iter())
125 .collect();
126
127 let generic_map = GenericMap {
128 map,
129 past_ctx: Some(self),
130 };
131
132 Ok(Context {
133 lookup: self.lookup,
134 generic_map,
135 depth: self.inc_depth()?,
136 })
137 }
138}
139
140pub(crate) fn do_validate(
141 value: &Value,
142 rule_def: &RuleDef,
143 ctx: &dyn LookupContext,
144) -> ValidateResult {
145 // If the rule_def passed in requires generic parameters, we should
146 // return an error, because we don't have any way to specify them.
147 if !rule_def.generic_parms.is_empty() {
148 return Err(ValidateError::GenericError);
149 }
150
151 let ctx = Context {
152 lookup: ctx,
153 generic_map: GenericMap::default(),
154 depth: 0,
155 };
156 let node = &rule_def.node;
157 validate(value, node, &ctx)
158}
159
160type ValueMap = BTreeMap<Value, Value>;
161
162// A Result that returns some temporary value.
163type TempResult<T> = Result<T, ValidateError>;
164
165/// This struct allows us to maintain a map that is consumed during validation.
166struct WorkingMap {
167 map: ValueMap,
168 // A stack of lists; each list contains maybe-discarded elements,
169 // in (key, value) form.
170 snaps: VecDeque<VecDeque<(Value, Value)>>,
171}
172
173impl WorkingMap {
174 /// Makes a copy of an existing map's table.
175 fn new(value_map: &ValueMap) -> WorkingMap {
176 WorkingMap {
177 map: value_map.clone(),
178 snaps: VecDeque::new(),
179 }
180 }
181
182 // When we start speculatively matching map elements (e.g. in a Choice
183 // or Occur containing groups), we may fail the match partway through, and
184 // need to rewind to the most recent snapshot.
185 //
186 // It's possible for nested snapshots to exist; for example if we have a
187 // group-of-choices nested inside a group-of-choices.
188 //
189 // If one array is nested inside another, the inner array will get its
190 // own WorkingArray so snapshots aren't necessary in that case.
191 fn snapshot(&mut self) {
192 self.snaps.push_back(VecDeque::new());
193 }
194
195 // Restore the map to the point when we last called snapshot()
196 fn rewind(&mut self) {
197 // If validate code is implemented correctly, then unwrap() should
198 // never panic.
199 let mut top_snap = self.snaps.pop_back().unwrap();
200 // Drain the elements (order not important), and insert them back into
201 // the working map.
202 self.map.extend(top_snap.drain(..));
203 }
204
205 // We completed a match, so we can retire the most recent snapshot.
206 fn commit(&mut self) {
207 // If validate code is implemented correctly, then unwrap() should
208 // never panic.
209 // This throws away the list that was popped; those values were
210 // successfully matched and are no longer needed.
211 self.snaps.pop_back().unwrap();
212 }
213
214 // Peek at the value correspending to a given key (if any)
215 fn peek_at(&self, key: &Value) -> Option<&Value> {
216 self.map.get(key)
217 }
218
219 // Remove a value from the working map.
220 // If there is an active snapshot, stash the key/value pair there until
221 // we're certain we've matched the entire group.
222 fn remove(&mut self, key: &Value) {
223 // If validate code is implemented correctly, then unwrap() should
224 // never panic (we've already peeked at this value in order to match
225 // it.)
226 let value = self.map.remove(key).unwrap();
227 // If there is a current snapshot, preserve this element
228 // for later rewind.
229 if let Some(snap) = self.snaps.back_mut() {
230 snap.push_back((key.clone(), value));
231 }
232 }
233}
234
235/// This struct allows us to maintain a copy of an array that is consumed
236/// during validation.
237#[derive(Debug)]
238struct WorkingArray {
239 // The elements in the Value Array
240 array: VecDeque<Value>,
241 // A stack of lists; each list contains maybe-discarded elements.
242 snaps: VecDeque<VecDeque<Value>>,
243}
244
245impl WorkingArray {
246 /// Makes a copy of an existing map's table.
247 fn new(array: &[Value]) -> WorkingArray {
248 let deque: VecDeque<Value> = array.iter().cloned().collect();
249 WorkingArray {
250 array: deque,
251 snaps: VecDeque::new(),
252 }
253 }
254
255 // When we start speculatively matching array elements (e.g. in a Choice
256 // or Occur containing groups), we may fail the match partway through, and
257 // need to rewind to the most recent snapshot.
258 //
259 // It's possible for nested snapshots to exist; for example if we have a
260 // group-of-choices nested inside a group-of-choices.
261 //
262 // If one array is nested inside another, the inner array will get its
263 // own WorkingArray so snapshots aren't necessary in that case.
264 fn snapshot(&mut self) {
265 let new_snap: VecDeque<Value> = VecDeque::new();
266 self.snaps.push_back(new_snap);
267 }
268
269 // Restore the array to the point when we last called snapshot()
270 fn rewind(&mut self) {
271 // If validate code is implemented correctly, then unwrap() should
272 // never panic.
273 let mut top_snap = self.snaps.pop_back().unwrap();
274 // drain the elements in LIFO order, and push them back into
275 // the working array.
276 for element in top_snap.drain(..).rev() {
277 self.array.push_front(element);
278 }
279 }
280
281 // We completed a match, so we can retire the most recent snapshot.
282 fn commit(&mut self) {
283 // If validate code is implemented correctly, then unwrap() should
284 // never panic.
285 // This throws away the list that was popped; those values were
286 // successfully matched and are no longer needed.
287 self.snaps.pop_back().unwrap();
288 }
289
290 // Peek at the front of the working array.
291 fn peek_front(&self) -> Option<&Value> {
292 self.array.front()
293 }
294
295 // Remove an element from the working array.
296 // If there is an active snapshot, stash the element there until we're
297 // certain we've matched the entire group.
298 fn pop_front(&mut self) {
299 // If validate code is implemented correctly, then unwrap() should
300 // never panic (we've already peeked at this value in order to match
301 // it.)
302 let element = self.array.pop_front().unwrap();
303 // If there is a current snapshot, preserve this element
304 // for later rewind.
305 if let Some(snap) = self.snaps.back_mut() {
306 snap.push_back(element);
307 }
308 }
309}
310
311// Prevent warnings if both serde_cbor and serde_json are disabled.
312#[allow(dead_code)]
313
314// This is the main validation dispatch function.
315// It tries to match a Node and a Value, recursing as needed.
316fn validate(value: &Value, node: &Node, ctx: &Context) -> ValidateResult {
317 match node {
318 Node::Literal(l) => validate_literal(l, value),
319 Node::PreludeType(p) => validate_prelude_type(*p, value),
320 Node::Choice(c) => validate_choice(c, value, ctx),
321 Node::Map(m) => validate_map(m, value, ctx),
322 Node::Array(a) => validate_array(a, value, ctx),
323 Node::Rule(r) => validate_rule(r, value, ctx),
324 Node::Group(g) => validate_standalone_group(g, value, ctx),
325 Node::KeyValue(_) => Err(ValidateError::Structural("unexpected KeyValue".into())),
326 Node::Occur(_) => Err(ValidateError::Structural("unexpected Occur".into())),
327 Node::Unwrap(_) => Err(ValidateError::Structural("unexpected Unwrap".into())),
328 Node::Range(r) => validate_range(r, value, ctx),
329 Node::Control(ctl) => validate_control(ctl, value, ctx),
330 Node::Choiceify(r) => validate_choiceify(r, value, ctx),
331 Node::ChoiceifyInline(a) => validate_choiceify_inline(a, value, ctx),
332 }
333}
334
335// Perform map key search.
336// Some keys (literal values) can be found with a fast search, while
337// others may require a linear search.
338fn validate_map_key<'a>(
339 value_map: &'a mut WorkingMap,
340 node: &Node,
341 ctx: &Context,
342) -> TempResult<(Value, &'a Value)> {
343 match node {
344 Node::Literal(l) => map_search_literal(l, value_map),
345 _ => map_search(node, value_map, ctx),
346 }
347}
348
349/// Validate a `Choice` containing an arbitrary number of "option" nodes.
350///
351/// If any of the options matches, this validation is successful.
352fn validate_choice(choice: &Choice, value: &Value, ctx: &Context) -> ValidateResult {
353 for node in &choice.options {
354 match validate(value, node, ctx) {
355 Ok(()) => {
356 return Ok(());
357 }
358 Err(e) => {
359 // Only fail if the error is considered fatal.
360 // Otherwise, we'll keep trying other options.
361 if e.is_fatal() {
362 return Err(e);
363 }
364 }
365 }
366 }
367 let expected = format!("choice of {}", choice.options.len());
368 Err(mismatch(expected))
369}
370
371/// Validate a `Rule` reference
372///
373/// Seek out the right `Node` and `Context`, and recurse.
374fn validate_rule(rule: &Rule, value: &Value, ctx: &Context) -> ValidateResult {
375 let answer = ctx.lookup_rule(rule)?;
376 validate(value, answer.node, &answer.ctx)
377}
378
379/// Create a `Value` from a `Literal`.
380impl From<&Literal> for Value {
381 fn from(l: &Literal) -> Value {
382 match l {
383 Literal::Bool(b) => Value::Bool(*b),
384 Literal::Int(i) => Value::Integer(*i),
385 Literal::Float(f) => Value::from_float(*f),
386 Literal::Text(t) => Value::Text(t.clone()),
387 Literal::Bytes(b) => Value::Bytes(b.clone()),
388 }
389 }
390}
391
392fn validate_literal(literal: &Literal, value: &Value) -> ValidateResult {
393 if *value == Value::from(literal) {
394 return Ok(());
395 }
396 Err(mismatch(format!("{}", literal)))
397}
398
399// Find a specific key in the map and return that key plus a reference to its value.
400fn map_search_literal<'a>(
401 literal: &Literal,
402 working_map: &'a mut WorkingMap,
403) -> TempResult<(Value, &'a Value)> {
404 let search_key = Value::from(literal);
405 match working_map.peek_at(&search_key) {
406 Some(val) => Ok((search_key, val)),
407 None => {
408 // We didn't find the key; return an error
409 Err(mismatch(format!("map{{{}}}", literal)))
410 }
411 }
412}
413
414// Iterate over each key in the working map, looking for a match.
415// If we find a match, return a copy of the key, and a reference to the value.
416// This is less efficient than map_search_literal.
417fn map_search<'a>(
418 node: &Node,
419 working_map: &'a mut WorkingMap,
420 ctx: &Context,
421) -> TempResult<(Value, &'a Value)> {
422 for (key, value) in &working_map.map {
423 let attempt = validate(key, node, ctx);
424 if attempt.is_ok() {
425 return Ok((key.clone(), value));
426 }
427 }
428 // We searched all the keys without finding a match. Validation fails.
429 Err(mismatch(format!("map{{{}}}", node)))
430}
431
432// Note `ty` is passed by value because clippy says it's only 1 byte.
433fn validate_prelude_type(ty: PreludeType, value: &Value) -> ValidateResult {
434 match (ty, value) {
435 (PreludeType::Any, _) => Ok(()),
436 (PreludeType::Nil, Value::Null) => Ok(()),
437 (PreludeType::Nil, _) => Err(mismatch("nil")),
438 (PreludeType::Bool, Value::Bool(_)) => Ok(()),
439 (PreludeType::Bool, _) => Err(mismatch("bool")),
440 (PreludeType::Int, Value::Integer(_)) => Ok(()),
441 (PreludeType::Int, _) => Err(mismatch("int")),
442 (PreludeType::Uint, Value::Integer(x)) if *x >= 0 => Ok(()),
443 (PreludeType::Uint, _) => Err(mismatch("uint")),
444 (PreludeType::Nint, Value::Integer(x)) if *x < 0 => Ok(()),
445 (PreludeType::Nint, _) => Err(mismatch("nint")),
446 (PreludeType::Float, Value::Float(_)) => Ok(()),
447 (PreludeType::Float, _) => Err(mismatch("float")),
448 (PreludeType::Tstr, Value::Text(_)) => Ok(()),
449 (PreludeType::Tstr, _) => Err(mismatch("tstr")),
450 (PreludeType::Bstr, Value::Bytes(_)) => Ok(()),
451 (PreludeType::Bstr, _) => Err(mismatch("bstr")),
452 }
453}
454
455// FIXME: should this be combined with Map handling?
456fn validate_array(ar: &Array, value: &Value, ctx: &Context) -> ValidateResult {
457 match value {
458 Value::Array(a) => validate_array_part2(ar, a, ctx),
459 _ => Err(mismatch("array")),
460 }
461}
462
463fn validate_array_part2(ar: &Array, value_array: &[Value], ctx: &Context) -> ValidateResult {
464 // Strategy for validating an array:
465 // 1. We assume that the code that constructed the IVT Array placed the
466 // members in matching order (literals first, more general types at the
467 // end) so that we consume IVT Array members in order without worrying
468 // about non-deterministic results.
469 // 2. Make a mutable working copy of the Value::Array contents
470 // 3. Iterate over the IVT Array, searching the working copy for a
471 // matching key.
472 // 4. If a match is found, remove the value from our working copy.
473 // 6. If the IVT member can consume multiple values, repeat the search for
474 // this key.
475 // 7. If a match is not found and the member is optional (or we've already
476 // consumed an acceptable number of keys), continue to the next IVT
477 // member.
478 // 8. If the member is not found and we haven't consumed the expected
479 // number of values, return an error.
480
481 let mut working_array = WorkingArray::new(value_array);
482
483 for member in &ar.members {
484 validate_array_member(member, &mut working_array, ctx)?;
485 }
486 if working_array.array.is_empty() {
487 Ok(())
488 } else {
489 // If the working map isn't empty, that means we had some extra values
490 // that didn't match anything.
491 // FIXME: Should this be a unique error type?
492 Err(mismatch("shorter array"))
493 }
494}
495
496fn validate_array_member(
497 member: &Node,
498 working_array: &mut WorkingArray,
499 ctx: &Context,
500) -> ValidateResult {
501 match member {
502 // FIXME: does it make sense for this to destructure & dispatch
503 // each Node type here? Is there any way to make this generic?
504 Node::Occur(o) => validate_array_occur(o, working_array, ctx),
505 Node::KeyValue(kv) => {
506 // The key is ignored. Validate the value only.
507 // FIXME: should we try to use the key to provide a more
508 // useful error message?
509 validate_array_value(&kv.value, working_array, ctx)
510 }
511 Node::Rule(r) => {
512 // FIXME: This seems like a gross hack. We need to dereference
513 // the rule here, because if we drop to the bottom and call
514 // validate_array_value() then we lose our ability to "see
515 // through" KeyValue and Group nodes while remembering that we are
516 // in an array context (with a particular working copy).
517 // BUG: Choice nodes will have the same problem.
518
519 let answer = ctx.lookup_rule(r)?;
520 validate_array_member(answer.node, working_array, &answer.ctx)
521 }
522 Node::Unwrap(r) => {
523 // Like Rule, we are dereferencing the Rule by hand here so that
524 // we can "see through" to the underlying data without forgetting
525 // we were in an array context.
526 let answer = ctx.lookup_rule(r)?;
527 validate_array_unwrap(answer.node, working_array, &answer.ctx)
528 }
529 Node::Choice(c) => {
530 // We need to explore each of the possible choices.
531 // We can't use validate_array_value() because we'll lose our
532 // array context.
533 for option in &c.options {
534 match validate_array_member(option, working_array, ctx) {
535 Ok(()) => {
536 return Ok(());
537 }
538 Err(e) => {
539 // Only fail if the error is considered fatal.
540 // Otherwise, we'll keep trying other options.
541 if e.is_fatal() {
542 return Err(e);
543 }
544 }
545 }
546 }
547 // None of the choices worked.
548 let expected = format!("choice of {}", c.options.len());
549 Err(mismatch(expected))
550 }
551 Node::Group(g) => {
552 // As we call validate_array_member, we don't know how many items
553 // it might speculatively pop from the list. So we'll take a snapshot
554 // now and commit our changes if we match successfully (and roll them
555 // back if it fails).
556 working_array.snapshot();
557
558 // Recurse into each member of the group.
559 for group_member in &g.members {
560 match validate_array_member(group_member, working_array, ctx) {
561 Ok(_) => {
562 // So far so good...
563 }
564 Err(e) => {
565 // Since we failed to validate the entire group, rewind to our most
566 // recent snapshot. This may put values back into the array,
567 // so they can be matched by whatever we try next (or trigger
568 // an error if they aren't consumed by anything).
569 working_array.rewind();
570 return Err(e);
571 }
572 }
573 }
574 // All group members validated Ok.
575 working_array.commit();
576 Ok(())
577 }
578 m => validate_array_value(m, working_array, ctx),
579 }
580}
581
582fn validate_array_unwrap(
583 node: &Node,
584 working_array: &mut WorkingArray,
585 ctx: &Context,
586) -> ValidateResult {
587 // After traversing an unwrap from inside an array, the next node must be an
588 // Array node too.
589 match node {
590 Node::Array(a) => {
591 // Recurse into each member of the unwrapped array.
592 for member in &a.members {
593 validate_array_member(member, working_array, ctx)?;
594 }
595 // All array members validated Ok.
596 Ok(())
597 }
598 _ => Err(mismatch("unwrap array")),
599 }
600}
601
602/// Validate an occurrence against a mutable working array.
603// FIXME: this is pretty similar to validate_map_occur; maybe they can be combined?
604fn validate_array_occur(
605 occur: &Occur,
606 working_array: &mut WorkingArray,
607 ctx: &Context,
608) -> ValidateResult {
609 let (lower_limit, upper_limit) = occur.limits();
610 let mut count: usize = 0;
611
612 loop {
613 match validate_array_member(&occur.node, working_array, ctx) {
614 Ok(_) => (),
615 Err(e) => {
616 if e.is_mismatch() {
617 // Stop trying to match this occurrence.
618 break;
619 }
620 // The error is something serious (e.g. MissingRule or
621 // Unsupported). We should fail now and propagate that
622 // error upward.
623 return Err(e);
624 }
625 }
626 count += 1;
627 if count >= upper_limit {
628 // Stop matching; we've consumed the maximum number of this key.
629 break;
630 }
631 }
632 if count < lower_limit {
633 return Err(mismatch(format!("more array element [{}]", occur)));
634 }
635 Ok(())
636}
637
638/// Validate some node against a mutable working array.
639fn validate_array_value(
640 node: &Node,
641 working_array: &mut WorkingArray,
642 ctx: &Context,
643) -> ValidateResult {
644 match working_array.peek_front() {
645 Some(val) => {
646 validate(val, node, ctx)?;
647 // We had a successful match; remove the matched value.
648 working_array.pop_front();
649 Ok(())
650 }
651 None => Err(mismatch(format!("array element {}", node))),
652 }
653}
654
655fn validate_map(m: &Map, value: &Value, ctx: &Context) -> ValidateResult {
656 match value {
657 Value::Map(vm) => validate_map_part2(m, vm, ctx),
658 _ => Err(mismatch("map")),
659 }
660}
661
662fn validate_map_part2(m: &Map, value_map: &ValueMap, ctx: &Context) -> ValidateResult {
663 // Strategy for validating a map:
664 // 1. We assume that the code that constructed the IVT Map placed the keys
665 // in matching order (literals first, more general types at the end) so
666 // that we consume IVT Map keys in order without worrying about non-
667 // deterministic results.
668 // 2. Make a mutable working copy of the Value::Map contents
669 // 3. Iterate over the IVT Map, searching the Value::Map for a matching key.
670 // 4. If a match is found, remove the key-value pair from our working copy.
671 // 5. Validate the key's corresponding value.
672 // 6. If the key can consume multiple values, repeat the search for this key.
673 // 7. If the key is not found and the key is optional (or we've already consumed
674 // an acceptable number of keys), continue to the next key.
675 // 8. If the key is not found and we haven't consumed the expected number of
676 // keys, return an error.
677
678 let mut working_map = WorkingMap::new(value_map);
679
680 for member in &m.members {
681 validate_map_member(member, &mut working_map, ctx).map_err(|e| {
682 // If a MapCut error pops out here, change it to a Mismatch, so that
683 // it can't cause trouble in nested maps.
684 e.erase_mapcut()
685 })?;
686 }
687 if working_map.map.is_empty() {
688 Ok(())
689 } else {
690 // If the working map isn't empty, that means we had some extra values
691 // that didn't match anything.
692 Err(mismatch("shorter map"))
693 }
694}
695
696fn validate_map_member(
697 member: &Node,
698 working_map: &mut WorkingMap,
699 ctx: &Context,
700) -> ValidateResult {
701 match member {
702 // FIXME: does it make sense for this to destructure & dispatch
703 // each Node type here? Is there any way to make this generic?
704 Node::Occur(o) => validate_map_occur(o, working_map, ctx),
705 Node::KeyValue(kv) => validate_map_keyvalue(kv, working_map, ctx),
706 Node::Rule(r) => {
707 // We can't use the generic validate() here; we would forget that
708 // we were in a map context. We need to punch down a level into
709 // the rule and match again.
710 let answer = ctx.lookup_rule(r)?;
711 validate_map_member(answer.node, working_map, &answer.ctx)
712 }
713 Node::Unwrap(r) => {
714 // Like Rule, we are dereferencing the Rule by hand here so that
715 // we can "see through" to the underlying data without forgetting
716 // we were in a map context.
717 let answer = ctx.lookup_rule(r)?;
718 validate_map_unwrap(answer.node, working_map, &answer.ctx)
719 }
720 Node::Group(g) => {
721 // As we call validate_array_member, we don't know how many items
722 // it might speculatively pop from the list. So we'll take a
723 // snapshot now and commit our changes if we match successfully
724 // (and roll them back if it fails).
725 working_map.snapshot();
726
727 // Recurse into each member of the group.
728 for group_member in &g.members {
729 match validate_map_member(group_member, working_map, ctx) {
730 Ok(_) => {
731 // So far so good...
732 }
733 Err(e) => {
734 // Since we failed to validate the entire group,
735 // rewind to our most recent snapshot. This may put
736 // values back into the map, so they can be matched by
737 // whatever we try next (or trigger an error if they
738 // aren't consumed by anything).
739 working_map.rewind();
740
741 // Also forget any MapCut errors, so that a sibling
742 // group may succeed where we failed.
743 return Err(e.erase_mapcut());
744 }
745 }
746 }
747 // All group members validated Ok.
748 working_map.commit();
749 Ok(())
750 }
751 Node::Choice(c) => validate_map_choice(&c.options, working_map, ctx),
752 Node::Choiceify(r) => validate_map_choiceify(r, working_map, ctx),
753 Node::ChoiceifyInline(a) => validate_map_choice(&a.members, working_map, ctx),
754
755 // I don't think any of these are possible using CDDL grammar.
756 Node::Literal(_) => Err(ValidateError::Structural("literal map member".into())),
757 Node::PreludeType(_) => Err(ValidateError::Structural("prelude type map member".into())),
758 Node::Map(_) => Err(ValidateError::Structural("map as map member".into())),
759 Node::Array(_) => Err(ValidateError::Structural("array as map member".into())),
760 Node::Range(_) => Err(ValidateError::Structural("range as map member".into())),
761 Node::Control(_) => Err(ValidateError::Structural("control op as map member".into())),
762 }
763}
764
765fn validate_map_choice(
766 options: &[Node],
767 working_map: &mut WorkingMap,
768 ctx: &Context,
769) -> ValidateResult {
770 // We need to explore each of the possible choices.
771 for option in options {
772 match validate_map_member(option, working_map, ctx) {
773 Ok(()) => {
774 return Ok(());
775 }
776 Err(e) => {
777 // We can keep trying other options as long as the
778 // error is a Mismatch, not a MapCut or something
779 // fatal.
780 if !e.is_mismatch() {
781 return Err(e);
782 }
783 }
784 }
785 }
786 // None of the choices worked.
787 let expected = format!("choice of {}", options.len());
788 Err(mismatch(expected))
789}
790
791// TODO: this duplicates a lot of code from validate_choiceify_members. Merge them?
792fn validate_map_choiceify_members(
793 choices: &[Node],
794 working_map: &mut WorkingMap,
795 ctx: &Context,
796) -> ValidateResult {
797 // Iterate over the input nodes. We expect a list of KeyValue variants.
798 // For each KeyValue, extract its .value member and try to validate that.
799 // Because we are in a group context, referring to other groups by name is
800 // also allowed; we will transparently unwrap those (recursively).
801
802 for item in choices {
803 let validate_result = match item {
804 Node::KeyValue(kv) => {
805 // Even though we're in a map, the choicify operation
806 // throws away the keys on its immediate operand.
807 // validate against the value (which is presumably
808 // a group containing KeyValues)
809 validate_map_member(&kv.value, working_map, ctx)
810 }
811 Node::Rule(rule) => {
812 // A group may include another group by name.
813 validate_map_choiceify(rule, working_map, ctx)
814 }
815 _ => {
816 // The flatten code will simplify a key-less KeyValue
817 // to just a plain Node; handle that here.
818 validate_map_member(item, working_map, ctx)
819 }
820 };
821
822 // This part is the same as validate_map_choice().
823 // If we matched, return Ok immediately.
824 // If we didn't, keep searching (unless the error is not a mismatch).
825 match validate_result {
826 Ok(()) => {
827 return Ok(());
828 }
829 Err(e) => {
830 // We can keep trying other options as long as the
831 // error is a Mismatch, not a MapCut or something
832 // fatal.
833 if !e.is_mismatch() {
834 return Err(e);
835 }
836 }
837 }
838 }
839 let expected = format!("choiceified group of {}", choices.len());
840 Err(mismatch(expected))
841}
842
843/// Validate a "choice-ified group" (the CDDL "&" operator)
844///
845/// Each value in the named group will be used as a possible choice.
846fn validate_map_choiceify(
847 rule: &Rule,
848 working_map: &mut WorkingMap,
849 ctx: &Context,
850) -> ValidateResult {
851 let NodeContext { node, ctx } = ctx.lookup_rule(rule)?;
852 match node {
853 Node::Group(g) => validate_map_choiceify_members(&g.members, working_map, &ctx),
854 Node::Rule(r) => validate_map_choiceify(r, working_map, &ctx),
855 _ => Err(ValidateError::Structural(
856 "improper map choiceify target".into(),
857 )),
858 }
859}
860
861fn validate_map_unwrap(node: &Node, working_map: &mut WorkingMap, ctx: &Context) -> ValidateResult {
862 // After traversing an unwrap from inside a map, the next node must be a
863 // Map node too.
864 match node {
865 Node::Map(m) => {
866 // Recurse into each member of the unwrapped array.
867 for member in &m.members {
868 validate_map_member(member, working_map, ctx)?;
869 }
870 // All array members validated Ok.
871 Ok(())
872 }
873 _ => Err(mismatch("unwrap map")),
874 }
875}
876
877/// Validate an occurrence against a mutable working map.
878fn validate_map_occur(
879 occur: &Occur,
880 working_map: &mut WorkingMap,
881 ctx: &Context,
882) -> ValidateResult {
883 let (lower_limit, upper_limit) = occur.limits();
884 let mut count: usize = 0;
885
886 loop {
887 match validate_map_member(&occur.node, working_map, ctx) {
888 Ok(_) => (),
889 Err(e) => {
890 if e.is_mismatch() {
891 // Stop trying to match this occurrence.
892 break;
893 }
894 // Either we got a MapCut error, or it's something even more
895 // serious (e.g. MissingRule or Unsupported). We should fail
896 // now and propagate that error upward.
897 return Err(e);
898 }
899 }
900 count += 1;
901 if count >= upper_limit {
902 // Stop matching; we've consumed the maximum number of this key.
903 break;
904 }
905 }
906 if count < lower_limit {
907 // Read this format string as "{{" then "{}" then "}}"
908 // The first and last print a single brace; the value is in the
909 // middle, e.g "{foo}".
910 return Err(mismatch(format!("map{{{}}}]", occur)));
911 }
912 Ok(())
913}
914
915/// Validate a key-value pair against a mutable working map.
916fn validate_map_keyvalue(
917 kv: &KeyValue,
918 working_map: &mut WorkingMap,
919 ctx: &Context,
920) -> ValidateResult {
921 // CDDL syntax reminder:
922 // a => b ; non-cut
923 // a ^ => b ; cut
924 // a: b ; cut
925 //
926 // If we're using "cut" semantics, a partial match (key matches + value
927 // mismatch) should force validation failure for the entire map. We
928 // signal this to our caller with a MapCut error.
929 // If we're using "non-cut" semantics, a partial match will leave the
930 // key-value pair in place, in the hope it may match something else.
931
932 let key_node = &kv.key;
933 let val_node = &kv.value;
934 let cut = kv.cut;
935
936 // If we fail to validate a key, exit now with an error.
937 let (working_key, working_val) = validate_map_key(working_map, key_node, ctx)?;
938
939 // Match the value that was returned.
940 match validate(working_val, val_node, ctx) {
941 Ok(()) => {
942 working_map.remove(&working_key);
943 Ok(())
944 }
945 Err(e) => {
946 match (cut, e) {
947 (true, ValidateError::Mismatch(m)) => {
948 // If "cut" semantics are in force, then rewrite Mismatch errors.
949 // This allows special handling when nested inside Occur nodes.
950 Err(ValidateError::MapCut(m))
951 }
952 (_, x) => Err(x),
953 }
954 }
955 }
956}
957
958fn validate_standalone_group(g: &Group, value: &Value, ctx: &Context) -> ValidateResult {
959 // Since we're not in an array or map context, it's not clear how we should
960 // validate a group containing multiple elements. If we see one, return an
961 // error.
962 match g.members.len() {
963 1 => {
964 // Since our group has length 1, validate against that single element.
965 validate(value, &g.members[0], ctx)
966 }
967 _ => Err(ValidateError::Unsupported("standalone group".into())),
968 }
969}
970
971fn deref_range_rule(node: &Node, ctx: &Context) -> TempResult<Literal> {
972 match node {
973 Node::Literal(l) => Ok(l.clone()),
974 Node::Rule(r) => {
975 let answer = ctx.lookup_rule(r)?;
976 deref_range_rule(answer.node, &answer.ctx)
977 }
978 _ => Err(ValidateError::Structural(
979 "confusing type on range operator".into(),
980 )),
981 }
982}
983
984// Returns true if value is within range
985fn check_range<T: PartialOrd>(start: T, end: T, value: T, inclusive: bool) -> bool {
986 if value < start {
987 return false;
988 }
989 if inclusive {
990 value <= end
991 } else {
992 value < end
993 }
994}
995
996fn validate_range(range: &Range, value: &Value, ctx: &Context) -> ValidateResult {
997 // first dereference rules on start and end, if necessary.
998 let start = deref_range_rule(&range.start, ctx)?;
999 let end = deref_range_rule(&range.end, ctx)?;
1000
1001 match (&start, &end, &value) {
1002 (Literal::Int(i1), Literal::Int(i2), Value::Integer(v)) => {
1003 if check_range(i1, i2, v, range.inclusive) {
1004 Ok(())
1005 } else {
1006 Err(mismatch(format!("{}", range)))
1007 }
1008 }
1009 (Literal::Float(f1), Literal::Float(f2), Value::Float(v)) => {
1010 if check_range(f1, f2, &v.0, range.inclusive) {
1011 Ok(())
1012 } else {
1013 Err(mismatch(format!("{}", range)))
1014 }
1015 }
1016 _ => {
1017 if discriminant(&start) == discriminant(&end) {
1018 // The range types were the same, so this is just a mismatch.
1019 Err(mismatch(format!("{}", range)))
1020 } else {
1021 // The range types didn't agree; return an error that points the
1022 // finger at the CDDL instead.
1023 Err(ValidateError::Structural(
1024 "mismatched types on range operator".into(),
1025 ))
1026 }
1027 }
1028 }
1029}
1030
1031// Follow a chain of Rule references until we reach a non-Rule node.
1032fn chase_rules<'a, F, R>(node: &'a Node, ctx: &'a Context<'a>, f: F) -> TempResult<R>
1033where
1034 F: Fn(&Node) -> TempResult<R>,
1035 R: 'static,
1036{
1037 if let Node::Rule(rule) = node {
1038 let answer = ctx.lookup_rule(rule)?;
1039 chase_rules(answer.node, &answer.ctx, f)
1040 } else {
1041 f(node)
1042 }
1043}
1044
1045fn validate_control(ctl: &Control, value: &Value, ctx: &Context) -> ValidateResult {
1046 match ctl {
1047 Control::Size(ctl_size) => validate_control_size(ctl_size, value, ctx),
1048 Control::Regexp(re) => validate_control_regexp(re, value),
1049 Control::Cbor(ctl_cbor) => validate_control_cbor(ctl_cbor, value, ctx),
1050 }
1051}
1052
1053#[cfg(not(feature = "serde_cbor"))]
1054fn validate_control_cbor(_ctl_cbor: &CtlOpCbor, _value: &Value, _ctx: &Context) -> ValidateResult {
1055 Err(ValidateError::Unsupported(
1056 "'.cbor' control operator; enable serde_cbor feature to support.".into(),
1057 ))
1058}
1059
1060#[cfg(feature = "serde_cbor")]
1061fn validate_control_cbor(ctl_cbor: &CtlOpCbor, value: &Value, ctx: &Context) -> ValidateResult {
1062 use serde_cbor::Value as CBOR_Value;
1063 use std::convert::TryFrom;
1064
1065 match value {
1066 Value::Bytes(bytes) => {
1067 let cbor_value: CBOR_Value = serde_cbor::from_slice(bytes)
1068 .map_err(|e| ValidateError::ValueError(format!("{}", e)))?;
1069
1070 let nested_value = Value::try_from(cbor_value)?;
1071
1072 validate(&nested_value, ctl_cbor.node.as_ref(), ctx)
1073 }
1074 _ => Err::<(), ValidateError>(mismatch("Bytes")),
1075 }
1076}
1077
1078fn validate_control_size(ctl: &CtlOpSize, value: &Value, ctx: &Context) -> ValidateResult {
1079 // Follow the chain of rules references until we wind up with a non-Rule node.
1080 let size: u64 = chase_rules(&ctl.size, ctx, |size_node| {
1081 // Compute the size in bytes
1082 match size_node {
1083 Node::Literal(Literal::Int(i)) => (*i).try_into().map_err(|_| {
1084 // Note the parser doesn't handle >64 bit positive integers.
1085 // Under normal circumstances, the only way this can occur is
1086 // when the limit is negative.
1087 let msg = format!("bad .size limit {}", i);
1088 ValidateError::Structural(msg)
1089 }),
1090 _ => {
1091 // Under normal circumstances this error is unreachable
1092 // because the flatten code will only allow literal integer sizes.
1093 let msg = format!("bad .size argument type ({})", size_node);
1094 Err(ValidateError::Structural(msg))
1095 }
1096 }
1097 })?;
1098
1099 chase_rules(&ctl.target, ctx, |target_node| {
1100 // Ensure that the target node evaluates to some type that is
1101 // compatible with the .size operator, and then validate the size limit.
1102 match target_node {
1103 Node::PreludeType(PreludeType::Uint) => validate_size_uint(size, value),
1104 Node::PreludeType(PreludeType::Tstr) => validate_size_tstr(size, value),
1105 Node::PreludeType(PreludeType::Bstr) => validate_size_bstr(size, value),
1106 _ => {
1107 let msg = format!("bad .size target type ({})", target_node);
1108
1109 Err(ValidateError::Structural(msg))
1110 }
1111 }
1112 })
1113}
1114
1115/// Validate the control operator "regexp"
1116///
1117/// `regexp` applies a regular expression to a text string.
1118///
1119fn validate_control_regexp(re: &CtlOpRegexp, value: &Value) -> ValidateResult {
1120 match value {
1121 Value::Text(text) => {
1122 if re.re.is_match(text) {
1123 Ok(())
1124 } else {
1125 Err(mismatch("regex mismatch"))
1126 }
1127 }
1128 _ => Err(mismatch("tstr")),
1129 }
1130}
1131
1132fn validate_size_uint(size: u64, value: &Value) -> ValidateResult {
1133 match value {
1134 Value::Integer(x) => {
1135 if *x < 0 {
1136 Err(mismatch(".size on negative integer"))
1137 } else if size >= 16 {
1138 // If the size is 16 or larger, the i128 value will always fit.
1139 Ok(())
1140 } else {
1141 let mask = (1i128 << (size * 8)) - 1;
1142 if *x & !mask == 0 {
1143 Ok(())
1144 } else {
1145 Err(mismatch("uint over .size limit"))
1146 }
1147 }
1148 }
1149 _ => Err(mismatch("uint")),
1150 }
1151}
1152
1153// Check the size of a text string.
1154fn validate_size_tstr(size: u64, value: &Value) -> ValidateResult {
1155 match value {
1156 Value::Text(s) => {
1157 let size: usize = size.try_into().unwrap_or(usize::MAX);
1158 if s.len() > size {
1159 Err(mismatch("tstr over .size limit"))
1160 } else {
1161 Ok(())
1162 }
1163 }
1164 _ => Err(mismatch("tstr")),
1165 }
1166}
1167
1168// Check the size of a byte string.
1169fn validate_size_bstr(size: u64, value: &Value) -> ValidateResult {
1170 match value {
1171 Value::Bytes(b) => {
1172 let size: usize = size.try_into().unwrap_or(usize::MAX);
1173 if b.len() > size {
1174 Err(mismatch("bstr over .size limit"))
1175 } else {
1176 Ok(())
1177 }
1178 }
1179 _ => Err(mismatch("bstr")),
1180 }
1181}
1182
1183fn validate_choiceify_members(choices: &[Node], value: &Value, ctx: &Context) -> ValidateResult {
1184 // Iterate over the input nodes. We expect a list of KeyValue variants.
1185 // For each KeyValue, extract its .value member and try to validate that.
1186 // Because we are in a group context, referring to other groups by name is
1187 // also allowed; we will transparently unwrap those (recursively).
1188 for item in choices {
1189 let validate_result = match item {
1190 Node::KeyValue(kv) => validate(value, &kv.value, ctx),
1191 Node::Rule(rule) => {
1192 // A group may include another group by name. Handling this:
1193 // Dereference the rule. If it leads to a Group, then
1194 // recursively walk the nodes in that group. Otherwise,
1195 // return an error.
1196 validate_choiceify(rule, value, ctx)
1197 }
1198 _ => {
1199 // Reading the CDDL spec, you might expect this case to be
1200 // un-necessary. However, the flatten code always simplifies
1201 // a keyless KeyValue node into just a value. Which means
1202 // that any type might appear here.
1203 validate(value, item, ctx)
1204 }
1205 };
1206
1207 // This part is the same as validate_choice().
1208 // If we matched, return Ok immediately.
1209 // If we didn't, keep searching (unless the error is fatal).
1210 match validate_result {
1211 Ok(()) => {
1212 return Ok(());
1213 }
1214 Err(e) => {
1215 // Only fail if the error is considered fatal.
1216 // Otherwise, we'll keep trying other options.
1217 if e.is_fatal() {
1218 return Err(e);
1219 }
1220 }
1221 }
1222 }
1223 let expected = format!("choiceified group of {}", choices.len());
1224 Err(mismatch(expected))
1225}
1226
1227/// Validate a "choice-ified group" (the CDDL "&" operator)
1228///
1229/// Each value in the named group will be used as a possible choice.
1230fn validate_choiceify(rule: &Rule, value: &Value, ctx: &Context) -> ValidateResult {
1231 let NodeContext { node, ctx } = ctx.lookup_rule(rule)?;
1232 match node {
1233 Node::Group(g) => validate_choiceify_members(&g.members, value, &ctx),
1234 Node::Rule(r) => validate_choiceify(r, value, &ctx),
1235 _ => Err(ValidateError::Structural(
1236 "improper choiceify target".into(),
1237 )),
1238 }
1239}
1240
1241/// Validate an inline "choice-ified group" (the CDDL "&" operator)
1242///
1243/// Each value in the inline group will be used as a possible choice.
1244fn validate_choiceify_inline(array: &Array, value: &Value, ctx: &Context) -> ValidateResult {
1245 validate_choiceify_members(&array.members, value, ctx)
1246}