cddl/validator/
cbor.rs

1#![cfg(feature = "std")]
2#![cfg(feature = "cbor")]
3#![cfg(not(feature = "lsp"))]
4
5use super::*;
6use crate::{
7  ast::*,
8  token,
9  visitor::{self, *},
10};
11
12use core::convert::TryInto;
13use std::{
14  borrow::Cow,
15  collections::HashMap,
16  convert::TryFrom,
17  fmt::{self, Write},
18};
19
20use chrono::{TimeZone, Utc};
21use ciborium::value::Value;
22use serde_json;
23
24#[cfg(feature = "additional-controls")]
25use crate::validator::control::{
26  abnf_from_complex_controller, cat_operation, plus_operation, validate_abnf,
27};
28
29/// cbor validation Result
30pub type Result<T> = std::result::Result<(), Error<T>>;
31
32/// cbor validation error
33#[derive(Debug)]
34pub enum Error<T: std::fmt::Debug> {
35  /// Zero or more validation errors
36  Validation(Vec<ValidationError>),
37  /// cbor parsing error
38  CBORParsing(ciborium::de::Error<T>),
39  /// json parsing error. Used only for parsing regex controller strings
40  JSONParsing(serde_json::Error),
41  /// CDDL parsing error
42  CDDLParsing(String),
43  /// UTF8 parsing error,
44  UTF8Parsing(std::str::Utf8Error),
45  /// Base16 decoding error
46  Base16Decoding(base16::DecodeError),
47  /// Base64 decoding error
48  Base64Decoding(data_encoding::DecodeError),
49}
50
51impl<T: std::fmt::Debug> fmt::Display for Error<T> {
52  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53    match self {
54      Error::Validation(errors) => {
55        let mut error_str = String::new();
56        for e in errors.iter() {
57          let _ = writeln!(error_str, "{}", e);
58        }
59        write!(f, "{}", error_str)
60      }
61      Error::CBORParsing(error) => write!(f, "error parsing cbor: {}", error),
62      Error::JSONParsing(error) => write!(f, "error parsing json string: {}", error),
63      Error::CDDLParsing(error) => write!(f, "error parsing CDDL: {}", error),
64      Error::UTF8Parsing(error) => write!(f, "error parsing utf8: {}", error),
65      Error::Base16Decoding(error) => write!(f, "error decoding base16: {}", error),
66      Error::Base64Decoding(error) => write!(f, "error decoding base64: {}", error),
67    }
68  }
69}
70
71impl<T: std::fmt::Debug + 'static> std::error::Error for Error<T> {
72  fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
73    match self {
74      Error::CBORParsing(error) => Some(error),
75      _ => None,
76    }
77  }
78}
79
80/// cbor validation error
81#[derive(Clone, Debug)]
82pub struct ValidationError {
83  /// Error message
84  pub reason: String,
85  /// Location in CDDL where error occurred
86  pub cddl_location: String,
87  /// Location in CBOR where error occurred
88  pub cbor_location: String,
89  /// Whether or not the error is associated with multiple type choices
90  pub is_multi_type_choice: bool,
91  /// Whether or not the error is associated with multiple group choices
92  pub is_multi_group_choice: bool,
93  /// Whether or not the error is associated with a group to choice enumeration
94  pub is_group_to_choice_enum: bool,
95  /// Error is associated with a type/group name group entry
96  pub type_group_name_entry: Option<String>,
97}
98
99impl fmt::Display for ValidationError {
100  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101    let mut error_str = String::from("error validating");
102    if self.is_multi_group_choice {
103      error_str.push_str(" group choice");
104    }
105    if self.is_multi_type_choice {
106      error_str.push_str(" type choice");
107    }
108    if self.is_group_to_choice_enum {
109      error_str.push_str(" type choice in group to choice enumeration");
110    }
111    if let Some(entry) = &self.type_group_name_entry {
112      let _ = write!(error_str, " group entry associated with rule \"{}\"", entry);
113    }
114
115    write!(
116      f,
117      "{} at cbor location {}: {}",
118      error_str, self.cbor_location, self.reason
119    )
120  }
121}
122
123impl std::error::Error for ValidationError {
124  fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
125    None
126  }
127}
128
129impl<T: std::fmt::Debug> Error<T> {
130  fn from_validator(cv: &CBORValidator, reason: String) -> Self {
131    Error::Validation(vec![ValidationError {
132      cddl_location: cv.cddl_location.clone(),
133      cbor_location: cv.cbor_location.clone(),
134      reason,
135      is_multi_type_choice: cv.is_multi_type_choice,
136      is_group_to_choice_enum: cv.is_group_to_choice_enum,
137      type_group_name_entry: cv.type_group_name_entry.map(|e| e.to_string()),
138      is_multi_group_choice: cv.is_multi_group_choice,
139    }])
140  }
141}
142
143/// cbor validator type
144#[derive(Clone)]
145pub struct CBORValidator<'a> {
146  cddl: &'a CDDL<'a>,
147  cbor: Value,
148  errors: Vec<ValidationError>,
149  cddl_location: String,
150  cbor_location: String,
151  // Occurrence indicator detected in current state of AST evaluation
152  occurrence: Option<Occur>,
153  // Current group entry index detected in current state of AST evaluation
154  group_entry_idx: Option<usize>,
155  // cbor object value hoisted from previous state of AST evaluation
156  object_value: Option<Value>,
157  // Is member key detected in current state of AST evaluation
158  is_member_key: bool,
159  // Is a cut detected in current state of AST evaluation
160  is_cut_present: bool,
161  // Str value of cut detected in current state of AST evaluation
162  cut_value: Option<Type1<'a>>,
163  // Validate the generic rule given by str ident in current state of AST
164  // evaluation
165  eval_generic_rule: Option<&'a str>,
166  // Aggregation of generic rules
167  generic_rules: Vec<GenericRule<'a>>,
168  // Control operator token detected in current state of AST evaluation
169  ctrl: Option<token::ControlOperator>,
170  // Is a group to choice enumeration detected in current state of AST
171  // evaluation
172  is_group_to_choice_enum: bool,
173  // Are 2 or more type choices detected in current state of AST evaluation
174  is_multi_type_choice: bool,
175  // Are 2 or more group choices detected in current state of AST evaluation
176  is_multi_group_choice: bool,
177  // Type/group name entry detected in current state of AST evaluation. Used
178  // only for providing more verbose error messages
179  type_group_name_entry: Option<&'a str>,
180  // Whether or not to advance to the next group entry if member key validation
181  // fails as detected during the current state of AST evaluation
182  advance_to_next_entry: bool,
183  // Is validation checking for map quality
184  is_ctrl_map_equality: bool,
185  entry_counts: Option<Vec<EntryCount>>,
186  // Collect map entry keys that have already been validated
187  validated_keys: Option<Vec<Value>>,
188  // Collect map entry values that have yet to be validated
189  values_to_validate: Option<Vec<Value>>,
190  // Whether or not the validator is validating a map entry value
191  validating_value: bool,
192  // Collect valid array indices when entries are type choices
193  valid_array_items: Option<Vec<usize>>,
194  // Collect invalid array item errors where the key is the index of the invalid
195  // array item
196  array_errors: Option<HashMap<usize, Vec<ValidationError>>>,
197  is_colon_shortcut_present: bool,
198  is_root: bool,
199  is_multi_type_choice_type_rule_validating_array: bool,
200  // Track visited rules to prevent infinite recursion
201  visited_rules: std::collections::HashSet<String>,
202  #[cfg(not(target_arch = "wasm32"))]
203  #[cfg(feature = "additional-controls")]
204  enabled_features: Option<&'a [&'a str]>,
205  #[cfg(target_arch = "wasm32")]
206  #[cfg(feature = "additional-controls")]
207  enabled_features: Option<Box<[JsValue]>>,
208  #[cfg(feature = "additional-controls")]
209  has_feature_errors: bool,
210  #[cfg(feature = "additional-controls")]
211  disabled_features: Option<Vec<String>>,
212  range_upper: Option<usize>,
213}
214
215#[derive(Clone, Debug)]
216struct GenericRule<'a> {
217  name: &'a str,
218  params: Vec<&'a str>,
219  args: Vec<Type1<'a>>,
220}
221
222impl<'a> CBORValidator<'a> {
223  #[cfg(not(target_arch = "wasm32"))]
224  #[cfg(feature = "additional-controls")]
225  /// New cborValidation from CDDL AST and cbor value
226  pub fn new(cddl: &'a CDDL<'a>, cbor: Value, enabled_features: Option<&'a [&'a str]>) -> Self {
227    CBORValidator {
228      cddl,
229      cbor,
230      errors: Vec::default(),
231      cddl_location: String::new(),
232      cbor_location: String::new(),
233      occurrence: None,
234      group_entry_idx: None,
235      object_value: None,
236      is_member_key: false,
237      is_cut_present: false,
238      cut_value: None,
239      eval_generic_rule: None,
240      generic_rules: Vec::new(),
241      ctrl: None,
242      is_group_to_choice_enum: false,
243      is_multi_type_choice: false,
244      is_multi_group_choice: false,
245      type_group_name_entry: None,
246      advance_to_next_entry: false,
247      is_ctrl_map_equality: false,
248      entry_counts: None,
249      validated_keys: None,
250      values_to_validate: None,
251      validating_value: false,
252      valid_array_items: None,
253      array_errors: None,
254      is_colon_shortcut_present: false,
255      is_root: false,
256      is_multi_type_choice_type_rule_validating_array: false,
257      visited_rules: std::collections::HashSet::new(),
258      enabled_features,
259      has_feature_errors: false,
260      disabled_features: None,
261      range_upper: None,
262    }
263  }
264
265  #[cfg(not(target_arch = "wasm32"))]
266  #[cfg(not(feature = "additional-controls"))]
267  /// New cborValidation from CDDL AST and cbor value
268  pub fn new(cddl: &'a CDDL<'a>, cbor: Value) -> Self {
269    CBORValidator {
270      cddl,
271      cbor,
272      errors: Vec::default(),
273      cddl_location: String::new(),
274      cbor_location: String::new(),
275      occurrence: None,
276      group_entry_idx: None,
277      object_value: None,
278      is_member_key: false,
279      is_cut_present: false,
280      cut_value: None,
281      eval_generic_rule: None,
282      generic_rules: Vec::new(),
283      ctrl: None,
284      is_group_to_choice_enum: false,
285      is_multi_type_choice: false,
286      is_multi_group_choice: false,
287      type_group_name_entry: None,
288      advance_to_next_entry: false,
289      is_ctrl_map_equality: false,
290      entry_counts: None,
291      validated_keys: None,
292      values_to_validate: None,
293      validating_value: false,
294      valid_array_items: None,
295      array_errors: None,
296      is_colon_shortcut_present: false,
297      is_root: false,
298      is_multi_type_choice_type_rule_validating_array: false,
299      visited_rules: std::collections::HashSet::new(),
300      range_upper: None,
301    }
302  }
303
304  #[cfg(target_arch = "wasm32")]
305  #[cfg(feature = "additional-controls")]
306  /// New cborValidation from CDDL AST and cbor value
307  pub fn new(cddl: &'a CDDL<'a>, cbor: Value, enabled_features: Option<Box<[JsValue]>>) -> Self {
308    CBORValidator {
309      cddl,
310      cbor,
311      errors: Vec::default(),
312      cddl_location: String::new(),
313      cbor_location: String::new(),
314      occurrence: None,
315      group_entry_idx: None,
316      object_value: None,
317      is_member_key: false,
318      is_cut_present: false,
319      cut_value: None,
320      eval_generic_rule: None,
321      generic_rules: Vec::new(),
322      ctrl: None,
323      is_group_to_choice_enum: false,
324      is_multi_type_choice: false,
325      is_multi_group_choice: false,
326      type_group_name_entry: None,
327      advance_to_next_entry: false,
328      is_ctrl_map_equality: false,
329      entry_counts: None,
330      validated_keys: None,
331      values_to_validate: None,
332      validating_value: false,
333      valid_array_items: None,
334      array_errors: None,
335      is_colon_shortcut_present: false,
336      is_root: false,
337      is_multi_type_choice_type_rule_validating_array: false,
338      visited_rules: std::collections::HashSet::new(),
339      enabled_features,
340      has_feature_errors: false,
341      disabled_features: None,
342      range_upper: None,
343    }
344  }
345
346  #[cfg(target_arch = "wasm32")]
347  #[cfg(not(feature = "additional-controls"))]
348  /// New cborValidation from CDDL AST and cbor value
349  pub fn new(cddl: &'a CDDL<'a>, cbor: Value) -> Self {
350    CBORValidator {
351      cddl,
352      cbor,
353      errors: Vec::default(),
354      cddl_location: String::new(),
355      cbor_location: String::new(),
356      occurrence: None,
357      group_entry_idx: None,
358      object_value: None,
359      is_member_key: false,
360      is_cut_present: false,
361      cut_value: None,
362      eval_generic_rule: None,
363      generic_rules: Vec::new(),
364      ctrl: None,
365      is_group_to_choice_enum: false,
366      is_multi_type_choice: false,
367      is_multi_group_choice: false,
368      type_group_name_entry: None,
369      advance_to_next_entry: false,
370      is_ctrl_map_equality: false,
371      entry_counts: None,
372      validated_keys: None,
373      values_to_validate: None,
374      validating_value: false,
375      valid_array_items: None,
376      array_errors: None,
377      is_colon_shortcut_present: false,
378      is_root: false,
379      is_multi_type_choice_type_rule_validating_array: false,
380      visited_rules: std::collections::HashSet::new(),
381      range_upper: None,
382    }
383  }
384
385  /// Extract the underlying CBOR Value.
386  pub fn extract_cbor(self) -> Value {
387    self.cbor
388  }
389
390  /// Create a new CBORValidator with inherited recursion state
391  fn new_with_recursion_state(&self, cbor: Value) -> CBORValidator<'a> {
392    #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
393    let mut cv = CBORValidator::new(self.cddl, cbor, self.enabled_features.clone());
394    #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
395    let mut cv = CBORValidator::new(self.cddl, cbor, self.enabled_features);
396    #[cfg(not(feature = "additional-controls"))]
397    let mut cv = CBORValidator::new(self.cddl, cbor);
398
399    cv.generic_rules = self.generic_rules.clone();
400    cv.eval_generic_rule = self.eval_generic_rule;
401    cv.visited_rules = self.visited_rules.clone();
402    cv
403  }
404
405  fn validate_array_items<T: std::fmt::Debug + 'static>(
406    &mut self,
407    token: &ArrayItemToken,
408  ) -> visitor::Result<Error<T>>
409  where
410    cbor::Error<T>: From<cbor::Error<std::io::Error>>,
411  {
412    if let Value::Array(a) = &self.cbor {
413      // Member keys are annotation only in an array context
414      if self.is_member_key {
415        return Ok(());
416      }
417
418      match validate_array_occurrence(
419        self.occurrence.as_ref(),
420        self.entry_counts.as_ref().map(|ec| &ec[..]),
421        a,
422      ) {
423        Ok((iter_items, allow_empty_array)) => {
424          if iter_items {
425            for (idx, v) in a.iter().enumerate() {
426              if let Some(indices) = &self.valid_array_items {
427                if self.is_multi_type_choice && indices.contains(&idx) {
428                  continue;
429                }
430              }
431
432              #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
433              let mut cv = self.new_with_recursion_state(v.clone());
434              #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
435              let mut cv = self.new_with_recursion_state(v.clone());
436              #[cfg(not(feature = "additional-controls"))]
437              let mut cv = self.new_with_recursion_state(v.clone());
438
439              cv.generic_rules = self.generic_rules.clone();
440              cv.eval_generic_rule = self.eval_generic_rule;
441              cv.ctrl = self.ctrl;
442              cv.is_multi_type_choice = self.is_multi_type_choice;
443              let _ = write!(cv.cbor_location, "{}/{}", self.cbor_location, idx);
444
445              match token {
446                ArrayItemToken::Value(value) => cv.visit_value(value)?,
447                ArrayItemToken::Range(lower, upper, is_inclusive) => {
448                  cv.visit_range(lower, upper, *is_inclusive)?
449                }
450                // Special handling for nested arrays when using the Group variant
451                ArrayItemToken::Group(group) if v.is_array() => {
452                  // Special handling for nested arrays
453                  cv.visit_group(group)?;
454                }
455                ArrayItemToken::Group(group) => cv.visit_group(group)?,
456                ArrayItemToken::Identifier(ident) => cv.visit_identifier(ident)?,
457                ArrayItemToken::TaggedData(tagged_data) => cv.visit_type2(tagged_data)?,
458              }
459
460              if self.is_multi_type_choice && cv.errors.is_empty() {
461                if let Some(indices) = &mut self.valid_array_items {
462                  indices.push(idx);
463                } else {
464                  self.valid_array_items = Some(vec![idx]);
465                }
466                continue;
467              }
468
469              if let Some(errors) = &mut self.array_errors {
470                if let Some(error) = errors.get_mut(&idx) {
471                  error.append(&mut cv.errors);
472                } else {
473                  errors.insert(idx, cv.errors);
474                }
475              } else {
476                let mut errors = HashMap::new();
477                errors.insert(idx, cv.errors);
478                self.array_errors = Some(errors)
479              }
480            }
481          } else {
482            let idx = if !self.is_multi_type_choice {
483              self.group_entry_idx.take()
484            } else {
485              self.group_entry_idx
486            };
487
488            if let Some(idx) = idx {
489              if let Some(v) = a.get(idx) {
490                #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
491                let mut cv = self.new_with_recursion_state(v.clone());
492                #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
493                let mut cv = self.new_with_recursion_state(v.clone());
494                #[cfg(not(feature = "additional-controls"))]
495                let mut cv = self.new_with_recursion_state(v.clone());
496
497                cv.ctrl = self.ctrl;
498                cv.is_multi_type_choice = self.is_multi_type_choice;
499                let _ = write!(cv.cbor_location, "{}/{}", self.cbor_location, idx);
500
501                match token {
502                  ArrayItemToken::Value(value) => cv.visit_value(value)?,
503                  ArrayItemToken::Range(lower, upper, is_inclusive) => {
504                    cv.visit_range(lower, upper, *is_inclusive)?
505                  }
506                  // Special nested array handling when using the Group variant
507                  ArrayItemToken::Group(group) if v.is_array() => {
508                    // Special handling for nested arrays
509                    cv.visit_group(group)?;
510                  }
511                  ArrayItemToken::Group(group) => cv.visit_group(group)?,
512                  ArrayItemToken::Identifier(ident) => cv.visit_identifier(ident)?,
513                  ArrayItemToken::TaggedData(tagged_data) => cv.visit_type2(tagged_data)?,
514                }
515
516                self.errors.append(&mut cv.errors);
517              } else if !allow_empty_array {
518                self.add_error(token.error_msg(Some(idx)));
519              }
520            } else if !self.is_multi_type_choice {
521              self.add_error(format!("{}, got {:?}", token.error_msg(None), self.cbor));
522            }
523          }
524        }
525        Err(errors) => {
526          for e in errors.into_iter() {
527            self.add_error(e);
528          }
529        }
530      }
531    }
532
533    Ok(())
534  }
535}
536
537impl<'a, T: std::fmt::Debug + 'static> Validator<'a, '_, cbor::Error<T>> for CBORValidator<'a>
538where
539  cbor::Error<T>: From<cbor::Error<std::io::Error>>,
540{
541  fn validate(&mut self) -> std::result::Result<(), cbor::Error<T>> {
542    for r in self.cddl.rules.iter() {
543      // First type rule is root
544      if let Rule::Type { rule, .. } = r {
545        if rule.generic_params.is_none() {
546          self.is_root = true;
547          self.visit_type_rule(rule)?;
548          self.is_root = false;
549          break;
550        }
551      }
552    }
553
554    if !self.errors.is_empty() {
555      return Err(Error::Validation(self.errors.clone()));
556    }
557
558    Ok(())
559  }
560
561  fn add_error(&mut self, reason: String) {
562    self.errors.push(ValidationError {
563      reason,
564      cddl_location: self.cddl_location.clone(),
565      cbor_location: self.cbor_location.clone(),
566      is_multi_type_choice: self.is_multi_type_choice,
567      is_multi_group_choice: self.is_multi_group_choice,
568      is_group_to_choice_enum: self.is_group_to_choice_enum,
569      type_group_name_entry: self.type_group_name_entry.map(|e| e.to_string()),
570    });
571  }
572}
573
574impl<'a, T: std::fmt::Debug + 'static> Visitor<'a, '_, Error<T>> for CBORValidator<'a>
575where
576  cbor::Error<T>: From<cbor::Error<std::io::Error>>,
577{
578  fn visit_type_rule(&mut self, tr: &TypeRule<'a>) -> visitor::Result<Error<T>> {
579    if let Some(gp) = &tr.generic_params {
580      if let Some(gr) = self
581        .generic_rules
582        .iter_mut()
583        .find(|r| r.name == tr.name.ident)
584      {
585        gr.params = gp.params.iter().map(|p| p.param.ident).collect();
586      } else {
587        self.generic_rules.push(GenericRule {
588          name: tr.name.ident,
589          params: gp.params.iter().map(|p| p.param.ident).collect(),
590          args: Vec::new(),
591        });
592      }
593    }
594
595    let type_choice_alternates = type_choice_alternates_from_ident(self.cddl, &tr.name);
596    if !type_choice_alternates.is_empty() {
597      self.is_multi_type_choice = true;
598
599      if self.cbor.is_array() {
600        self.is_multi_type_choice_type_rule_validating_array = true;
601      }
602    }
603
604    let error_count = self.errors.len();
605
606    for t in type_choice_alternates {
607      let cur_errors = self.errors.len();
608      self.visit_type(t)?;
609      if self.errors.len() == cur_errors {
610        for _ in 0..self.errors.len() - error_count {
611          self.errors.pop();
612        }
613
614        return Ok(());
615      }
616    }
617
618    if tr.value.type_choices.len() > 1 && self.cbor.is_array() {
619      self.is_multi_type_choice_type_rule_validating_array = true;
620    }
621
622    self.visit_type(&tr.value)
623  }
624
625  fn visit_group_rule(&mut self, gr: &GroupRule<'a>) -> visitor::Result<Error<T>> {
626    if let Some(gp) = &gr.generic_params {
627      if let Some(gr) = self
628        .generic_rules
629        .iter_mut()
630        .find(|r| r.name == gr.name.ident)
631      {
632        gr.params = gp.params.iter().map(|p| p.param.ident).collect();
633      } else {
634        self.generic_rules.push(GenericRule {
635          name: gr.name.ident,
636          params: gp.params.iter().map(|p| p.param.ident).collect(),
637          args: Vec::new(),
638        });
639      }
640    }
641
642    let group_choice_alternates = group_choice_alternates_from_ident(self.cddl, &gr.name);
643    if !group_choice_alternates.is_empty() {
644      self.is_multi_group_choice = true;
645    }
646
647    let error_count = self.errors.len();
648    for ge in group_choice_alternates {
649      let cur_errors = self.errors.len();
650      self.visit_group_entry(ge)?;
651      if self.errors.len() == cur_errors {
652        for _ in 0..self.errors.len() - error_count {
653          self.errors.pop();
654        }
655
656        return Ok(());
657      }
658    }
659
660    self.visit_group_entry(&gr.entry)
661  }
662
663  fn visit_type(&mut self, t: &Type<'a>) -> visitor::Result<Error<T>> {
664    // Special case for nested array in literal position
665    if let Value::Array(outer_array) = &self.cbor {
666      if let Some(idx) = self.group_entry_idx {
667        // We're processing a specific array item
668        if let Some(item) = outer_array.get(idx) {
669          if item.is_array() {
670            // This is a nested array, check if we're supposed to validate against an array type
671            for tc in t.type_choices.iter() {
672              if let Type2::Array { .. } = &tc.type1.type2 {
673                #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
674                let mut cv =
675                  CBORValidator::new(self.cddl, item.clone(), self.enabled_features.clone());
676                #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
677                let mut cv = CBORValidator::new(self.cddl, item.clone(), self.enabled_features);
678                #[cfg(not(feature = "additional-controls"))]
679                let mut cv = CBORValidator::new(self.cddl, item.clone());
680
681                cv.generic_rules = self.generic_rules.clone();
682                cv.eval_generic_rule = self.eval_generic_rule;
683                cv.is_multi_type_choice = self.is_multi_type_choice;
684
685                let _ = write!(cv.cbor_location, "{}/{}", self.cbor_location, idx);
686
687                // Visit the type choice with the inner array value
688                cv.visit_type_choice(tc)?;
689
690                self.errors.append(&mut cv.errors);
691                return Ok(());
692              }
693            }
694          }
695        }
696      }
697    }
698
699    // Regular type processing
700    if t.type_choices.len() > 1 {
701      self.is_multi_type_choice = true;
702    }
703
704    let initial_error_count = self.errors.len();
705    let mut choice_validation_succeeded = false;
706
707    for type_choice in t.type_choices.iter() {
708      if matches!(self.cbor, Value::Array(_))
709        && !self.is_multi_type_choice_type_rule_validating_array
710      {
711        let error_count = self.errors.len();
712
713        self.visit_type_choice(type_choice)?;
714
715        #[cfg(feature = "additional-controls")]
716        if self.errors.len() == error_count
717          && !self.has_feature_errors
718          && self.disabled_features.is_none()
719        {
720          // Disregard invalid type choice validation errors if one of the
721          // choices validates successfully
722          let type_choice_error_count = self.errors.len() - initial_error_count;
723          if type_choice_error_count > 0 {
724            for _ in 0..type_choice_error_count {
725              self.errors.pop();
726            }
727          }
728          choice_validation_succeeded = true;
729        }
730
731        #[cfg(not(feature = "additional-controls"))]
732        if self.errors.len() == error_count {
733          // Disregard invalid type choice validation errors if one of the
734          // choices validates successfully
735          let type_choice_error_count = self.errors.len() - initial_error_count;
736          if type_choice_error_count > 0 {
737            for _ in 0..type_choice_error_count {
738              self.errors.pop();
739            }
740          }
741          choice_validation_succeeded = true;
742        }
743
744        continue;
745      }
746
747      // Create a copy of the validator to test this choice in isolation
748      let mut choice_validator = self.clone();
749      choice_validator.errors.clear();
750
751      choice_validator.visit_type_choice(type_choice)?;
752
753      // If this choice validates successfully (no errors), use it
754      if choice_validator.errors.is_empty() {
755        #[cfg(feature = "additional-controls")]
756        if !choice_validator.has_feature_errors || choice_validator.disabled_features.is_some() {
757          // Clear any accumulated errors and return success
758          let type_choice_error_count = self.errors.len() - initial_error_count;
759          if type_choice_error_count > 0 {
760            for _ in 0..type_choice_error_count {
761              self.errors.pop();
762            }
763          }
764          return Ok(());
765        }
766
767        #[cfg(not(feature = "additional-controls"))]
768        {
769          // Clear any accumulated errors and return success
770          let type_choice_error_count = self.errors.len() - initial_error_count;
771          if type_choice_error_count > 0 {
772            for _ in 0..type_choice_error_count {
773              self.errors.pop();
774            }
775          }
776          return Ok(());
777        }
778      } else {
779        // This choice failed, accumulate its errors
780        self.errors.extend(choice_validator.errors);
781      }
782    }
783
784    // If we got here and choice_validation_succeeded is true (for array case),
785    // then validation succeeded
786    if choice_validation_succeeded {
787      return Ok(());
788    }
789    Ok(())
790  }
791
792  fn visit_group(&mut self, g: &Group<'a>) -> visitor::Result<Error<T>> {
793    if g.group_choices.len() > 1 {
794      self.is_multi_group_choice = true;
795    }
796
797    // Map equality/inequality validation
798    if self.is_ctrl_map_equality {
799      if let Some(t) = &self.ctrl {
800        if let Value::Map(m) = &self.cbor {
801          let entry_counts = entry_counts_from_group(self.cddl, g);
802          let len = m.len();
803          if let ControlOperator::EQ | ControlOperator::NE = t {
804            if !validate_entry_count(&entry_counts, len) {
805              for ec in entry_counts.iter() {
806                if let Some(occur) = &ec.entry_occurrence {
807                  self.add_error(format!(
808                    "expected array with length per occurrence {}",
809                    occur,
810                  ));
811                } else {
812                  self.add_error(format!(
813                    "expected array with length {}, got {}",
814                    ec.count, len
815                  ));
816                }
817              }
818              return Ok(());
819            }
820          }
821        }
822      }
823    }
824
825    self.is_ctrl_map_equality = false;
826
827    let initial_error_count = self.errors.len();
828    for group_choice in g.group_choices.iter() {
829      let error_count = self.errors.len();
830      self.visit_group_choice(group_choice)?;
831      if self.errors.len() == error_count {
832        // Disregard invalid group choice validation errors if one of the
833        // choices validates successfully
834        let group_choice_error_count = self.errors.len() - initial_error_count;
835        if group_choice_error_count > 0 {
836          for _ in 0..group_choice_error_count {
837            self.errors.pop();
838          }
839        }
840
841        return Ok(());
842      }
843    }
844
845    Ok(())
846  }
847
848  fn visit_group_choice(&mut self, gc: &GroupChoice<'a>) -> visitor::Result<Error<T>> {
849    if self.is_group_to_choice_enum {
850      let initial_error_count = self.errors.len();
851      for tc in type_choices_from_group_choice(self.cddl, gc).iter() {
852        let error_count = self.errors.len();
853        self.visit_type_choice(tc)?;
854        if self.errors.len() == error_count {
855          let type_choice_error_count = self.errors.len() - initial_error_count;
856          if type_choice_error_count > 0 {
857            for _ in 0..type_choice_error_count {
858              self.errors.pop();
859            }
860          }
861          return Ok(());
862        }
863      }
864
865      return Ok(());
866    }
867
868    for (idx, ge) in gc.group_entries.iter().enumerate() {
869      self.group_entry_idx = Some(idx);
870
871      self.visit_group_entry(&ge.0)?;
872    }
873
874    Ok(())
875  }
876
877  fn visit_range(
878    &mut self,
879    lower: &Type2,
880    upper: &Type2,
881    is_inclusive: bool,
882  ) -> visitor::Result<Error<T>> {
883    if let Value::Array(_) = &self.cbor {
884      return self.validate_array_items(&ArrayItemToken::Range(lower, upper, is_inclusive));
885    }
886
887    match (lower, upper) {
888      (Type2::UintValue { value: l, .. }, Type2::UintValue { value: u, .. }) => {
889        match &self.cbor {
890          Value::Bytes(b) => {
891            let len = b.len();
892            if is_inclusive {
893              if len < *l || len > *u {
894                self.add_error(format!(
895                  "expected uint to be in range {} <= value <= {}, got Bytes({:?})",
896                  l, u, b
897                ));
898              }
899            } else if len < *l || len >= *u {
900              self.add_error(format!(
901                "expected uint to be in range {} <= value < {}, got Bytes({:?})",
902                l, u, b
903              ));
904            }
905          }
906          Value::Text(s) => match self.ctrl {
907            Some(ControlOperator::SIZE) => {
908              let len = s.len();
909              let s = s.clone();
910              if is_inclusive {
911                if s.len() < *l || s.len() > *u {
912                  self.add_error(format!(
913                    "expected \"{}\" string length to be in the range {} <= value <= {}, got {}",
914                    s, l, u, len
915                  ));
916                }
917
918                return Ok(());
919              } else if s.len() < *l || s.len() >= *u {
920                self.add_error(format!(
921                  "expected \"{}\" string length to be in the range {} <= value < {}, got {}",
922                  s, l, u, len
923                ));
924                return Ok(());
925              }
926            }
927            _ => {
928              self.add_error("string value cannot be validated against a range without the .size control operator".to_string());
929              return Ok(());
930            }
931          },
932          Value::Integer(i) => {
933            if is_inclusive {
934              if i128::from(*i) < *l as i128 || i128::from(*i) > *u as i128 {
935                self.add_error(format!(
936                  "expected integer to be in range {} <= value <= {}, got {:?}",
937                  l, u, i
938                ));
939              }
940            } else if i128::from(*i) < *l as i128 || i128::from(*i) >= *u as i128 {
941              self.add_error(format!(
942                "expected integer to be in range {} <= value < {}, got {:?}",
943                l, u, i
944              ));
945            }
946          }
947          _ => {
948            self.add_error(format!(
949              "expected value to be in range {} {} value {} {}, got {:?}",
950              l,
951              if is_inclusive { "<=" } else { "<" },
952              if is_inclusive { "<=" } else { "<" },
953              u,
954              self.cbor
955            ));
956          }
957        }
958      }
959      _ => {
960        self.add_error(format!(
961          "invalid cddl range. upper and lower values must be uint types. got {} and {}",
962          lower, upper
963        ));
964      }
965    }
966
967    Ok(())
968  }
969
970  fn visit_control_operator(
971    &mut self,
972    target: &Type2<'a>,
973    ctrl: ControlOperator,
974    controller: &Type2<'a>,
975  ) -> visitor::Result<Error<T>> {
976    if let Type2::Typename {
977      ident: target_ident,
978      ..
979    } = target
980    {
981      if let Type2::Typename {
982        ident: controller_ident,
983        ..
984      } = controller
985      {
986        if let Some(name) = self.eval_generic_rule {
987          if let Some(gr) = self
988            .generic_rules
989            .iter()
990            .find(|&gr| gr.name == name)
991            .cloned()
992          {
993            for (idx, gp) in gr.params.iter().enumerate() {
994              if let Some(arg) = gr.args.get(idx) {
995                if *gp == target_ident.ident {
996                  let t2 = Type2::from(arg.clone());
997
998                  if *gp == controller_ident.ident {
999                    return self.visit_control_operator(&t2, ctrl, &t2);
1000                  }
1001
1002                  return self.visit_control_operator(&arg.type2, ctrl, controller);
1003                }
1004              }
1005            }
1006          }
1007        }
1008      }
1009
1010      if let Some(name) = self.eval_generic_rule {
1011        if let Some(gr) = self
1012          .generic_rules
1013          .iter()
1014          .find(|&gr| gr.name == name)
1015          .cloned()
1016        {
1017          for (idx, gp) in gr.params.iter().enumerate() {
1018            if let Some(arg) = gr.args.get(idx) {
1019              if *gp == target_ident.ident {
1020                let t2 = Type2::from(arg.clone());
1021                return self.visit_control_operator(&t2, ctrl, controller);
1022              }
1023            }
1024          }
1025        }
1026      }
1027    }
1028
1029    match ctrl {
1030      ControlOperator::EQ => {
1031        match target {
1032          Type2::Typename { ident, .. } => {
1033            if is_ident_string_data_type(self.cddl, ident)
1034              || is_ident_numeric_data_type(self.cddl, ident)
1035            {
1036              return self.visit_type2(controller);
1037            }
1038          }
1039          Type2::Array { group, .. } => {
1040            if let Value::Array(_) = &self.cbor {
1041              self.entry_counts = Some(entry_counts_from_group(self.cddl, group));
1042              self.visit_type2(controller)?;
1043              self.entry_counts = None;
1044              return Ok(());
1045            }
1046          }
1047          Type2::Map { .. } => {
1048            if let Value::Map(_) = &self.cbor {
1049              self.ctrl = Some(ctrl);
1050              self.is_ctrl_map_equality = true;
1051              self.visit_type2(controller)?;
1052              self.ctrl = None;
1053              self.is_ctrl_map_equality = false;
1054              return Ok(());
1055            }
1056          }
1057          _ => self.add_error(format!(
1058            "target for .eq operator must be a string, numerical, array or map data type, got {}",
1059            target
1060          )),
1061        }
1062        Ok(())
1063      }
1064      ControlOperator::NE => {
1065        match target {
1066          Type2::Typename { ident, .. } => {
1067            if is_ident_string_data_type(self.cddl, ident)
1068              || is_ident_numeric_data_type(self.cddl, ident)
1069            {
1070              self.ctrl = Some(ctrl);
1071              self.visit_type2(controller)?;
1072              self.ctrl = None;
1073              return Ok(());
1074            }
1075          }
1076          Type2::Array { .. } => {
1077            if let Value::Array(_) = &self.cbor {
1078              self.ctrl = Some(ctrl);
1079              self.visit_type2(controller)?;
1080              self.ctrl = None;
1081              return Ok(());
1082            }
1083          }
1084          Type2::Map { .. } => {
1085            if let Value::Map(_) = &self.cbor {
1086              self.ctrl = Some(ctrl);
1087              self.is_ctrl_map_equality = true;
1088              self.visit_type2(controller)?;
1089              self.ctrl = None;
1090              self.is_ctrl_map_equality = false;
1091              return Ok(());
1092            }
1093          }
1094          _ => self.add_error(format!(
1095            "target for .ne operator must be a string, numerical, array or map data type, got {}",
1096            target
1097          )),
1098        }
1099        Ok(())
1100      }
1101      ControlOperator::LT | ControlOperator::GT | ControlOperator::GE | ControlOperator::LE => {
1102        match target {
1103          Type2::Typename { ident, .. } if is_ident_numeric_data_type(self.cddl, ident) => {
1104            self.ctrl = Some(ctrl);
1105            self.visit_type2(controller)?;
1106            self.ctrl = None;
1107            Ok(())
1108          }
1109          _ => {
1110            self.add_error(format!(
1111              "target for .lt, .gt, .ge or .le operator must be a numerical data type, got {}",
1112              target
1113            ));
1114            Ok(())
1115          }
1116        }
1117      }
1118      ControlOperator::SIZE => match target {
1119        Type2::Typename { ident, .. }
1120          if is_ident_string_data_type(self.cddl, ident)
1121            || is_ident_uint_data_type(self.cddl, ident)
1122            || is_ident_byte_string_data_type(self.cddl, ident) =>
1123        {
1124          self.ctrl = Some(ctrl);
1125          self.visit_type2(controller)?;
1126          self.ctrl = None;
1127          Ok(())
1128        }
1129        _ => {
1130          self.add_error(format!(
1131            "target for .size must a string or uint data type, got {}",
1132            target
1133          ));
1134          Ok(())
1135        }
1136      },
1137      ControlOperator::AND => {
1138        self.ctrl = Some(ctrl);
1139        self.visit_type2(target)?;
1140        self.visit_type2(controller)?;
1141        self.ctrl = None;
1142        Ok(())
1143      }
1144      ControlOperator::WITHIN => {
1145        self.ctrl = Some(ctrl);
1146        let error_count = self.errors.len();
1147        self.visit_type2(target)?;
1148        let no_errors = self.errors.len() == error_count;
1149        self.visit_type2(controller)?;
1150        if no_errors && self.errors.len() > error_count {
1151          for _ in 0..self.errors.len() - error_count {
1152            self.errors.pop();
1153          }
1154
1155          self.add_error(format!(
1156            "expected type {} .within type {}, got {:?}",
1157            target, controller, self.cbor,
1158          ));
1159        }
1160
1161        self.ctrl = None;
1162
1163        Ok(())
1164      }
1165      ControlOperator::DEFAULT => {
1166        self.ctrl = Some(ctrl);
1167        let error_count = self.errors.len();
1168        self.visit_type2(target)?;
1169        if self.errors.len() != error_count {
1170          #[cfg(feature = "ast-span")]
1171          if let Some(Occur::Optional { .. }) = self.occurrence.take() {
1172            self.add_error(format!(
1173              "expected default value {}, got {:?}",
1174              controller, self.cbor
1175            ));
1176          }
1177          #[cfg(not(feature = "ast-span"))]
1178          if let Some(Occur::Optional {}) = self.occurrence.take() {
1179            self.add_error(format!(
1180              "expected default value {}, got {:?}",
1181              controller, self.cbor
1182            ));
1183          }
1184        }
1185        self.ctrl = None;
1186        Ok(())
1187      }
1188      ControlOperator::REGEXP | ControlOperator::PCRE => {
1189        self.ctrl = Some(ctrl);
1190        match target {
1191          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1192            match self.cbor {
1193              Value::Text(_) | Value::Array(_) => self.visit_type2(controller)?,
1194              _ => self.add_error(format!(
1195                ".regexp/.pcre control can only be matched against CBOR string, got {:?}",
1196                self.cbor
1197              )),
1198            }
1199          }
1200          _ => self.add_error(format!(
1201            ".regexp/.pcre control can only be matched against string data type, got {}",
1202            target
1203          )),
1204        }
1205        self.ctrl = None;
1206
1207        Ok(())
1208      }
1209      ControlOperator::CBOR | ControlOperator::CBORSEQ => {
1210        self.ctrl = Some(ctrl);
1211        match target {
1212          Type2::Typename { ident, .. } if is_ident_byte_string_data_type(self.cddl, ident) => {
1213            match &self.cbor {
1214              Value::Bytes(b) => {
1215                // Handle direct byte string case
1216                let inner_value = ciborium::de::from_reader(&b[..]);
1217                match inner_value {
1218                  Ok(value) => {
1219                    #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
1220                    let mut cv =
1221                      CBORValidator::new(self.cddl, value, self.enabled_features.clone());
1222                    #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
1223                    let mut cv = CBORValidator::new(self.cddl, value, self.enabled_features);
1224                    #[cfg(not(feature = "additional-controls"))]
1225                    let mut cv = CBORValidator::new(self.cddl, value);
1226
1227                    cv.generic_rules = self.generic_rules.clone();
1228                    cv.eval_generic_rule = self.eval_generic_rule;
1229                    cv.cbor_location.push_str(&self.cbor_location);
1230
1231                    cv.visit_type2(controller)?;
1232
1233                    if !cv.errors.is_empty() {
1234                      self.errors.append(&mut cv.errors);
1235                    }
1236                  }
1237                  Err(e) => {
1238                    self.add_error(format!("error decoding embedded CBOR: {}", e));
1239                  }
1240                }
1241              }
1242              Value::Array(arr) => {
1243                // Handle array of byte strings case
1244                for (idx, item) in arr.iter().enumerate() {
1245                  if let Value::Bytes(b) = item {
1246                    let inner_value = ciborium::de::from_reader(&b[..]);
1247                    match inner_value {
1248                      Ok(value) => {
1249                        let current_location = self.cbor_location.clone();
1250                        #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
1251                        let mut cv =
1252                          CBORValidator::new(self.cddl, value, self.enabled_features.clone());
1253                        #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
1254                        let mut cv = CBORValidator::new(self.cddl, value, self.enabled_features);
1255                        #[cfg(not(feature = "additional-controls"))]
1256                        let mut cv = CBORValidator::new(self.cddl, value);
1257
1258                        cv.generic_rules = self.generic_rules.clone();
1259                        cv.eval_generic_rule = self.eval_generic_rule;
1260                        let _ = write!(cv.cbor_location, "{}/{}", self.cbor_location, idx);
1261
1262                        cv.visit_type2(controller)?;
1263
1264                        if !cv.errors.is_empty() {
1265                          self.errors.append(&mut cv.errors);
1266                        }
1267                        self.cbor_location = current_location;
1268                      }
1269                      Err(e) => {
1270                        let error_msg =
1271                          format!("error decoding embedded CBOR at index {}: {}", idx, e);
1272                        self.errors.push(ValidationError {
1273                          reason: error_msg,
1274                          cddl_location: self.cddl_location.clone(),
1275                          cbor_location: self.cbor_location.clone(),
1276                          is_multi_type_choice: self.is_multi_type_choice,
1277                          is_multi_group_choice: self.is_multi_group_choice,
1278                          is_group_to_choice_enum: self.is_group_to_choice_enum,
1279                          type_group_name_entry: self.type_group_name_entry.map(|e| e.to_string()),
1280                        });
1281                      }
1282                    }
1283                  } else {
1284                    let error_msg = format!(
1285                      "array item at index {} must be a byte string for .cbor control, got {:?}",
1286                      idx, item
1287                    );
1288                    self.errors.push(ValidationError {
1289                      reason: error_msg,
1290                      cddl_location: self.cddl_location.clone(),
1291                      cbor_location: self.cbor_location.clone(),
1292                      is_multi_type_choice: self.is_multi_type_choice,
1293                      is_multi_group_choice: self.is_multi_group_choice,
1294                      is_group_to_choice_enum: self.is_group_to_choice_enum,
1295                      type_group_name_entry: self.type_group_name_entry.map(|e| e.to_string()),
1296                    });
1297                  }
1298                }
1299              }
1300              _ => {
1301                self.add_error(format!(
1302                  ".cbor control can only be matched against a CBOR byte string or array of byte strings, got {:?}",
1303                  self.cbor
1304                ));
1305              }
1306            }
1307          }
1308          _ => self.add_error(format!(
1309            ".cbor control can only be matched against a byte string data type, got {}",
1310            target
1311          )),
1312        }
1313        self.ctrl = None;
1314
1315        Ok(())
1316      }
1317      ControlOperator::BITS => {
1318        self.ctrl = Some(ctrl);
1319        match target {
1320          Type2::Typename { ident, .. }
1321            if is_ident_byte_string_data_type(self.cddl, ident)
1322              || is_ident_uint_data_type(self.cddl, ident) =>
1323          {
1324            match &self.cbor {
1325              Value::Bytes(_) | Value::Array(_) => self.visit_type2(controller)?,
1326              Value::Integer(i) if i128::from(*i) >= 0i128 => self.visit_type2(controller)?,
1327              _ => self.add_error(format!(
1328                "{} control can only be matched against a CBOR byte string or uint, got {:?}",
1329                ctrl, self.cbor,
1330              )),
1331            }
1332          }
1333          _ => self.add_error(format!(
1334            ".bits control can only be matched against a byte string data type, got {}",
1335            target
1336          )),
1337        }
1338        self.ctrl = None;
1339
1340        Ok(())
1341      }
1342      #[cfg(feature = "additional-controls")]
1343      ControlOperator::CAT => {
1344        self.ctrl = Some(ctrl);
1345
1346        match cat_operation(self.cddl, target, controller, false) {
1347          Ok(values) => {
1348            let error_count = self.errors.len();
1349            for v in values.iter() {
1350              let cur_errors = self.errors.len();
1351
1352              self.visit_type2(v)?;
1353
1354              if self.errors.len() == cur_errors {
1355                for _ in 0..self.errors.len() - error_count {
1356                  self.errors.pop();
1357                }
1358
1359                break;
1360              }
1361            }
1362          }
1363          Err(e) => self.add_error(e),
1364        }
1365
1366        self.ctrl = None;
1367
1368        Ok(())
1369      }
1370      #[cfg(feature = "additional-controls")]
1371      ControlOperator::DET => {
1372        self.ctrl = Some(ctrl);
1373
1374        match cat_operation(self.cddl, target, controller, true) {
1375          Ok(values) => {
1376            let error_count = self.errors.len();
1377
1378            for v in values.iter() {
1379              let cur_errors = self.errors.len();
1380              self.visit_type2(v)?;
1381
1382              if self.errors.len() == cur_errors {
1383                for _ in 0..self.errors.len() - error_count {
1384                  self.errors.pop();
1385                }
1386
1387                break;
1388              }
1389            }
1390          }
1391          Err(e) => self.add_error(e),
1392        }
1393
1394        self.ctrl = None;
1395
1396        Ok(())
1397      }
1398      #[cfg(feature = "additional-controls")]
1399      ControlOperator::PLUS => {
1400        self.ctrl = Some(ctrl);
1401
1402        match plus_operation(self.cddl, target, controller) {
1403          Ok(values) => {
1404            let error_count = self.errors.len();
1405            for v in values.iter() {
1406              let cur_errors = self.errors.len();
1407
1408              self.visit_type2(v)?;
1409
1410              self.visit_type2(v)?;
1411              if self.errors.len() == cur_errors {
1412                for _ in 0..self.errors.len() - error_count {
1413                  self.errors.pop();
1414                }
1415
1416                break;
1417              }
1418            }
1419          }
1420
1421          Err(e) => self.add_error(e),
1422        }
1423
1424        self.ctrl = None;
1425
1426        Ok(())
1427      }
1428      #[cfg(feature = "additional-controls")]
1429      ControlOperator::ABNF => {
1430        self.ctrl = Some(ctrl);
1431
1432        match target {
1433          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1434            match self.cbor {
1435              Value::Text(_) | Value::Array(_) => {
1436                if let Type2::ParenthesizedType { pt, .. } = controller {
1437                  match abnf_from_complex_controller(self.cddl, pt) {
1438                    Ok(values) => {
1439                      let error_count = self.errors.len();
1440                      for v in values.iter() {
1441                        let cur_errors = self.errors.len();
1442
1443                        self.visit_type2(v)?;
1444
1445                        if self.errors.len() == cur_errors {
1446                          for _ in 0..self.errors.len() - error_count {
1447                            self.errors.pop();
1448                          }
1449
1450                          break;
1451                        }
1452                      }
1453                    }
1454                    Err(e) => self.add_error(e),
1455                  }
1456                } else {
1457                  self.visit_type2(controller)?
1458                }
1459              }
1460              _ => self.add_error(format!(
1461                ".abnf control can only be matched against a cbor string, got {:?}",
1462                self.cbor,
1463              )),
1464            }
1465          }
1466          _ => self.add_error(format!(
1467            ".abnf can only be matched against string data type, got {}",
1468            target,
1469          )),
1470        }
1471
1472        self.ctrl = None;
1473
1474        Ok(())
1475      }
1476      #[cfg(feature = "additional-controls")]
1477      ControlOperator::ABNFB => {
1478        self.ctrl = Some(ctrl);
1479
1480        match target {
1481          Type2::Typename { ident, .. } if is_ident_byte_string_data_type(self.cddl, ident) => {
1482            match self.cbor {
1483              Value::Bytes(_) | Value::Array(_) => {
1484                if let Type2::ParenthesizedType { pt, .. } = controller {
1485                  match abnf_from_complex_controller(self.cddl, pt) {
1486                    Ok(values) => {
1487                      let error_count = self.errors.len();
1488                      for v in values.iter() {
1489                        let cur_errors = self.errors.len();
1490
1491                        self.visit_type2(v)?;
1492
1493                        if self.errors.len() == cur_errors {
1494                          for _ in 0..self.errors.len() - error_count {
1495                            self.errors.pop();
1496                          }
1497
1498                          break;
1499                        }
1500                      }
1501                    }
1502                    Err(e) => self.add_error(e),
1503                  }
1504                } else {
1505                  self.visit_type2(controller)?
1506                }
1507              }
1508              _ => self.add_error(format!(
1509                ".abnfb control can only be matched against cbor bytes, got {:?}",
1510                self.cbor,
1511              )),
1512            }
1513          }
1514          _ => self.add_error(format!(
1515            ".abnfb can only be matched against byte string target data type, got {}",
1516            target,
1517          )),
1518        }
1519
1520        self.ctrl = None;
1521
1522        Ok(())
1523      }
1524      #[cfg(feature = "additional-controls")]
1525      #[cfg(not(target_arch = "wasm32"))]
1526      ControlOperator::FEATURE => {
1527        self.ctrl = Some(ctrl);
1528
1529        if let Some(ef) = self.enabled_features {
1530          let tv = text_value_from_type2(self.cddl, controller);
1531          if let Some(Type2::TextValue { value, .. }) = tv {
1532            if ef.contains(&&**value) {
1533              let err_count = self.errors.len();
1534              self.visit_type2(target)?;
1535              if self.errors.len() > err_count {
1536                self.has_feature_errors = true;
1537              }
1538              self.ctrl = None;
1539            } else {
1540              self
1541                .disabled_features
1542                .get_or_insert(vec![value.to_string()])
1543                .push(value.to_string());
1544            }
1545          } else if let Some(Type2::UTF8ByteString { value, .. }) = tv {
1546            let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?;
1547            if ef.contains(&value) {
1548              let err_count = self.errors.len();
1549              self.visit_type2(target)?;
1550              if self.errors.len() > err_count {
1551                self.has_feature_errors = true;
1552              }
1553              self.ctrl = None;
1554            } else {
1555              self
1556                .disabled_features
1557                .get_or_insert(vec![value.to_string()])
1558                .push(value.to_string());
1559            }
1560          }
1561        }
1562
1563        self.ctrl = None;
1564
1565        Ok(())
1566      }
1567      #[cfg(feature = "additional-controls")]
1568      #[cfg(target_arch = "wasm32")]
1569      ControlOperator::FEATURE => {
1570        self.ctrl = Some(ctrl);
1571
1572        if let Some(ef) = &self.enabled_features {
1573          let tv = text_value_from_type2(self.cddl, controller);
1574          if let Some(Type2::TextValue { value, .. }) = tv {
1575            if ef.contains(&JsValue::from(value.as_ref())) {
1576              let err_count = self.errors.len();
1577              self.visit_type2(target)?;
1578              if self.errors.len() > err_count {
1579                self.has_feature_errors = true;
1580              }
1581              self.ctrl = None;
1582            } else {
1583              self
1584                .disabled_features
1585                .get_or_insert(vec![value.to_string()])
1586                .push(value.to_string());
1587            }
1588          } else if let Some(Type2::UTF8ByteString { value, .. }) = tv {
1589            let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?;
1590            if ef.contains(&JsValue::from(value)) {
1591              let err_count = self.errors.len();
1592              self.visit_type2(target)?;
1593              if self.errors.len() > err_count {
1594                self.has_feature_errors = true;
1595              }
1596              self.ctrl = None;
1597            } else {
1598              self
1599                .disabled_features
1600                .get_or_insert(vec![value.to_string()])
1601                .push(value.to_string());
1602            }
1603          }
1604        }
1605
1606        self.ctrl = None;
1607
1608        Ok(())
1609      }
1610      #[cfg(feature = "additional-controls")]
1611      ControlOperator::B64U => {
1612        match target {
1613          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1614            match &self.cbor {
1615              Value::Text(s) => {
1616                match crate::validator::control::validate_b64u_text(target, controller, s, false) {
1617                  Ok(is_valid) => {
1618                    if !is_valid {
1619                      self.add_error(format!(
1620                        "text string \"{}\" does not match .b64u encoded bytes",
1621                        s
1622                      ));
1623                    }
1624                  }
1625                  Err(e) => self.add_error(e),
1626                }
1627              }
1628              _ => self.add_error(format!(
1629                ".b64u can only be matched against CBOR text, got {:?}",
1630                self.cbor
1631              )),
1632            }
1633          }
1634          _ => self.add_error(format!(
1635            ".b64u can only be matched against string data type, got {}",
1636            target
1637          )),
1638        }
1639
1640        Ok(())
1641      }
1642      #[cfg(feature = "additional-controls")]
1643      ControlOperator::B64C => {
1644        match target {
1645          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1646            match &self.cbor {
1647              Value::Text(s) => {
1648                match crate::validator::control::validate_b64c_text(target, controller, s, false) {
1649                  Ok(is_valid) => {
1650                    if !is_valid {
1651                      self.add_error(format!(
1652                        "text string \"{}\" does not match .b64c encoded bytes",
1653                        s
1654                      ));
1655                    }
1656                  }
1657                  Err(e) => self.add_error(e),
1658                }
1659              }
1660              _ => self.add_error(format!(
1661                ".b64c can only be matched against CBOR text, got {:?}",
1662                self.cbor
1663              )),
1664            }
1665          }
1666          _ => self.add_error(format!(
1667            ".b64c can only be matched against string data type, got {}",
1668            target
1669          )),
1670        }
1671
1672        Ok(())
1673      }
1674      #[cfg(feature = "additional-controls")]
1675      ControlOperator::B64USLOPPY => {
1676        match target {
1677          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1678            match &self.cbor {
1679              Value::Text(s) => {
1680                match crate::validator::control::validate_b64u_text(target, controller, s, true) {
1681                  Ok(is_valid) => {
1682                    if !is_valid {
1683                      self.add_error(format!(
1684                        "text string \"{}\" does not match .b64u-sloppy encoded bytes",
1685                        s
1686                      ));
1687                    }
1688                  }
1689                  Err(e) => self.add_error(e),
1690                }
1691              }
1692              _ => self.add_error(format!(
1693                ".b64u-sloppy can only be matched against CBOR text, got {:?}",
1694                self.cbor
1695              )),
1696            }
1697          }
1698          _ => self.add_error(format!(
1699            ".b64u-sloppy can only be matched against string data type, got {}",
1700            target
1701          )),
1702        }
1703
1704        Ok(())
1705      }
1706      #[cfg(feature = "additional-controls")]
1707      ControlOperator::B64CSLOPPY => {
1708        match target {
1709          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1710            match &self.cbor {
1711              Value::Text(s) => {
1712                match crate::validator::control::validate_b64c_text(target, controller, s, true) {
1713                  Ok(is_valid) => {
1714                    if !is_valid {
1715                      self.add_error(format!(
1716                        "text string \"{}\" does not match .b64c-sloppy encoded bytes",
1717                        s
1718                      ));
1719                    }
1720                  }
1721                  Err(e) => self.add_error(e),
1722                }
1723              }
1724              _ => self.add_error(format!(
1725                ".b64c-sloppy can only be matched against CBOR text, got {:?}",
1726                self.cbor
1727              )),
1728            }
1729          }
1730          _ => self.add_error(format!(
1731            ".b64c-sloppy can only be matched against string data type, got {}",
1732            target
1733          )),
1734        }
1735
1736        Ok(())
1737      }
1738      #[cfg(feature = "additional-controls")]
1739      ControlOperator::HEX => {
1740        match target {
1741          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1742            match &self.cbor {
1743              Value::Text(s) => {
1744                match crate::validator::control::validate_hex_text(
1745                  target,
1746                  controller,
1747                  s,
1748                  crate::validator::control::HexCase::Any,
1749                ) {
1750                  Ok(is_valid) => {
1751                    if !is_valid {
1752                      self.add_error(format!(
1753                        "text string \"{}\" does not match .hex encoded bytes",
1754                        s
1755                      ));
1756                    }
1757                  }
1758                  Err(e) => self.add_error(e),
1759                }
1760              }
1761              _ => self.add_error(format!(
1762                ".hex can only be matched against CBOR text, got {:?}",
1763                self.cbor
1764              )),
1765            }
1766          }
1767          _ => self.add_error(format!(
1768            ".hex can only be matched against string data type, got {}",
1769            target
1770          )),
1771        }
1772
1773        Ok(())
1774      }
1775      #[cfg(feature = "additional-controls")]
1776      ControlOperator::HEXLC => {
1777        match target {
1778          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1779            match &self.cbor {
1780              Value::Text(s) => {
1781                match crate::validator::control::validate_hex_text(
1782                  target,
1783                  controller,
1784                  s,
1785                  crate::validator::control::HexCase::Lower,
1786                ) {
1787                  Ok(is_valid) => {
1788                    if !is_valid {
1789                      self.add_error(format!(
1790                        "text string \"{}\" does not match .hexlc encoded bytes",
1791                        s
1792                      ));
1793                    }
1794                  }
1795                  Err(e) => self.add_error(e),
1796                }
1797              }
1798              _ => self.add_error(format!(
1799                ".hexlc can only be matched against CBOR text, got {:?}",
1800                self.cbor
1801              )),
1802            }
1803          }
1804          _ => self.add_error(format!(
1805            ".hexlc can only be matched against string data type, got {}",
1806            target
1807          )),
1808        }
1809
1810        Ok(())
1811      }
1812      #[cfg(feature = "additional-controls")]
1813      ControlOperator::HEXUC => {
1814        match target {
1815          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1816            match &self.cbor {
1817              Value::Text(s) => {
1818                match crate::validator::control::validate_hex_text(
1819                  target,
1820                  controller,
1821                  s,
1822                  crate::validator::control::HexCase::Upper,
1823                ) {
1824                  Ok(is_valid) => {
1825                    if !is_valid {
1826                      self.add_error(format!(
1827                        "text string \"{}\" does not match .hexuc encoded bytes",
1828                        s
1829                      ));
1830                    }
1831                  }
1832                  Err(e) => self.add_error(e),
1833                }
1834              }
1835              _ => self.add_error(format!(
1836                ".hexuc can only be matched against CBOR text, got {:?}",
1837                self.cbor
1838              )),
1839            }
1840          }
1841          _ => self.add_error(format!(
1842            ".hexuc can only be matched against string data type, got {}",
1843            target
1844          )),
1845        }
1846
1847        Ok(())
1848      }
1849      #[cfg(feature = "additional-controls")]
1850      ControlOperator::B32 => {
1851        match target {
1852          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1853            match &self.cbor {
1854              Value::Text(s) => {
1855                match crate::validator::control::validate_b32_text(target, controller, s, false) {
1856                  Ok(is_valid) => {
1857                    if !is_valid {
1858                      self.add_error(format!(
1859                        "text string \"{}\" does not match .b32 encoded bytes",
1860                        s
1861                      ));
1862                    }
1863                  }
1864                  Err(e) => self.add_error(e),
1865                }
1866              }
1867              _ => self.add_error(format!(
1868                ".b32 can only be matched against CBOR text, got {:?}",
1869                self.cbor
1870              )),
1871            }
1872          }
1873          _ => self.add_error(format!(
1874            ".b32 can only be matched against string data type, got {}",
1875            target
1876          )),
1877        }
1878
1879        Ok(())
1880      }
1881      #[cfg(feature = "additional-controls")]
1882      ControlOperator::H32 => {
1883        match target {
1884          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1885            match &self.cbor {
1886              Value::Text(s) => {
1887                match crate::validator::control::validate_b32_text(target, controller, s, true) {
1888                  Ok(is_valid) => {
1889                    if !is_valid {
1890                      self.add_error(format!(
1891                        "text string \"{}\" does not match .h32 encoded bytes",
1892                        s
1893                      ));
1894                    }
1895                  }
1896                  Err(e) => self.add_error(e),
1897                }
1898              }
1899              _ => self.add_error(format!(
1900                ".h32 can only be matched against CBOR text, got {:?}",
1901                self.cbor
1902              )),
1903            }
1904          }
1905          _ => self.add_error(format!(
1906            ".h32 can only be matched against string data type, got {}",
1907            target
1908          )),
1909        }
1910
1911        Ok(())
1912      }
1913      #[cfg(feature = "additional-controls")]
1914      ControlOperator::B45 => {
1915        match target {
1916          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1917            match &self.cbor {
1918              Value::Text(s) => {
1919                match crate::validator::control::validate_b45_text(target, controller, s) {
1920                  Ok(is_valid) => {
1921                    if !is_valid {
1922                      self.add_error(format!(
1923                        "text string \"{}\" does not match .b45 encoded bytes",
1924                        s
1925                      ));
1926                    }
1927                  }
1928                  Err(e) => self.add_error(e),
1929                }
1930              }
1931              _ => self.add_error(format!(
1932                ".b45 can only be matched against CBOR text, got {:?}",
1933                self.cbor
1934              )),
1935            }
1936          }
1937          _ => self.add_error(format!(
1938            ".b45 can only be matched against string data type, got {}",
1939            target
1940          )),
1941        }
1942
1943        Ok(())
1944      }
1945      #[cfg(feature = "additional-controls")]
1946      ControlOperator::BASE10 => {
1947        match target {
1948          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1949            match &self.cbor {
1950              Value::Text(s) => {
1951                match crate::validator::control::validate_base10_text(target, controller, s) {
1952                  Ok(is_valid) => {
1953                    if !is_valid {
1954                      self.add_error(format!(
1955                        "text string \"{}\" does not match .base10 integer format",
1956                        s
1957                      ));
1958                    }
1959                  }
1960                  Err(e) => self.add_error(e),
1961                }
1962              }
1963              _ => self.add_error(format!(
1964                ".base10 can only be matched against CBOR text, got {:?}",
1965                self.cbor
1966              )),
1967            }
1968          }
1969          _ => self.add_error(format!(
1970            ".base10 can only be matched against string data type, got {}",
1971            target
1972          )),
1973        }
1974
1975        Ok(())
1976      }
1977      #[cfg(feature = "additional-controls")]
1978      ControlOperator::PRINTF => {
1979        match target {
1980          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
1981            match &self.cbor {
1982              Value::Text(s) => {
1983                match crate::validator::control::validate_printf_text(target, controller, s) {
1984                  Ok(is_valid) => {
1985                    if !is_valid {
1986                      self.add_error(format!(
1987                        "text string \"{}\" does not match .printf format",
1988                        s
1989                      ));
1990                    }
1991                  }
1992                  Err(e) => self.add_error(e),
1993                }
1994              }
1995              _ => self.add_error(format!(
1996                ".printf can only be matched against CBOR text, got {:?}",
1997                self.cbor
1998              )),
1999            }
2000          }
2001          _ => self.add_error(format!(
2002            ".printf can only be matched against string data type, got {}",
2003            target
2004          )),
2005        }
2006
2007        Ok(())
2008      }
2009      #[cfg(feature = "additional-controls")]
2010      ControlOperator::JSON => {
2011        match target {
2012          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
2013            match &self.cbor {
2014              Value::Text(s) => {
2015                match crate::validator::control::validate_json_text(target, controller, s) {
2016                  Ok(is_valid) => {
2017                    if !is_valid {
2018                      self.add_error(format!("text string \"{}\" does not contain valid JSON", s));
2019                    }
2020                  }
2021                  Err(e) => self.add_error(e),
2022                }
2023              }
2024              _ => self.add_error(format!(
2025                ".json can only be matched against CBOR text, got {:?}",
2026                self.cbor
2027              )),
2028            }
2029          }
2030          _ => self.add_error(format!(
2031            ".json can only be matched against string data type, got {}",
2032            target
2033          )),
2034        }
2035
2036        Ok(())
2037      }
2038      #[cfg(feature = "additional-controls")]
2039      ControlOperator::JOIN => {
2040        match target {
2041          Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
2042            match &self.cbor {
2043              Value::Text(s) => {
2044                match crate::validator::control::validate_join_text(target, controller, s) {
2045                  Ok(is_valid) => {
2046                    if !is_valid {
2047                      self.add_error(format!("text string \"{}\" does not match .join result", s));
2048                    }
2049                  }
2050                  Err(e) => self.add_error(e),
2051                }
2052              }
2053              _ => self.add_error(format!(
2054                ".join can only be matched against CBOR text, got {:?}",
2055                self.cbor
2056              )),
2057            }
2058          }
2059          _ => self.add_error(format!(
2060            ".join can only be matched against string data type, got {}",
2061            target
2062          )),
2063        }
2064
2065        Ok(())
2066      }
2067    }
2068  }
2069
2070  fn visit_type2(&mut self, t2: &Type2<'a>) -> visitor::Result<Error<T>> {
2071    if matches!(self.ctrl, Some(ControlOperator::CBOR)) {
2072      if let Value::Bytes(b) = &self.cbor {
2073        let value = ciborium::de::from_reader(&b[..]);
2074        match value {
2075          Ok(value) => {
2076            let current_location = self.cbor_location.clone();
2077
2078            #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2079            let mut cv = CBORValidator::new(self.cddl, value, self.enabled_features.clone());
2080            #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2081            let mut cv = CBORValidator::new(self.cddl, value, self.enabled_features);
2082            #[cfg(not(feature = "additional-controls"))]
2083            let mut cv = CBORValidator::new(self.cddl, value);
2084
2085            cv.generic_rules = self.generic_rules.clone();
2086            cv.eval_generic_rule = self.eval_generic_rule;
2087            cv.is_multi_type_choice = self.is_multi_type_choice;
2088            cv.is_multi_group_choice = self.is_multi_group_choice;
2089            cv.cbor_location.push_str(&self.cbor_location);
2090            cv.type_group_name_entry = self.type_group_name_entry;
2091            cv.visit_type2(t2)?;
2092
2093            if cv.errors.is_empty() {
2094              self.cbor_location = current_location;
2095              return Ok(());
2096            }
2097
2098            self.errors.append(&mut cv.errors);
2099          }
2100          Err(e) => {
2101            self.add_error(format!("error decoding embedded CBOR, {}", e));
2102          }
2103        }
2104      }
2105
2106      return Ok(());
2107    } else if matches!(self.ctrl, Some(ControlOperator::CBORSEQ)) {
2108      if let Value::Bytes(b) = &self.cbor {
2109        let value = ciborium::de::from_reader(&b[..]);
2110        match value {
2111          Ok(Value::Array(_)) => {
2112            let current_location = self.cbor_location.clone();
2113
2114            #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2115            let mut cv = CBORValidator::new(
2116              self.cddl,
2117              value.unwrap_or(Value::Null),
2118              self.enabled_features.clone(),
2119            );
2120            #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2121            let mut cv = CBORValidator::new(
2122              self.cddl,
2123              value.unwrap_or(Value::Null),
2124              self.enabled_features,
2125            );
2126
2127            #[cfg(not(feature = "additional-controls"))]
2128            let mut cv = CBORValidator::new(self.cddl, value.unwrap_or(Value::Null));
2129
2130            cv.generic_rules = self.generic_rules.clone();
2131            cv.eval_generic_rule = self.eval_generic_rule;
2132            cv.is_multi_type_choice = self.is_multi_type_choice;
2133            cv.is_multi_group_choice = self.is_multi_group_choice;
2134            cv.cbor_location.push_str(&self.cbor_location);
2135            cv.type_group_name_entry = self.type_group_name_entry;
2136            cv.visit_type2(t2)?;
2137
2138            if cv.errors.is_empty() {
2139              self.cbor_location = current_location;
2140              return Ok(());
2141            }
2142
2143            self.errors.append(&mut cv.errors);
2144          }
2145          Err(e) => {
2146            self.add_error(format!("error decoding embedded CBOR, {}", e));
2147          }
2148          Ok(v) => self.add_error(format!(
2149            "embedded CBOR must be a CBOR sequence, got {:?}",
2150            v
2151          )),
2152        }
2153      }
2154
2155      return Ok(());
2156    }
2157
2158    match t2 {
2159      Type2::TextValue { value, .. } => self.visit_value(&token::Value::TEXT(value.clone())),
2160      Type2::Map { group, .. } => match &self.cbor {
2161        Value::Map(m) => {
2162          if self.is_member_key {
2163            let current_location = self.cbor_location.clone();
2164
2165            for (k, v) in m.iter() {
2166              #[cfg(feature = "additional-controls")]
2167              #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2168              let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features.clone());
2169              #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2170              let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features);
2171              #[cfg(not(feature = "additional-controls"))]
2172              let mut cv = CBORValidator::new(self.cddl, k.clone());
2173
2174              cv.generic_rules = self.generic_rules.clone();
2175              cv.eval_generic_rule = self.eval_generic_rule;
2176              cv.is_multi_type_choice = self.is_multi_type_choice;
2177              cv.is_multi_group_choice = self.is_multi_group_choice;
2178              cv.cbor_location.push_str(&self.cbor_location);
2179              cv.type_group_name_entry = self.type_group_name_entry;
2180              cv.visit_type2(t2)?;
2181
2182              if cv.errors.is_empty() {
2183                self.object_value = Some(v.clone());
2184                self
2185                  .validated_keys
2186                  .get_or_insert(vec![k.clone()])
2187                  .push(k.clone());
2188                self.cbor_location = current_location;
2189                return Ok(());
2190              }
2191
2192              self.errors.append(&mut cv.errors);
2193            }
2194
2195            return Ok(());
2196          }
2197
2198          // Check if this is an empty map schema with non-empty CBOR map
2199          if group.group_choices.len() == 1
2200            && group.group_choices[0].group_entries.is_empty()
2201            && !m.is_empty()
2202            && !matches!(
2203              self.ctrl,
2204              Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
2205            )
2206          {
2207            self.add_error(format!("expected empty map, got {:?}", self.cbor));
2208            return Ok(());
2209          }
2210
2211          #[allow(clippy::needless_collect)]
2212          let m = m.iter().map(|entry| entry.0.clone()).collect::<Vec<_>>();
2213
2214          self.visit_group(group)?;
2215
2216          // If extra map entries are detected, return validation error
2217          if self.values_to_validate.is_none() {
2218            for k in m.into_iter() {
2219              if let Some(keys) = &self.validated_keys {
2220                if !keys.contains(&k) {
2221                  self.add_error(format!("unexpected key {:?}", k));
2222                }
2223              }
2224            }
2225          }
2226
2227          self.is_cut_present = false;
2228          self.cut_value = None;
2229          Ok(())
2230        }
2231        Value::Array(_) => self.validate_array_items(&ArrayItemToken::Group(group)),
2232        _ => {
2233          self.add_error(format!("expected map object {}, got {:?}", t2, self.cbor));
2234          Ok(())
2235        }
2236      },
2237      Type2::Array { group, .. } => match &self.cbor {
2238        Value::Array(a) => {
2239          if group.group_choices.len() == 1
2240            && group.group_choices[0].group_entries.is_empty()
2241            && !a.is_empty()
2242            && !matches!(
2243              self.ctrl,
2244              Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
2245            )
2246          {
2247            self.add_error(format!("expected empty array, got {:?}", self.cbor));
2248            return Ok(());
2249          }
2250
2251          self.entry_counts = Some(entry_counts_from_group(self.cddl, group));
2252          self.visit_group(group)?;
2253          self.entry_counts = None;
2254
2255          if let Some(errors) = &mut self.array_errors {
2256            if let Some(indices) = &self.valid_array_items {
2257              for idx in indices.iter() {
2258                errors.remove(idx);
2259              }
2260            }
2261
2262            for error in errors.values_mut() {
2263              self.errors.append(error);
2264            }
2265          }
2266
2267          self.valid_array_items = None;
2268          self.array_errors = None;
2269
2270          Ok(())
2271        }
2272        Value::Map(m) if self.is_member_key => {
2273          let current_location = self.cbor_location.clone();
2274
2275          self.entry_counts = Some(entry_counts_from_group(self.cddl, group));
2276
2277          for (k, v) in m.iter() {
2278            #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2279            let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features.clone());
2280            #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2281            let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features);
2282            #[cfg(not(feature = "additional-controls"))]
2283            let mut cv = CBORValidator::new(self.cddl, k.clone());
2284
2285            cv.generic_rules = self.generic_rules.clone();
2286            cv.entry_counts = self.entry_counts.clone();
2287            cv.eval_generic_rule = self.eval_generic_rule;
2288            cv.is_multi_type_choice = self.is_multi_type_choice;
2289            cv.is_multi_group_choice = self.is_multi_group_choice;
2290            cv.cbor_location.push_str(&self.cbor_location);
2291            cv.type_group_name_entry = self.type_group_name_entry;
2292            cv.visit_type2(t2)?;
2293
2294            if cv.errors.is_empty() {
2295              self.object_value = Some(v.clone());
2296              self
2297                .validated_keys
2298                .get_or_insert(vec![k.clone()])
2299                .push(k.clone());
2300              self.cbor_location = current_location;
2301              return Ok(());
2302            }
2303
2304            self.errors.append(&mut cv.errors);
2305          }
2306
2307          self.entry_counts = None;
2308
2309          Ok(())
2310        }
2311        _ => {
2312          self.add_error(format!("expected array type, got {:?}", self.cbor));
2313          Ok(())
2314        }
2315      },
2316      Type2::ChoiceFromGroup {
2317        ident,
2318        generic_args,
2319        ..
2320      } => {
2321        if let Some(ga) = generic_args {
2322          if let Some(rule) = rule_from_ident(self.cddl, ident) {
2323            if let Some(gr) = self
2324              .generic_rules
2325              .iter_mut()
2326              .find(|gr| gr.name == ident.ident)
2327            {
2328              for arg in ga.args.iter() {
2329                gr.args.push((*arg.arg).clone());
2330              }
2331            } else if let Some(params) = generic_params_from_rule(rule) {
2332              self.generic_rules.push(GenericRule {
2333                name: ident.ident,
2334                params,
2335                args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
2336              });
2337            }
2338
2339            #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2340            let mut cv =
2341              CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
2342            #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2343            let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
2344            #[cfg(not(feature = "additional-controls"))]
2345            let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
2346
2347            cv.generic_rules = self.generic_rules.clone();
2348            cv.eval_generic_rule = Some(ident.ident);
2349            cv.is_group_to_choice_enum = true;
2350            cv.is_multi_type_choice = self.is_multi_type_choice;
2351            cv.visit_rule(rule)?;
2352
2353            self.errors.append(&mut cv.errors);
2354
2355            return Ok(());
2356          }
2357        }
2358
2359        if group_rule_from_ident(self.cddl, ident).is_none() {
2360          self.add_error(format!(
2361            "rule {} must be a group rule to turn it into a choice",
2362            ident
2363          ));
2364          return Ok(());
2365        }
2366
2367        self.is_group_to_choice_enum = true;
2368        self.visit_identifier(ident)?;
2369        self.is_group_to_choice_enum = false;
2370
2371        Ok(())
2372      }
2373      Type2::ChoiceFromInlineGroup { group, .. } => {
2374        self.is_group_to_choice_enum = true;
2375        self.visit_group(group)?;
2376        self.is_group_to_choice_enum = false;
2377        Ok(())
2378      }
2379      Type2::Typename {
2380        ident,
2381        generic_args,
2382        ..
2383      } => {
2384        if let Some(ga) = generic_args {
2385          if let Some(rule) = rule_from_ident(self.cddl, ident) {
2386            if let Some(gr) = self
2387              .generic_rules
2388              .iter_mut()
2389              .find(|gr| gr.name == ident.ident)
2390            {
2391              for arg in ga.args.iter() {
2392                gr.args.push((*arg.arg).clone());
2393              }
2394            } else if let Some(params) = generic_params_from_rule(rule) {
2395              self.generic_rules.push(GenericRule {
2396                name: ident.ident,
2397                params,
2398                args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
2399              });
2400            }
2401
2402            #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2403            let mut cv =
2404              CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
2405            #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2406            let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
2407            #[cfg(not(feature = "additional-controls"))]
2408            let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
2409
2410            cv.generic_rules = self.generic_rules.clone();
2411            cv.eval_generic_rule = Some(ident.ident);
2412            cv.is_multi_type_choice = self.is_multi_type_choice;
2413            cv.visit_rule(rule)?;
2414
2415            self.errors.append(&mut cv.errors);
2416
2417            return Ok(());
2418          }
2419        }
2420
2421        let type_choice_alternates = type_choice_alternates_from_ident(self.cddl, ident);
2422        if !type_choice_alternates.is_empty() {
2423          self.is_multi_type_choice = true;
2424        }
2425
2426        let error_count = self.errors.len();
2427        for t in type_choice_alternates {
2428          let cur_errors = self.errors.len();
2429          self.visit_type(t)?;
2430          if self.errors.len() == cur_errors {
2431            for _ in 0..self.errors.len() - error_count {
2432              self.errors.pop();
2433            }
2434
2435            return Ok(());
2436          }
2437        }
2438
2439        self.visit_identifier(ident)
2440      }
2441      Type2::IntValue { value, .. } => self.visit_value(&token::Value::INT(*value)),
2442      Type2::UintValue { value, .. } => self.visit_value(&token::Value::UINT(*value)),
2443      Type2::FloatValue { value, .. } => self.visit_value(&token::Value::FLOAT(*value)),
2444      Type2::UTF8ByteString { value, .. } => {
2445        self.visit_value(&token::Value::BYTE(ByteValue::UTF8(value.clone())))
2446      }
2447      Type2::B16ByteString { value, .. } => {
2448        self.visit_value(&token::Value::BYTE(ByteValue::B16(value.clone())))
2449      }
2450      Type2::ParenthesizedType { pt, .. } => self.visit_type(pt),
2451      Type2::Unwrap {
2452        ident,
2453        generic_args,
2454        ..
2455      } => {
2456        // Per
2457        // https://github.com/w3c/did-spec-registries/pull/138#issuecomment-719739215,
2458        // strip tag and validate underlying type
2459        if let Some(Type2::TaggedData { t, .. }) = tag_from_token(&lookup_ident(ident.ident)) {
2460          return self.visit_type(&t);
2461        }
2462
2463        if let Some(ga) = generic_args {
2464          if let Some(rule) = unwrap_rule_from_ident(self.cddl, ident) {
2465            if let Some(gr) = self
2466              .generic_rules
2467              .iter_mut()
2468              .find(|gr| gr.name == ident.ident)
2469            {
2470              for arg in ga.args.iter() {
2471                gr.args.push((*arg.arg).clone());
2472              }
2473            } else if let Some(params) = generic_params_from_rule(rule) {
2474              self.generic_rules.push(GenericRule {
2475                name: ident.ident,
2476                params,
2477                args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
2478              });
2479            }
2480
2481            #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2482            let mut cv =
2483              CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
2484            #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2485            let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
2486            #[cfg(not(feature = "additional-controls"))]
2487            let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
2488
2489            cv.generic_rules = self.generic_rules.clone();
2490            cv.eval_generic_rule = Some(ident.ident);
2491            cv.is_multi_type_choice = self.is_multi_type_choice;
2492            cv.visit_rule(rule)?;
2493
2494            self.errors.append(&mut cv.errors);
2495
2496            return Ok(());
2497          }
2498        }
2499
2500        if let Some(rule) = unwrap_rule_from_ident(self.cddl, ident) {
2501          return self.visit_rule(rule);
2502        }
2503
2504        self.add_error(format!(
2505          "cannot unwrap identifier {}, rule not found",
2506          ident
2507        ));
2508
2509        Ok(())
2510      }
2511      Type2::TaggedData { tag, t, .. } => match &self.cbor {
2512        Value::Tag(actual_tag, value) => {
2513          if let Some(tag_constraint) = tag {
2514            // For literal tag constraints, check the value matches
2515            if let Some(expected_tag) = tag_constraint.as_literal() {
2516              if expected_tag != *actual_tag {
2517                self.add_error(format!(
2518                  "expected tagged data #6.{}({}), got {:?}",
2519                  expected_tag, t, self.cbor
2520                ));
2521                return Ok(());
2522              }
2523            } else {
2524              // For type expression constraints, we would need to evaluate the type
2525              // For now, accept any tag value (this could be enhanced later)
2526            }
2527          } else if *actual_tag > 0 {
2528            self.add_error(format!(
2529              "expected tagged data #6({}), got {:?}",
2530              t, self.cbor
2531            ));
2532            return Ok(());
2533          }
2534
2535          #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
2536          let mut cv = CBORValidator::new(
2537            self.cddl,
2538            value.as_ref().clone(),
2539            self.enabled_features.clone(),
2540          );
2541          #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
2542          let mut cv = CBORValidator::new(self.cddl, value.as_ref().clone(), self.enabled_features);
2543          #[cfg(not(feature = "additional-controls"))]
2544          let mut cv = CBORValidator::new(self.cddl, value.as_ref().clone());
2545
2546          cv.generic_rules = self.generic_rules.clone();
2547          cv.eval_generic_rule = self.eval_generic_rule;
2548          cv.is_multi_type_choice = self.is_multi_type_choice;
2549          cv.is_multi_group_choice = self.is_multi_group_choice;
2550          cv.cbor_location.push_str(&self.cbor_location);
2551          cv.type_group_name_entry = self.type_group_name_entry;
2552          cv.visit_type(t)?;
2553
2554          self.errors.append(&mut cv.errors);
2555          Ok(())
2556        }
2557        Value::Array(_) => self.validate_array_items(&ArrayItemToken::TaggedData(t2)),
2558        _ => {
2559          if let Some(tag) = tag {
2560            self.add_error(format!(
2561              "expected tagged data #6.{}({}), got {:?}",
2562              tag, t, self.cbor
2563            ));
2564          } else {
2565            self.add_error(format!(
2566              "expected tagged data #6({}), got {:?}",
2567              t, self.cbor
2568            ));
2569          }
2570
2571          Ok(())
2572        }
2573      },
2574      Type2::DataMajorType { mt, constraint, .. } => match &self.cbor {
2575        Value::Integer(i) => {
2576          match mt {
2577            0u8 => match constraint {
2578              Some(c) => {
2579                if let Some(literal_val) = c.as_literal() {
2580                  if i128::from(*i) == literal_val as i128 && i128::from(*i) >= 0i128 {
2581                    return Ok(());
2582                  }
2583                }
2584                self.add_error(format!(
2585                  "expected uint data type with constraint {} (#{}.{}), got {:?}",
2586                  c, mt, c, self.cbor
2587                ));
2588                return Ok(());
2589              }
2590              _ => {
2591                if i128::from(*i).is_negative() {
2592                  self.add_error(format!(
2593                    "expected uint data type (#{}), got {:?}",
2594                    mt, self.cbor
2595                  ));
2596                  return Ok(());
2597                }
2598              }
2599            },
2600            1u8 => match constraint {
2601              Some(c) => {
2602                if let Some(literal_val) = c.as_literal() {
2603                  if i128::from(*i) == 0i128 - literal_val as i128 {
2604                    return Ok(());
2605                  }
2606                }
2607                self.add_error(format!(
2608                  "expected nint type with constraint {} (#{}.{}), got {:?}",
2609                  c, mt, c, self.cbor
2610                ));
2611                return Ok(());
2612              }
2613              _ => {
2614                if i128::from(*i) >= 0i128 {
2615                  self.add_error(format!(
2616                    "expected nint data type (#{}), got {:?}",
2617                    mt, self.cbor
2618                  ));
2619                  return Ok(());
2620                }
2621              }
2622            },
2623            _ => self.add_error(format!(
2624              "expected major type {} with constraint {:?}, got {:?}",
2625              mt, constraint, self.cbor
2626            )),
2627          }
2628
2629          Ok(())
2630        }
2631        Value::Bytes(b) => {
2632          match mt {
2633            2u8 => match constraint {
2634              Some(c) if c.is_literal(b.len() as u64) => return Ok(()),
2635              Some(c) => self.add_error(format!(
2636                "expected byte string type with constraint {} (#{}.{}), got {:?}",
2637                c, mt, c, self.cbor
2638              )),
2639              _ => return Ok(()),
2640            },
2641            _ => self.add_error(format!(
2642              "expected major type {} with constraint {:?}, got {:?}",
2643              mt, constraint, self.cbor
2644            )),
2645          }
2646
2647          Ok(())
2648        }
2649        Value::Text(t) => {
2650          match mt {
2651            3u8 => match constraint {
2652              Some(c) if c.is_literal(t.len() as u64) => return Ok(()),
2653              Some(c) => self.add_error(format!(
2654                "expected text string type with constraint {} (#{}.{}), got {:?}",
2655                c, mt, c, self.cbor
2656              )),
2657              _ => return Ok(()),
2658            },
2659            _ => self.add_error(format!(
2660              "expected major type {} with constraint {:?}, got {:?}",
2661              mt, constraint, self.cbor
2662            )),
2663          }
2664
2665          Ok(())
2666        }
2667        Value::Array(a) => {
2668          match mt {
2669            4u8 => match constraint {
2670              Some(c) if c.is_literal(a.len() as u64) => return Ok(()),
2671              Some(c) => self.add_error(format!(
2672                "expected array type with constraint {} (#{}.{}), got {:?}",
2673                c, mt, c, self.cbor
2674              )),
2675              _ => return Ok(()),
2676            },
2677            _ => self.add_error(format!(
2678              "expected major type {} with constraint {:?}, got {:?}",
2679              mt, constraint, self.cbor
2680            )),
2681          }
2682
2683          Ok(())
2684        }
2685        Value::Map(m) => {
2686          match mt {
2687            5u8 => match constraint {
2688              Some(c) if c.is_literal(m.len() as u64) => return Ok(()),
2689              Some(c) => self.add_error(format!(
2690                "expected map type with constraint {} (#{}.{}), got {:?}",
2691                c, mt, c, self.cbor
2692              )),
2693              _ => return Ok(()),
2694            },
2695            _ => self.add_error(format!(
2696              "expected major type {} with constraint {:?}, got {:?}",
2697              mt, constraint, self.cbor
2698            )),
2699          }
2700
2701          Ok(())
2702        }
2703        Value::Float(_f) => {
2704          match mt {
2705            7u8 => match constraint {
2706              Some(_c) => unimplemented!(),
2707              _ => return Ok(()),
2708            },
2709            _ => self.add_error(format!(
2710              "expected major type {} with constraint {:?}, got {:?}",
2711              mt, constraint, self.cbor
2712            )),
2713          }
2714
2715          Ok(())
2716        }
2717        _ => {
2718          if let Some(constraint) = constraint {
2719            self.add_error(format!(
2720              "expected major type #{}.{}, got {:?}",
2721              mt, constraint, self.cbor
2722            ));
2723          } else {
2724            self.add_error(format!("expected major type #{}, got {:?}", mt, self.cbor));
2725          }
2726
2727          Ok(())
2728        }
2729      },
2730      #[cfg(feature = "ast-span")]
2731      Type2::Any { .. } => Ok(()),
2732      #[cfg(not(feature = "ast-span"))]
2733      Type2::Any {} => Ok(()),
2734      _ => {
2735        self.add_error(format!(
2736          "unsupported data type for validating cbor, got {}",
2737          t2
2738        ));
2739        Ok(())
2740      }
2741    }
2742  }
2743
2744  fn visit_identifier(&mut self, ident: &Identifier<'a>) -> visitor::Result<Error<T>> {
2745    if let Some(name) = self.eval_generic_rule {
2746      if let Some(gr) = self
2747        .generic_rules
2748        .iter()
2749        .find(|&gr| gr.name == name)
2750        .cloned()
2751      {
2752        for (idx, gp) in gr.params.iter().enumerate() {
2753          if *gp == ident.ident {
2754            if let Some(arg) = gr.args.get(idx) {
2755              return self.visit_type1(arg);
2756            }
2757          }
2758        }
2759      }
2760    }
2761
2762    // self.is_colon_shortcut_present is only true when the ident is part of a
2763    // member key
2764    if !self.is_colon_shortcut_present {
2765      if let Some(r) = rule_from_ident(self.cddl, ident) {
2766        // Check for recursion to prevent stack overflow
2767        let rule_key = ident.ident.to_string();
2768        if self.visited_rules.contains(&rule_key) {
2769          // We've already validated this rule in the current validation path
2770          // This is a recursive reference, so we allow it and assume it's valid
2771          return Ok(());
2772        }
2773
2774        // Mark this rule as visited before recursing
2775        self.visited_rules.insert(rule_key.clone());
2776        let result = self.visit_rule(r);
2777        // Remove the rule from visited set after processing
2778        self.visited_rules.remove(&rule_key);
2779
2780        return result;
2781      }
2782    }
2783
2784    if is_ident_any_type(self.cddl, ident) {
2785      return Ok(());
2786    }
2787
2788    // Special case for array values - check if we're in an array context and this
2789    // is a reference to another array type
2790    if let Value::Array(_) = &self.cbor {
2791      if let Some(Rule::Type { rule, .. }) = rule_from_ident(self.cddl, ident) {
2792        for tc in rule.value.type_choices.iter() {
2793          if let Type2::Array { .. } = &tc.type1.type2 {
2794            return self.visit_type_choice(tc);
2795          }
2796        }
2797      }
2798    }
2799
2800    match &self.cbor {
2801      Value::Null if is_ident_null_data_type(self.cddl, ident) => Ok(()),
2802      Value::Bytes(_) if is_ident_byte_string_data_type(self.cddl, ident) => Ok(()),
2803      Value::Bool(b) => {
2804        if is_ident_bool_data_type(self.cddl, ident) {
2805          return Ok(());
2806        }
2807
2808        if ident_matches_bool_value(self.cddl, ident, *b) {
2809          return Ok(());
2810        }
2811
2812        self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2813        Ok(())
2814      }
2815      Value::Integer(i) => {
2816        if is_ident_uint_data_type(self.cddl, ident) {
2817          if i128::from(*i).is_negative() {
2818            self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2819          }
2820
2821          Ok(())
2822        } else if is_ident_integer_data_type(self.cddl, ident) {
2823          Ok(())
2824        } else if is_ident_time_data_type(self.cddl, ident) {
2825          if let chrono::LocalResult::None =
2826            Utc.timestamp_millis_opt((i128::from(*i) * 1000) as i64)
2827          {
2828            let i = *i;
2829            self.add_error(format!(
2830              "expected time data type, invalid UNIX timestamp {:?}",
2831              i,
2832            ));
2833          }
2834
2835          Ok(())
2836        } else {
2837          self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2838          Ok(())
2839        }
2840      }
2841      Value::Float(f) => {
2842        if is_ident_float_data_type(self.cddl, ident) {
2843          Ok(())
2844        } else if is_ident_time_data_type(self.cddl, ident) {
2845          if let chrono::LocalResult::None = Utc.timestamp_millis_opt((*f * 1000f64) as i64) {
2846            let f = *f;
2847            self.add_error(format!(
2848              "expected time data type, invalid UNIX timestamp {:?}",
2849              f,
2850            ));
2851          }
2852
2853          Ok(())
2854        } else {
2855          self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2856          Ok(())
2857        }
2858      }
2859      Value::Text(s) => {
2860        if is_ident_uri_data_type(self.cddl, ident) {
2861          if let Err(e) = uriparse::URI::try_from(&**s) {
2862            self.add_error(format!("expected URI data type, decoding error: {}", e));
2863          }
2864        } else if is_ident_b64url_data_type(self.cddl, ident) {
2865          if let Err(e) = base64_url::decode(s) {
2866            self.add_error(format!(
2867              "expected base64 URL data type, decoding error: {}",
2868              e
2869            ));
2870          }
2871        } else if is_ident_tdate_data_type(self.cddl, ident) {
2872          if let Err(e) = chrono::DateTime::parse_from_rfc3339(s) {
2873            self.add_error(format!("expected tdate data type, decoding error: {}", e));
2874          }
2875        } else if is_ident_string_data_type(self.cddl, ident) {
2876          return Ok(());
2877        } else {
2878          self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2879        }
2880
2881        Ok(())
2882      }
2883      Value::Tag(tag, value) => {
2884        match *tag {
2885          0 => {
2886            if is_ident_tdate_data_type(self.cddl, ident) {
2887              if let Value::Text(value) = value.as_ref() {
2888                if let Err(e) = chrono::DateTime::parse_from_rfc3339(value) {
2889                  self.add_error(format!("expected tdate data type, decoding error: {}", e));
2890                }
2891              } else {
2892                self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2893              }
2894            } else {
2895              self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2896            }
2897          }
2898          1 => {
2899            if is_ident_time_data_type(self.cddl, ident) {
2900              if let Value::Integer(value) = *value.as_ref() {
2901                let dt = Utc.timestamp_opt(value.try_into().unwrap(), 0);
2902                if let chrono::LocalResult::None = dt {
2903                  self.add_error(format!(
2904                    "expected time data type, invalid UNIX timestamp {:?}",
2905                    self.cbor
2906                  ));
2907                }
2908              } else if let Value::Float(value) = value.as_ref() {
2909                let seconds = value.trunc() as i64;
2910                let nanoseconds = (value.fract() * 1e9) as u32;
2911                let dt = Utc.timestamp_opt(seconds, nanoseconds);
2912                if let chrono::LocalResult::None = dt {
2913                  self.add_error(format!(
2914                    "expected time data type, invalid UNIX timestamp {:?}",
2915                    self.cbor
2916                  ));
2917                }
2918              } else {
2919                self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2920              }
2921            } else {
2922              self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
2923            }
2924          }
2925          _ => (),
2926        }
2927
2928        Ok(())
2929      }
2930      Value::Array(_) => self.validate_array_items(&ArrayItemToken::Identifier(ident)),
2931      Value::Map(m) => {
2932        match &self.occurrence {
2933          #[cfg(feature = "ast-span")]
2934          Some(Occur::Optional { .. }) | None => {
2935            if is_ident_string_data_type(self.cddl, ident) && !self.validating_value {
2936              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Text(_))) {
2937                self
2938                  .validated_keys
2939                  .get_or_insert(vec![k.clone()])
2940                  .push(k.clone());
2941                self.object_value = Some(v.clone());
2942                let _ = write!(self.cbor_location, "/{:?}", v);
2943              } else {
2944                self.add_error(format!("map requires entry key of type {}", ident));
2945              }
2946              return Ok(());
2947            }
2948
2949            if is_ident_integer_data_type(self.cddl, ident) && !self.validating_value {
2950              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Integer(_))) {
2951                self
2952                  .validated_keys
2953                  .get_or_insert(vec![k.clone()])
2954                  .push(k.clone());
2955                self.object_value = Some(v.clone());
2956                let _ = write!(self.cbor_location, "/{:?}", v);
2957              } else {
2958                self.add_error(format!("map requires entry key of type {}", ident));
2959              }
2960              return Ok(());
2961            }
2962
2963            if is_ident_bool_data_type(self.cddl, ident) && !self.validating_value {
2964              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bool(_))) {
2965                self
2966                  .validated_keys
2967                  .get_or_insert(vec![k.clone()])
2968                  .push(k.clone());
2969                self.object_value = Some(v.clone());
2970                let _ = write!(self.cbor_location, "/{:?}", v);
2971              } else {
2972                self.add_error(format!("map requires entry key of type {}", ident));
2973              }
2974              return Ok(());
2975            }
2976
2977            if is_ident_null_data_type(self.cddl, ident) && !self.validating_value {
2978              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
2979                self
2980                  .validated_keys
2981                  .get_or_insert(vec![k.clone()])
2982                  .push(k.clone());
2983                self.object_value = Some(v.clone());
2984                let _ = write!(self.cbor_location, "/{:?}", v);
2985              } else {
2986                self.add_error(format!("map requires entry key of type {}", ident));
2987              }
2988              return Ok(());
2989            }
2990
2991            if is_ident_byte_string_data_type(self.cddl, ident) && !self.validating_value {
2992              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bytes(_))) {
2993                self
2994                  .validated_keys
2995                  .get_or_insert(vec![k.clone()])
2996                  .push(k.clone());
2997                self.object_value = Some(v.clone());
2998                let _ = write!(self.cbor_location, "/{:?}", v);
2999              } else {
3000                self.add_error(format!("map requires entry key of type {}", ident));
3001              }
3002              return Ok(());
3003            }
3004
3005            if is_ident_float_data_type(self.cddl, ident) && !self.validating_value {
3006              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
3007                self
3008                  .validated_keys
3009                  .get_or_insert(vec![k.clone()])
3010                  .push(k.clone());
3011                self.object_value = Some(v.clone());
3012                let _ = write!(self.cbor_location, "/{:?}", v);
3013              } else {
3014                self.add_error(format!("map requires entry key of type {}", ident));
3015              }
3016              return Ok(());
3017            }
3018
3019            if token::lookup_ident(ident.ident)
3020              .in_standard_prelude()
3021              .is_some()
3022            {
3023              self.add_error(format!(
3024                "expected object value of type {}, got object",
3025                ident.ident
3026              ));
3027              return Ok(());
3028            }
3029
3030            self.visit_value(&token::Value::TEXT(ident.ident.into()))
3031          }
3032          #[cfg(not(feature = "ast-span"))]
3033          Some(Occur::Optional {}) | None => {
3034            if is_ident_string_data_type(self.cddl, ident) && !self.validating_value {
3035              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Text(_))) {
3036                self
3037                  .validated_keys
3038                  .get_or_insert(vec![k.clone()])
3039                  .push(k.clone());
3040                self.object_value = Some(v.clone());
3041                self.cbor_location.push_str(&format!("/{}", value));
3042              } else {
3043                self.add_error(format!("map requires entry key of type {}", ident));
3044              }
3045
3046              return Ok(());
3047            }
3048
3049            if is_ident_integer_data_type(self.cddl, ident) && !self.validating_value {
3050              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Integer(_))) {
3051                self
3052                  .validated_keys
3053                  .get_or_insert(vec![k.clone()])
3054                  .push(k.clone());
3055                self.object_value = Some(v.clone());
3056                self.cbor_location.push_str(&format!("/{}", value));
3057              } else {
3058                self.add_error(format!("map requires entry key of type {}", ident));
3059              }
3060              return Ok(());
3061            }
3062
3063            if is_ident_bool_data_type(self.cddl, ident) && !self.validating_value {
3064              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bool(_))) {
3065                self
3066                  .validated_keys
3067                  .get_or_insert(vec![k.clone()])
3068                  .push(k.clone());
3069                self.object_value = Some(v.clone());
3070                self.cbor_location.push_str(&format!("/{}", value));
3071              } else {
3072                self.add_error(format!("map requires entry key of type {}", ident));
3073              }
3074              return Ok(());
3075            }
3076
3077            if is_ident_null_data_type(self.cddl, ident) && !self.validating_value {
3078              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
3079                self
3080                  .validated_keys
3081                  .get_or_insert(vec![k.clone()])
3082                  .push(k.clone());
3083                self.object_value = Some(v.clone());
3084                self.cbor_location.push_str(&format!("/{}", value));
3085              } else {
3086                self.add_error(format!("map requires entry key of type {}", ident));
3087              }
3088              return Ok(());
3089            }
3090
3091            if is_ident_byte_string_data_type(self.cddl, ident) && !self.validating_value {
3092              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bytes(_))) {
3093                self
3094                  .validated_keys
3095                  .get_or_insert(vec![k.clone()])
3096                  .push(k.clone());
3097                self.object_value = Some(v.clone());
3098                self.cbor_location.push_str(&format!("/{}", value));
3099              } else {
3100                self.add_error(format!("map requires entry key of type {}", ident));
3101              }
3102              return Ok(());
3103            }
3104
3105            if is_ident_float_data_type(self.cddl, ident) && !self.validating_value {
3106              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
3107                self
3108                  .validated_keys
3109                  .get_or_insert(vec![k.clone()])
3110                  .push(k.clone());
3111                self.object_value = Some(v.clone());
3112                self.cbor_location.push_str(&format!("/{}", value));
3113              } else {
3114                self.add_error(format!("map requires entry key of type {}", ident));
3115              }
3116              return Ok(());
3117            }
3118
3119            if token::lookup_ident(ident.ident)
3120              .in_standard_prelude()
3121              .is_some()
3122            {
3123              self.add_error(format!(
3124                "expected object value of type {}, got object",
3125                ident.ident
3126              ));
3127              return Ok(());
3128            }
3129
3130            self.visit_value(&token::Value::TEXT(ident.ident.into()))
3131          }
3132          Some(occur) => {
3133            let mut errors = Vec::new();
3134
3135            if is_ident_string_data_type(self.cddl, ident) {
3136              let values_to_validate = m
3137                .iter()
3138                .filter_map(|(k, v)| {
3139                  if let Some(keys) = &self.validated_keys {
3140                    if !keys.contains(k) {
3141                      if matches!(k, Value::Text(_)) {
3142                        Some(v.clone())
3143                      } else {
3144                        errors.push(format!("key of type {} required, got {:?}", ident, k));
3145                        None
3146                      }
3147                    } else {
3148                      None
3149                    }
3150                  } else if matches!(k, Value::Text(_)) {
3151                    Some(v.clone())
3152                  } else {
3153                    errors.push(format!("key of type {} required, got {:?}", ident, k));
3154                    None
3155                  }
3156                })
3157                .collect::<Vec<_>>();
3158
3159              self.values_to_validate = Some(values_to_validate);
3160            }
3161
3162            if is_ident_integer_data_type(self.cddl, ident) {
3163              let mut errors = Vec::new();
3164              let values_to_validate = m
3165                .iter()
3166                .filter_map(|(k, v)| {
3167                  if let Some(keys) = &self.validated_keys {
3168                    if !keys.contains(k) {
3169                      if matches!(k, Value::Integer(_)) {
3170                        Some(v.clone())
3171                      } else {
3172                        errors.push(format!("key of type {} required, got {:?}", ident, k));
3173                        None
3174                      }
3175                    } else {
3176                      None
3177                    }
3178                  } else if matches!(k, Value::Integer(_)) {
3179                    Some(v.clone())
3180                  } else {
3181                    errors.push(format!("key of type {} required, got {:?}", ident, k));
3182                    None
3183                  }
3184                })
3185                .collect::<Vec<_>>();
3186
3187              self.values_to_validate = Some(values_to_validate);
3188            }
3189
3190            if is_ident_bool_data_type(self.cddl, ident) {
3191              let mut errors = Vec::new();
3192              let values_to_validate = m
3193                .iter()
3194                .filter_map(|(k, v)| {
3195                  if let Some(keys) = &self.validated_keys {
3196                    if !keys.contains(k) {
3197                      if matches!(k, Value::Bool(_)) {
3198                        Some(v.clone())
3199                      } else {
3200                        errors.push(format!("key of type {} required, got {:?}", ident, k));
3201                        None
3202                      }
3203                    } else {
3204                      None
3205                    }
3206                  } else if matches!(k, Value::Bool(_)) {
3207                    Some(v.clone())
3208                  } else {
3209                    errors.push(format!("key of type {} required, got {:?}", ident, k));
3210                    None
3211                  }
3212                })
3213                .collect::<Vec<_>>();
3214
3215              self.values_to_validate = Some(values_to_validate);
3216            }
3217
3218            if is_ident_byte_string_data_type(self.cddl, ident) {
3219              let mut errors = Vec::new();
3220              let values_to_validate = m
3221                .iter()
3222                .filter_map(|(k, v)| {
3223                  if let Some(keys) = &self.validated_keys {
3224                    if !keys.contains(k) {
3225                      if matches!(k, Value::Bytes(_)) {
3226                        Some(v.clone())
3227                      } else {
3228                        errors.push(format!("key of type {} required, got {:?}", ident, k));
3229                        None
3230                      }
3231                    } else {
3232                      None
3233                    }
3234                  } else if matches!(k, Value::Bytes(_)) {
3235                    Some(v.clone())
3236                  } else {
3237                    errors.push(format!("key of type {} required, got {:?}", ident, k));
3238                    None
3239                  }
3240                })
3241                .collect::<Vec<_>>();
3242
3243              self.values_to_validate = Some(values_to_validate);
3244            }
3245
3246            if is_ident_null_data_type(self.cddl, ident) {
3247              let mut errors = Vec::new();
3248              let values_to_validate = m
3249                .iter()
3250                .filter_map(|(k, v)| {
3251                  if let Some(keys) = &self.validated_keys {
3252                    if !keys.contains(k) {
3253                      if matches!(k, Value::Null) {
3254                        Some(v.clone())
3255                      } else {
3256                        errors.push(format!("key of type {} required, got {:?}", ident, k));
3257                        None
3258                      }
3259                    } else {
3260                      None
3261                    }
3262                  } else if matches!(k, Value::Null) {
3263                    Some(v.clone())
3264                  } else {
3265                    errors.push(format!("key of type {} required, got {:?}", ident, k));
3266                    None
3267                  }
3268                })
3269                .collect::<Vec<_>>();
3270
3271              self.values_to_validate = Some(values_to_validate);
3272            }
3273
3274            if is_ident_float_data_type(self.cddl, ident) {
3275              let mut errors = Vec::new();
3276              let values_to_validate = m
3277                .iter()
3278                .filter_map(|(k, v)| {
3279                  if let Some(keys) = &self.validated_keys {
3280                    if !keys.contains(k) {
3281                      if matches!(k, Value::Float(_)) {
3282                        Some(v.clone())
3283                      } else {
3284                        errors.push(format!("key of type {} required, got {:?}", ident, k));
3285                        None
3286                      }
3287                    } else {
3288                      None
3289                    }
3290                  } else if matches!(k, Value::Float(_)) {
3291                    Some(v.clone())
3292                  } else {
3293                    errors.push(format!("key of type {} required, got {:?}", ident, k));
3294                    None
3295                  }
3296                })
3297                .collect::<Vec<_>>();
3298
3299              self.values_to_validate = Some(values_to_validate);
3300            }
3301
3302            // If key validation error occurs, return early before checking occurrences
3303            if !errors.is_empty() {
3304              for e in errors.into_iter() {
3305                self.add_error(e);
3306              }
3307
3308              return Ok(());
3309            }
3310
3311            #[cfg(feature = "ast-span")]
3312            if let Occur::ZeroOrMore { .. } | Occur::OneOrMore { .. } = occur {
3313              if let Occur::OneOrMore { .. } = occur {
3314                if m.is_empty() {
3315                  self.add_error(format!(
3316                    "map cannot be empty, one or more entries with key type {} required",
3317                    ident
3318                  ));
3319                  return Ok(());
3320                }
3321              }
3322            } else if let Occur::Exact { lower, upper, .. } = occur {
3323              if let Some(values_to_validate) = &self.values_to_validate {
3324                if let Some(lower) = lower {
3325                  if let Some(upper) = upper {
3326                    if values_to_validate.len() < *lower || values_to_validate.len() > *upper {
3327                      if lower == upper {
3328                        self.add_error(format!(
3329                          "object must contain exactly {} entries of key of type {}",
3330                          lower, ident,
3331                        ));
3332                      } else {
3333                        self.add_error(format!(
3334                          "object must contain between {} and {} entries of key of type {}",
3335                          lower, upper, ident,
3336                        ));
3337                      }
3338
3339                      return Ok(());
3340                    }
3341                  }
3342
3343                  if values_to_validate.len() < *lower {
3344                    self.add_error(format!(
3345                      "object must contain at least {} entries of key of type {}",
3346                      lower, ident,
3347                    ));
3348
3349                    return Ok(());
3350                  }
3351                }
3352
3353                if let Some(upper) = upper {
3354                  if values_to_validate.len() > *upper {
3355                    self.add_error(format!(
3356                      "object must contain no more than {} entries of key of type {}",
3357                      upper, ident,
3358                    ));
3359
3360                    return Ok(());
3361                  }
3362                }
3363
3364                return Ok(());
3365              }
3366            }
3367
3368            #[cfg(not(feature = "ast-span"))]
3369            if let Occur::ZeroOrMore {} | Occur::OneOrMore {} = occur {
3370              if let Occur::OneOrMore {} = occur {
3371                if m.is_empty() {
3372                  self.add_error(format!(
3373                    "object cannot be empty, one or more entries with key type {} required",
3374                    ident
3375                  ));
3376                  return Ok(());
3377                }
3378              }
3379            } else if let Occur::Exact { lower, upper } = occur {
3380              if let Some(values_to_validate) = &self.values_to_validate {
3381                if let Some(lower) = lower {
3382                  if let Some(upper) = upper {
3383                    if values_to_validate.len() < *lower || values_to_validate.len() > *upper {
3384                      if lower == upper {
3385                        self.add_error(format!(
3386                          "object must contain exactly {} entries of key of type {}",
3387                          lower, ident,
3388                        ));
3389                      } else {
3390                        self.add_error(format!(
3391                          "object must contain between {} and {} entries of key of type {}",
3392                          lower, upper, ident,
3393                        ));
3394                      }
3395
3396                      return Ok(());
3397                    }
3398                  }
3399
3400                  if values_to_validate.len() < *lower {
3401                    self.add_error(format!(
3402                      "object must contain at least {} entries of key of type {}",
3403                      lower, ident,
3404                    ));
3405
3406                    return Ok(());
3407                  }
3408                }
3409
3410                if let Some(upper) = upper {
3411                  if values_to_validate.len() > *upper {
3412                    self.add_error(format!(
3413                      "object must contain no more than {} entries of key of type {}",
3414                      upper, ident,
3415                    ));
3416
3417                    return Ok(());
3418                  }
3419                }
3420
3421                return Ok(());
3422              }
3423            }
3424
3425            if is_ident_string_data_type(self.cddl, ident) && !self.validating_value {
3426              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Text(_))) {
3427                self
3428                  .validated_keys
3429                  .get_or_insert(vec![k.clone()])
3430                  .push(k.clone());
3431                self.object_value = Some(v.clone());
3432                let _ = write!(self.cbor_location, "/{:?}", v);
3433              } else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
3434                || (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
3435              {
3436                self.add_error(format!("map requires entry key of type {}", ident));
3437              }
3438
3439              return Ok(());
3440            }
3441
3442            if is_ident_integer_data_type(self.cddl, ident) && !self.validating_value {
3443              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Integer(_))) {
3444                self
3445                  .validated_keys
3446                  .get_or_insert(vec![k.clone()])
3447                  .push(k.clone());
3448                self.object_value = Some(v.clone());
3449                let _ = write!(self.cbor_location, "/{:?}", v);
3450              } else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
3451                || (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
3452              {
3453                self.add_error(format!("map requires entry key of type {}", ident));
3454              }
3455              return Ok(());
3456            }
3457
3458            if is_ident_bool_data_type(self.cddl, ident) && !self.validating_value {
3459              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bool(_))) {
3460                self
3461                  .validated_keys
3462                  .get_or_insert(vec![k.clone()])
3463                  .push(k.clone());
3464                self.object_value = Some(v.clone());
3465                let _ = write!(self.cbor_location, "/{:?}", v);
3466              } else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
3467                || (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
3468              {
3469                self.add_error(format!("map requires entry key of type {}", ident));
3470              }
3471              return Ok(());
3472            }
3473
3474            if is_ident_null_data_type(self.cddl, ident) && !self.validating_value {
3475              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
3476                self
3477                  .validated_keys
3478                  .get_or_insert(vec![k.clone()])
3479                  .push(k.clone());
3480                self.object_value = Some(v.clone());
3481                let _ = write!(self.cbor_location, "/{:?}", v);
3482              } else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
3483                || (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
3484              {
3485                self.add_error(format!("map requires entry key of type {}", ident));
3486              }
3487              return Ok(());
3488            }
3489
3490            if is_ident_byte_string_data_type(self.cddl, ident) && !self.validating_value {
3491              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bytes(_))) {
3492                self
3493                  .validated_keys
3494                  .get_or_insert(vec![k.clone()])
3495                  .push(k.clone());
3496                self.object_value = Some(v.clone());
3497                let _ = write!(self.cbor_location, "/{:?}", v);
3498              } else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
3499                || (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
3500              {
3501                self.add_error(format!("map requires entry key of type {}", ident));
3502              }
3503              return Ok(());
3504            }
3505
3506            if is_ident_float_data_type(self.cddl, ident) && !self.validating_value {
3507              if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
3508                self
3509                  .validated_keys
3510                  .get_or_insert(vec![k.clone()])
3511                  .push(k.clone());
3512                self.object_value = Some(v.clone());
3513                let _ = write!(self.cbor_location, "/{:?}", v);
3514              } else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
3515                || (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
3516              {
3517                self.add_error(format!("map requires entry key of type {}", ident));
3518              }
3519              return Ok(());
3520            }
3521
3522            if token::lookup_ident(ident.ident)
3523              .in_standard_prelude()
3524              .is_some()
3525            {
3526              self.add_error(format!(
3527                "expected object value of type {}, got object",
3528                ident.ident
3529              ));
3530              return Ok(());
3531            }
3532
3533            self.visit_value(&token::Value::TEXT(ident.ident.into()))
3534          }
3535        }
3536      }
3537      _ => {
3538        if let Some(cut_value) = self.cut_value.take() {
3539          self.add_error(format!(
3540            "cut present for member key {}. expected type {}, got {:?}",
3541            cut_value, ident, self.cbor
3542          ));
3543        } else {
3544          self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
3545        }
3546        Ok(())
3547      }
3548    }
3549  }
3550
3551  fn visit_value_member_key_entry(
3552    &mut self,
3553    entry: &ValueMemberKeyEntry<'a>,
3554  ) -> visitor::Result<Error<T>> {
3555    if let Some(occur) = &entry.occur {
3556      self.visit_occurrence(occur)?;
3557    }
3558
3559    let current_location = self.cbor_location.clone();
3560
3561    if let Some(mk) = &entry.member_key {
3562      let error_count = self.errors.len();
3563      self.is_member_key = true;
3564      self.visit_memberkey(mk)?;
3565      self.is_member_key = false;
3566
3567      // Move to next entry if member key validation fails
3568      if self.errors.len() != error_count {
3569        self.advance_to_next_entry = true;
3570        return Ok(());
3571      }
3572    }
3573
3574    if let Some(values) = &self.values_to_validate {
3575      for v in values.iter() {
3576        #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
3577        let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features.clone());
3578        #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
3579        let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features);
3580        #[cfg(not(feature = "additional-controls"))]
3581        let mut cv = CBORValidator::new(self.cddl, v.clone());
3582
3583        cv.generic_rules = self.generic_rules.clone();
3584        cv.eval_generic_rule = self.eval_generic_rule;
3585        cv.is_multi_type_choice = self.is_multi_type_choice;
3586        cv.is_multi_group_choice = self.is_multi_group_choice;
3587        cv.cbor_location.push_str(&self.cbor_location);
3588        cv.type_group_name_entry = self.type_group_name_entry;
3589        cv.validating_value = true;
3590        cv.visit_type(&entry.entry_type)?;
3591
3592        self.cbor_location = current_location.clone();
3593
3594        self.errors.append(&mut cv.errors);
3595        if entry.occur.is_some() {
3596          self.occurrence = None;
3597        }
3598      }
3599
3600      return Ok(());
3601    }
3602
3603    if let Some(v) = self.object_value.take() {
3604      #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
3605      let mut cv = CBORValidator::new(self.cddl, v, self.enabled_features.clone());
3606      #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
3607      let mut cv = CBORValidator::new(self.cddl, v, self.enabled_features);
3608      #[cfg(not(feature = "additional-controls"))]
3609      let mut cv = CBORValidator::new(self.cddl, v);
3610
3611      cv.generic_rules = self.generic_rules.clone();
3612      cv.eval_generic_rule = self.eval_generic_rule;
3613      cv.is_multi_type_choice = self.is_multi_type_choice;
3614      cv.is_multi_group_choice = self.is_multi_group_choice;
3615      cv.cbor_location.push_str(&self.cbor_location);
3616      cv.type_group_name_entry = self.type_group_name_entry;
3617      cv.visit_type(&entry.entry_type)?;
3618
3619      self.cbor_location = current_location;
3620
3621      self.errors.append(&mut cv.errors);
3622      if entry.occur.is_some() {
3623        self.occurrence = None;
3624      }
3625
3626      Ok(())
3627    } else if !self.advance_to_next_entry {
3628      self.visit_type(&entry.entry_type)
3629    } else {
3630      Ok(())
3631    }
3632  }
3633
3634  fn visit_type_groupname_entry(
3635    &mut self,
3636    entry: &TypeGroupnameEntry<'a>,
3637  ) -> visitor::Result<Error<T>> {
3638    self.type_group_name_entry = Some(entry.name.ident);
3639
3640    if let Some(ga) = &entry.generic_args {
3641      if let Some(rule) = rule_from_ident(self.cddl, &entry.name) {
3642        if let Some(gr) = self
3643          .generic_rules
3644          .iter_mut()
3645          .find(|gr| gr.name == entry.name.ident)
3646        {
3647          for arg in ga.args.iter() {
3648            gr.args.push((*arg.arg).clone());
3649          }
3650        } else if let Some(params) = generic_params_from_rule(rule) {
3651          self.generic_rules.push(GenericRule {
3652            name: entry.name.ident,
3653            params,
3654            args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
3655          });
3656        }
3657
3658        #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
3659        let mut cv =
3660          CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
3661        #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
3662        let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
3663        #[cfg(not(feature = "additional-controls"))]
3664        let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
3665
3666        cv.generic_rules = self.generic_rules.clone();
3667        cv.eval_generic_rule = Some(entry.name.ident);
3668        cv.is_multi_type_choice = self.is_multi_type_choice;
3669        cv.visit_rule(rule)?;
3670
3671        self.errors.append(&mut cv.errors);
3672
3673        return Ok(());
3674      }
3675    }
3676
3677    let type_choice_alternates = type_choice_alternates_from_ident(self.cddl, &entry.name);
3678    if !type_choice_alternates.is_empty() {
3679      self.is_multi_type_choice = true;
3680    }
3681
3682    let error_count = self.errors.len();
3683    for t in type_choice_alternates {
3684      let cur_errors = self.errors.len();
3685      self.visit_type(t)?;
3686      if self.errors.len() == cur_errors {
3687        for _ in 0..self.errors.len() - error_count {
3688          self.errors.pop();
3689        }
3690
3691        return Ok(());
3692      }
3693    }
3694
3695    let error_count = self.errors.len();
3696    let group_choice_alternates = group_choice_alternates_from_ident(self.cddl, &entry.name);
3697    if !group_choice_alternates.is_empty() {
3698      self.is_multi_group_choice = true;
3699    }
3700
3701    for ge in group_choice_alternates {
3702      let cur_errors = self.errors.len();
3703      self.visit_group_entry(ge)?;
3704      if self.errors.len() == cur_errors {
3705        for _ in 0..self.errors.len() - error_count {
3706          self.errors.pop();
3707        }
3708
3709        return Ok(());
3710      }
3711    }
3712
3713    walk_type_groupname_entry(self, entry)?;
3714    self.type_group_name_entry = None;
3715
3716    Ok(())
3717  }
3718
3719  fn visit_memberkey(&mut self, mk: &MemberKey<'a>) -> visitor::Result<Error<T>> {
3720    match mk {
3721      MemberKey::Type1 { is_cut, .. } => {
3722        self.is_cut_present = *is_cut;
3723        walk_memberkey(self, mk)?;
3724        self.is_cut_present = false;
3725      }
3726      MemberKey::Bareword { .. } => {
3727        self.is_colon_shortcut_present = true;
3728        walk_memberkey(self, mk)?;
3729        self.is_colon_shortcut_present = false;
3730      }
3731      _ => return walk_memberkey(self, mk),
3732    }
3733
3734    Ok(())
3735  }
3736
3737  fn visit_value(&mut self, value: &token::Value<'a>) -> visitor::Result<Error<T>> {
3738    let error: Option<String> = match &self.cbor {
3739      Value::Integer(i) => match value {
3740        token::Value::INT(v) => match &self.ctrl {
3741          Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
3742            if i128::from(*i) != *v as i128 =>
3743          {
3744            None
3745          }
3746          Some(ControlOperator::LT) if i128::from(*i) < *v as i128 => None,
3747          Some(ControlOperator::LE) if i128::from(*i) <= *v as i128 => None,
3748          Some(ControlOperator::GT) if i128::from(*i) > *v as i128 => None,
3749          Some(ControlOperator::GE) if i128::from(*i) >= *v as i128 => None,
3750          #[cfg(feature = "additional-controls")]
3751          Some(ControlOperator::PLUS) => {
3752            if i128::from(*i) == *v as i128 {
3753              None
3754            } else {
3755              Some(format!("expected computed .plus value {}, got {:?}", v, i))
3756            }
3757          }
3758          #[cfg(feature = "additional-controls")]
3759          None | Some(ControlOperator::FEATURE) => {
3760            if i128::from(*i) == *v as i128 {
3761              None
3762            } else {
3763              Some(format!("expected value {}, got {:?}", v, i))
3764            }
3765          }
3766          #[cfg(not(feature = "additional-controls"))]
3767          None => {
3768            if i128::from(*i) == *v as i128 {
3769              None
3770            } else {
3771              Some(format!("expected value {}, got {:?}", v, i))
3772            }
3773          }
3774          _ => Some(format!(
3775            "expected value {} {}, got {:?}",
3776            self.ctrl.unwrap(),
3777            v,
3778            i
3779          )),
3780        },
3781        token::Value::UINT(v) => match &self.ctrl {
3782          Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
3783            if i128::from(*i) != *v as i128 =>
3784          {
3785            None
3786          }
3787          Some(ControlOperator::LT) if i128::from(*i) < *v as i128 => None,
3788          Some(ControlOperator::LE) if i128::from(*i) <= *v as i128 => None,
3789          Some(ControlOperator::GT) if i128::from(*i) > *v as i128 => None,
3790          Some(ControlOperator::GE) if i128::from(*i) >= *v as i128 => None,
3791          Some(ControlOperator::SIZE) => match 256i128.checked_pow(*v as u32) {
3792            Some(n) if i128::from(*i) < n => None,
3793            _ => Some(format!("expected value .size {}, got {:?}", v, i)),
3794          },
3795          Some(ControlOperator::BITS) => {
3796            if let Some(sv) = 1u32.checked_shl(*v as u32) {
3797              if (i128::from(*i) & sv as i128) != 0 {
3798                None
3799              } else {
3800                Some(format!("expected uint .bits {}, got {:?}", v, i))
3801              }
3802            } else {
3803              Some(format!("expected uint .bits {}, got {:?}", v, i))
3804            }
3805          }
3806          #[cfg(feature = "additional-controls")]
3807          Some(ControlOperator::PLUS) => {
3808            if i128::from(*i) == *v as i128 {
3809              None
3810            } else {
3811              Some(format!("expected computed .plus value {}, got {:?}", v, i))
3812            }
3813          }
3814          #[cfg(feature = "additional-controls")]
3815          None | Some(ControlOperator::FEATURE) => {
3816            if i128::from(*i) == *v as i128 {
3817              None
3818            } else {
3819              Some(format!("expected value {}, got {:?}", v, i))
3820            }
3821          }
3822          #[cfg(not(feature = "additional-controls"))]
3823          None => {
3824            if i128::from(*i) == *v as i128 {
3825              None
3826            } else {
3827              Some(format!("expected value {}, got {:?}", v, i))
3828            }
3829          }
3830          _ => Some(format!(
3831            "expected value {} {}, got {:?}",
3832            self.ctrl.unwrap(),
3833            v,
3834            i
3835          )),
3836        },
3837
3838        _ => Some(format!("expected {}, got {:?}", value, i)),
3839      },
3840      Value::Float(f) => match value {
3841        token::Value::FLOAT(v) => match &self.ctrl {
3842          Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
3843            if (*f - *v).abs() > f64::EPSILON =>
3844          {
3845            None
3846          }
3847          Some(ControlOperator::LT) if *f < *v => None,
3848          Some(ControlOperator::LE) if *f <= *v => None,
3849          Some(ControlOperator::GT) if *f > *v => None,
3850          Some(ControlOperator::GE) if *f >= *v => None,
3851          #[cfg(feature = "additional-controls")]
3852          Some(ControlOperator::PLUS) => {
3853            if (*f - *v).abs() < f64::EPSILON {
3854              None
3855            } else {
3856              Some(format!("expected computed .plus value {}, got {:?}", v, f))
3857            }
3858          }
3859          #[cfg(feature = "additional-controls")]
3860          None | Some(ControlOperator::FEATURE) => {
3861            if (*f - *v).abs() < f64::EPSILON {
3862              None
3863            } else {
3864              Some(format!("expected value {}, got {:?}", v, f))
3865            }
3866          }
3867          #[cfg(not(feature = "additional-controls"))]
3868          None => {
3869            if (*f - *v).abs() < f64::EPSILON {
3870              None
3871            } else {
3872              Some(format!("expected value {}, got {:?}", v, f))
3873            }
3874          }
3875          _ => Some(format!(
3876            "expected value {} {}, got {:?}",
3877            self.ctrl.unwrap(),
3878            v,
3879            f
3880          )),
3881        },
3882        _ => Some(format!("expected {}, got {:?}", value, f)),
3883      },
3884      Value::Text(s) => match value {
3885        token::Value::TEXT(t) => match &self.ctrl {
3886          Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT) => {
3887            if s != t {
3888              None
3889            } else {
3890              Some(format!("expected {} .ne to \"{}\"", value, s))
3891            }
3892          }
3893          Some(ControlOperator::REGEXP) | Some(ControlOperator::PCRE) => {
3894            let re = regex::Regex::new(
3895              &format_regex(
3896                // Text strings must be JSON escaped per
3897                // https://datatracker.ietf.org/doc/html/rfc8610#section-3.1
3898                serde_json::from_str::<serde_json::Value>(&format!("\"{}\"", t))
3899                  .map_err(Error::JSONParsing)?
3900                  .as_str()
3901                  .ok_or_else(|| Error::from_validator(self, "malformed regex".to_string()))?,
3902              )
3903              .ok_or_else(|| Error::from_validator(self, "malformed regex".to_string()))?,
3904            )
3905            .map_err(|e| Error::from_validator(self, e.to_string()))?;
3906
3907            if re.is_match(s) {
3908              None
3909            } else {
3910              Some(format!("expected \"{}\" to match regex \"{}\"", s, t))
3911            }
3912          }
3913          #[cfg(feature = "additional-controls")]
3914          Some(ControlOperator::ABNF) => validate_abnf(t, s)
3915            .err()
3916            .map(|e| format!("\"{}\" is not valid against abnf: {}", s, e)),
3917          _ => {
3918            #[cfg(feature = "additional-controls")]
3919            if s == t {
3920              None
3921            } else if let Some(ControlOperator::CAT) | Some(ControlOperator::DET) = &self.ctrl {
3922              Some(format!(
3923                "expected value to match concatenated string {}, got \"{}\"",
3924                value, s
3925              ))
3926            } else if let Some(ctrl) = &self.ctrl {
3927              Some(format!("expected value {} {}, got \"{}\"", ctrl, value, s))
3928            } else {
3929              Some(format!("expected value {} got \"{}\"", value, s))
3930            }
3931
3932            #[cfg(not(feature = "additional-controls"))]
3933            if s == t {
3934              None
3935            } else if let Some(ctrl) = &self.ctrl {
3936              Some(format!("expected value {} {}, got \"{}\"", ctrl, value, s))
3937            } else {
3938              Some(format!("expected value {} got \"{}\"", value, s))
3939            }
3940          }
3941        },
3942        token::Value::UINT(u) => match &self.ctrl {
3943          Some(ControlOperator::SIZE) => {
3944            if s.len() == *u {
3945              None
3946            } else {
3947              Some(format!("expected \"{}\" .size {}, got {}", s, u, s.len()))
3948            }
3949          }
3950          _ => Some(format!("expected {}, got {}", u, s)),
3951        },
3952        token::Value::BYTE(token::ByteValue::UTF8(b)) if s.as_bytes() == b.as_ref() => None,
3953        token::Value::BYTE(token::ByteValue::B16(b)) if s.as_bytes() == b.as_ref() => None,
3954        token::Value::BYTE(token::ByteValue::B64(b)) if s.as_bytes() == b.as_ref() => None,
3955        _ => Some(format!("expected {}, got \"{}\"", value, s)),
3956      },
3957      Value::Bytes(b) => match value {
3958        token::Value::UINT(v) => match &self.ctrl {
3959          Some(ControlOperator::SIZE) => {
3960            if let Some(range_upper) = self.range_upper.as_ref() {
3961              let len = b.len();
3962              if len < *v || len > *range_upper {
3963                Some(format!(
3964                  "expected bytes .size to be in range {} <= value <= {}, got {}",
3965                  v, range_upper, len
3966                ))
3967              } else {
3968                None
3969              }
3970            } else if b.len() == *v {
3971              None
3972            } else {
3973              Some(format!("expected \"{:?}\" .size {}, got {}", b, v, b.len()))
3974            }
3975          }
3976          Some(ControlOperator::BITS) => {
3977            if let Some(rsv) = v.checked_shr(3) {
3978              if let Some(s) = b.get(rsv) {
3979                if let Some(lsv) = 1u32.checked_shl(*v as u32 & 7) {
3980                  if (*s as u32 & lsv) != 0 {
3981                    None
3982                  } else {
3983                    Some(format!(
3984                      "expected value {} {}, got {:?}",
3985                      self.ctrl.unwrap(),
3986                      v,
3987                      b
3988                    ))
3989                  }
3990                } else {
3991                  Some(format!(
3992                    "expected value {} {}, got {:?}",
3993                    self.ctrl.unwrap(),
3994                    v,
3995                    b
3996                  ))
3997                }
3998              } else {
3999                Some(format!(
4000                  "expected value {} {}, got {:?}",
4001                  self.ctrl.unwrap(),
4002                  v,
4003                  b
4004                ))
4005              }
4006            } else {
4007              Some(format!(
4008                "expected value {} {}, got {:?}",
4009                self.ctrl.unwrap(),
4010                v,
4011                b
4012              ))
4013            }
4014          }
4015          _ => {
4016            if let Some(ctrl) = self.ctrl {
4017              Some(format!("expected value {} {}, got {:?}", ctrl, v, b))
4018            } else {
4019              Some(format!("expected value {}, got {:?}", v, b))
4020            }
4021          }
4022        },
4023        #[cfg(feature = "additional-controls")]
4024        token::Value::TEXT(t) => match &self.ctrl {
4025          Some(ControlOperator::ABNFB) => {
4026            validate_abnf(t, std::str::from_utf8(b).map_err(Error::UTF8Parsing)?)
4027              .err()
4028              .map(|e| {
4029                format!(
4030                  "cbor bytes \"{:?}\" are not valid against abnf {}: {}",
4031                  b, t, e
4032                )
4033              })
4034          }
4035          _ => Some(format!(
4036            "expected value {} {}, got {:?}",
4037            self.ctrl.unwrap(),
4038            t,
4039            b
4040          )),
4041        },
4042        #[cfg(feature = "additional-controls")]
4043        token::Value::BYTE(bv) => match &self.ctrl {
4044          Some(ControlOperator::ABNFB) => match bv {
4045            ByteValue::UTF8(utf8bv) => validate_abnf(
4046              std::str::from_utf8(utf8bv).map_err(Error::UTF8Parsing)?,
4047              std::str::from_utf8(b).map_err(Error::UTF8Parsing)?,
4048            )
4049            .err()
4050            .map(|e| {
4051              format!(
4052                "cbor bytes \"{:?}\" are not valid against abnf {}: {}",
4053                b, bv, e
4054              )
4055            }),
4056            ByteValue::B16(b16bv) => validate_abnf(
4057              std::str::from_utf8(&base16::decode(b16bv).map_err(Error::Base16Decoding)?)
4058                .map_err(Error::UTF8Parsing)?,
4059              std::str::from_utf8(b).map_err(Error::UTF8Parsing)?,
4060            )
4061            .err()
4062            .map(|e| {
4063              format!(
4064                "cbor bytes \"{:?}\" are not valid against abnf {}: {}",
4065                b, bv, e
4066              )
4067            }),
4068            ByteValue::B64(b64bv) => validate_abnf(
4069              std::str::from_utf8(
4070                &data_encoding::BASE64URL
4071                  .decode(b64bv)
4072                  .map_err(Error::Base64Decoding)?,
4073              )
4074              .map_err(Error::UTF8Parsing)?,
4075              std::str::from_utf8(b).map_err(Error::UTF8Parsing)?,
4076            )
4077            .err()
4078            .map(|e| {
4079              format!(
4080                "cbor bytes \"{:?}\" are not valid against abnf {}: {}",
4081                b, bv, e
4082              )
4083            }),
4084          },
4085          _ => Some(format!(
4086            "expected value {} {}, got {:?}",
4087            self.ctrl.unwrap(),
4088            bv,
4089            b
4090          )),
4091        },
4092        _ => Some(format!("expected {}, got {:?}", value, b)),
4093      },
4094      Value::Array(_) => {
4095        self.validate_array_items(&ArrayItemToken::Value(value))?;
4096
4097        None
4098      }
4099      Value::Map(o) => {
4100        if self.is_cut_present {
4101          self.cut_value = Some(Type1::from(value.clone()));
4102        }
4103
4104        if let token::Value::TEXT(Cow::Borrowed("any")) = value {
4105          return Ok(());
4106        }
4107
4108        // Retrieve the value from key unless optional/zero or more, in which
4109        // case advance to next group entry
4110        let k = token_value_into_cbor_value(value.clone());
4111
4112        #[cfg(feature = "ast-span")]
4113        if let Some(v) = o
4114          .iter()
4115          .find_map(|entry| if entry.0 == k { Some(&entry.1) } else { None })
4116        {
4117          self.validated_keys.get_or_insert(vec![k.clone()]).push(k);
4118          self.object_value = Some(v.clone());
4119          let _ = write!(self.cbor_location, "/{}", value);
4120
4121          None
4122        } else if let Some(Occur::Optional { .. }) | Some(Occur::ZeroOrMore { .. }) =
4123          &self.occurrence.take()
4124        {
4125          self.advance_to_next_entry = true;
4126          None
4127        } else if let Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT) = &self.ctrl {
4128          None
4129        } else {
4130          Some(format!("object missing key: \"{}\"", value))
4131        }
4132
4133        #[cfg(not(feature = "ast-span"))]
4134        if let Some(v) = o
4135          .iter()
4136          .find_map(|entry| if entry.0 == k { Some(&entry.1) } else { None })
4137        {
4138          self.validated_keys.get_or_insert(vec![k.clone()]).push(k);
4139          self.object_value = Some(v.clone());
4140          self.cbor_location.push_str(&format!("/{}", value));
4141
4142          None
4143        } else if let Some(Occur::Optional {}) | Some(Occur::ZeroOrMore {}) =
4144          &self.occurrence.take()
4145        {
4146          self.advance_to_next_entry = true;
4147          None
4148        } else if let Some(Token::NE) | Some(Token::DEFAULT) = &self.ctrl {
4149          None
4150        } else {
4151          Some(format!("object missing key: \"{}\"", value))
4152        }
4153      }
4154      _ => Some(format!("expected {}, got {:?}", value, self.cbor)),
4155    };
4156
4157    if let Some(e) = error {
4158      self.add_error(e);
4159    }
4160
4161    Ok(())
4162  }
4163
4164  fn visit_occurrence(&mut self, o: &Occurrence<'a>) -> visitor::Result<Error<T>> {
4165    self.occurrence = Some(o.occur);
4166
4167    Ok(())
4168  }
4169}
4170
4171/// Converts a CDDL value type to ciborium::value::Value
4172pub fn token_value_into_cbor_value(value: token::Value) -> ciborium::value::Value {
4173  match value {
4174    token::Value::UINT(i) => ciborium::value::Value::Integer(i.into()),
4175    token::Value::INT(i) => ciborium::value::Value::Integer(i.into()),
4176    token::Value::FLOAT(f) => ciborium::value::Value::Float(f),
4177    token::Value::TEXT(t) => ciborium::value::Value::Text(t.to_string()),
4178    token::Value::BYTE(b) => match b {
4179      ByteValue::UTF8(b) | ByteValue::B16(b) | ByteValue::B64(b) => {
4180        ciborium::value::Value::Bytes(b.into_owned())
4181      }
4182    },
4183  }
4184}
4185
4186#[cfg(test)]
4187#[cfg(not(target_arch = "wasm32"))]
4188mod tests {
4189  use super::*;
4190  use ciborium::cbor;
4191  use indoc::indoc;
4192
4193  #[cfg(not(feature = "additional-controls"))]
4194  #[test]
4195  fn validate() -> std::result::Result<(), Box<dyn std::error::Error>> {
4196    let cddl = indoc!(
4197      r#"
4198        tcpflagbytes = bstr .bits flags
4199        flags = &(
4200          fin: 8,
4201          syn: 9,
4202          rst: 10,
4203          psh: 11,
4204          ack: 12,
4205          urg: 13,
4206          ece: 14,
4207          cwr: 15,
4208          ns: 0,
4209        ) / (4..7) ; data offset bits
4210      "#
4211    );
4212
4213    let cbor = ciborium::value::Value::Bytes(vec![0x90, 0x6d]);
4214
4215    let mut lexer = lexer_from_str(cddl);
4216    let cddl = cddl_from_str(&mut lexer, cddl, true)?;
4217
4218    let mut cv = CBORValidator::new(&cddl, cbor);
4219    cv.validate()?;
4220
4221    Ok(())
4222  }
4223
4224  #[cfg(feature = "additional-controls")]
4225  #[test]
4226  fn validate_abnfb_1() -> std::result::Result<(), Box<dyn std::error::Error>> {
4227    let cddl = indoc!(
4228      r#"
4229        oid = bytes .abnfb ("oid" .det cbor-tags-oid)
4230        roid = bytes .abnfb ("roid" .det cbor-tags-oid)
4231 
4232        cbor-tags-oid = '
4233          oid = 1*arc
4234          roid = *arc
4235          arc = [nlsb] %x00-7f
4236          nlsb = %x81-ff *%x80-ff
4237        '
4238      "#
4239    );
4240
4241    let sha256_oid = "2.16.840.1.101.3.4.2.1";
4242
4243    let cbor = ciborium::value::Value::Bytes(sha256_oid.as_bytes().to_vec());
4244
4245    let cddl = cddl_from_str(cddl, true)?;
4246
4247    let mut cv = CBORValidator::new(&cddl, cbor, None);
4248    cv.validate()?;
4249
4250    Ok(())
4251  }
4252
4253  #[cfg(feature = "additional-controls")]
4254  #[test]
4255  fn validate_feature() -> std::result::Result<(), Box<dyn std::error::Error>> {
4256    let cddl = indoc!(
4257      r#"
4258        v = JC<"v", 2>
4259        JC<J, C> = J .feature "json" / C .feature "cbor"
4260      "#
4261    );
4262
4263    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4264    if let Err(e) = &cddl {
4265      println!("{}", e);
4266    }
4267
4268    let cbor = ciborium::value::Value::Integer(2.into());
4269
4270    let cddl = cddl.unwrap();
4271
4272    let mut cv = CBORValidator::new(&cddl, cbor, Some(&["cbor"]));
4273    cv.validate()?;
4274
4275    Ok(())
4276  }
4277
4278  #[test]
4279  fn validate_type_choice_alternate() -> std::result::Result<(), Box<dyn std::error::Error>> {
4280    let cddl = indoc!(
4281      r#"
4282        tester = [ $vals ]
4283        $vals /= 12
4284        $vals /= 13
4285      "#
4286    );
4287
4288    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4289    if let Err(e) = &cddl {
4290      println!("{}", e);
4291    }
4292
4293    let cbor = ciborium::cbor!([13]).unwrap();
4294
4295    let cddl = cddl.unwrap();
4296
4297    let mut cv = CBORValidator::new(&cddl, cbor, None);
4298    cv.validate()?;
4299
4300    Ok(())
4301  }
4302
4303  #[test]
4304  fn validate_group_choice_alternate_in_array(
4305  ) -> std::result::Result<(), Box<dyn std::error::Error>> {
4306    let cddl = indoc!(
4307      r#"
4308        tester = [$$val]
4309        $$val //= (
4310          type: 10,
4311          data: uint
4312        )
4313        $$val //= (
4314          type: 11,
4315          data: tstr
4316        )
4317      "#
4318    );
4319
4320    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4321    if let Err(e) = &cddl {
4322      println!("{}", e);
4323    }
4324
4325    let cbor = ciborium::cbor!([11, "test"]).unwrap();
4326
4327    let cddl = cddl.unwrap();
4328
4329    let mut cv = CBORValidator::new(&cddl, cbor, None);
4330    cv.validate()?;
4331
4332    Ok(())
4333  }
4334
4335  #[test]
4336  fn validate_tdate_tag() -> std::result::Result<(), Box<dyn std::error::Error>> {
4337    let cddl = indoc!(
4338      r#"
4339        root = time
4340      "#
4341    );
4342
4343    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4344    if let Err(e) = &cddl {
4345      println!("{}", e);
4346    }
4347
4348    let cbor = ciborium::value::Value::Tag(
4349      1,
4350      Box::from(ciborium::value::Value::Float(1680965875.01_f64)),
4351    );
4352
4353    let cddl = cddl.unwrap();
4354
4355    let mut cv = CBORValidator::new(&cddl, cbor, None);
4356    cv.validate()?;
4357
4358    Ok(())
4359  }
4360
4361  #[test]
4362  fn validate_abnfb_2() -> std::result::Result<(), Box<dyn std::error::Error>> {
4363    let cddl = indoc!(
4364      r#"
4365        ; Binary ABNF Test Schema
4366        test_cbor = {
4367          61285: sub_map
4368        }
4369        
4370        sub_map = {
4371          1: signature_abnf
4372        }
4373        
4374        signature = bytes .size 64
4375        
4376        signature_abnf = signature .abnfb '     
4377        ANYDATA
4378        ANYDATA = *OCTET
4379
4380        OCTET =  %x00-FF
4381        '
4382      "#
4383    );
4384
4385    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4386    if let Err(e) = &cddl {
4387      println!("{}", e);
4388    }
4389
4390    let cbor = ciborium::value::Value::Map(vec![(
4391      ciborium::value::Value::Integer(61285.into()),
4392      ciborium::value::Value::Map(vec![(
4393        ciborium::value::Value::Integer(1.into()),
4394        ciborium::value::Value::Bytes(b"test".to_vec()),
4395      )]),
4396    )]);
4397
4398    let cddl = cddl.unwrap();
4399
4400    let mut cv = CBORValidator::new(&cddl, cbor, None);
4401    cv.validate()?;
4402
4403    Ok(())
4404  }
4405
4406  #[test]
4407  fn multi_type_choice_type_rule_array_validation(
4408  ) -> std::result::Result<(), Box<dyn std::error::Error>> {
4409    use ciborium::value::Value;
4410
4411    let cddl = indoc!(
4412      r#"
4413        Ref = nil / refShort / refFull
4414
4415        blobSize = uint
4416        hashID = uint .lt 23
4417        hashName = text
4418        hashDigest = bytes
4419        
4420        refShort = [ blobSize, hashID, hashDigest ]
4421        refFull = { 1: blobSize, 2: hashName, 3: hashDigest }
4422      "#
4423    );
4424
4425    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4426    if let Err(e) = &cddl {
4427      println!("{}", e);
4428    }
4429
4430    let cbor = Value::Array(vec![
4431      Value::Integer(3.into()),
4432      Value::Integer(2.into()),
4433      Value::Bytes(
4434        base16::decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD").unwrap(),
4435      ),
4436    ]);
4437
4438    let cddl = cddl.unwrap();
4439
4440    let mut cv = CBORValidator::new(&cddl, cbor, None);
4441    cv.validate()?;
4442
4443    Ok(())
4444  }
4445
4446  #[test]
4447  fn tagged_data_in_array_validation() -> std::result::Result<(), Box<dyn std::error::Error>> {
4448    use ciborium::value::Value;
4449
4450    let cddl = indoc!(
4451      r#"
4452        start = [ * help ]
4453
4454        help = #6.123(bstr)
4455      "#
4456    );
4457
4458    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
4459    if let Err(e) = &cddl {
4460      println!("{}", e);
4461    }
4462
4463    let cbor = Value::Array(vec![Value::Tag(
4464      123,
4465      Box::from(Value::Bytes(base16::decode("00").unwrap())),
4466    )]);
4467
4468    let cddl = cddl.unwrap();
4469
4470    let mut cv = CBORValidator::new(&cddl, cbor, None);
4471    cv.validate()?;
4472
4473    Ok(())
4474  }
4475
4476  #[test]
4477  fn test_conditional_array_validation() {
4478    let cddl_str = r#"
4479        NestedPart = [
4480          disposition: 0,
4481          language: tstr,
4482          partIndex: uint,
4483          ( NullPart // SinglePart )
4484        ]
4485
4486        NullPart = ( cardinality: 0 )
4487        SinglePart = (
4488            cardinality: 1,
4489            contentType: tstr,
4490            content: bstr
4491        )
4492    "#;
4493
4494    let cddl = cddl_from_str(cddl_str, true).unwrap();
4495
4496    // Test data: A SinglePart with 6 elements (disposition, language, partIndex, cardinality, contentType, content)
4497    let cbor_data = Value::Array(vec![
4498      Value::Integer(0.into()),              // disposition
4499      Value::Text("en".to_string()),         // language: tstr
4500      Value::Integer(1.into()),              // partIndex: uint
4501      Value::Integer(1.into()),              // cardinality: single (1)
4502      Value::Text("text/plain".to_string()), // contentType: tstr
4503      Value::Bytes(b"hello world".to_vec()), // content: bstr
4504    ]);
4505
4506    #[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
4507    let mut validator = CBORValidator::new(&cddl, cbor_data, None);
4508    #[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
4509    let mut validator = CBORValidator::new(&cddl, cbor_data, None);
4510    #[cfg(not(feature = "additional-controls"))]
4511    let mut validator = CBORValidator::new(&cddl, cbor_data);
4512    let result = validator.validate();
4513
4514    // This should pass but currently fails
4515    assert!(
4516      result.is_ok(),
4517      "Validation should succeed for SinglePart structure: {:?}",
4518      result
4519    );
4520  }
4521
4522  #[test]
4523  fn extract_cbor() {
4524    use ciborium::value::Value;
4525
4526    let cbor = Value::Float(1.23);
4527    let cddl = cddl_from_str("start = any", true).unwrap();
4528    let cv = CBORValidator::new(&cddl, cbor, None);
4529    assert_eq!(cv.extract_cbor(), Value::Float(1.23));
4530  }
4531
4532  #[test]
4533  fn validate_bstr_size_range() -> std::result::Result<(), Box<dyn std::error::Error>> {
4534    let cddl = indoc!(
4535      r#"
4536        m = { field: bstr .size (16..1000) }
4537        "#
4538    );
4539
4540    let cddl = cddl_from_str(cddl, true)?;
4541
4542    // Test valid byte string length
4543    let valid_bytes = vec![0u8; 100];
4544    let valid_cbor = ciborium::value::Value::Map(vec![(
4545      ciborium::value::Value::Text("field".to_string()),
4546      ciborium::value::Value::Bytes(valid_bytes),
4547    )]);
4548
4549    #[cfg(feature = "additional-controls")]
4550    let mut cv = CBORValidator::new(&cddl, valid_cbor, None);
4551    #[cfg(not(feature = "additional-controls"))]
4552    let mut cv = CBORValidator::new(&cddl, valid_cbor);
4553    assert!(cv.validate().is_ok());
4554
4555    // Test byte string that's too short
4556    let short_bytes = vec![0u8; 10];
4557    let short_cbor = ciborium::value::Value::Map(vec![(
4558      ciborium::value::Value::Text("field".to_string()),
4559      ciborium::value::Value::Bytes(short_bytes),
4560    )]);
4561
4562    #[cfg(feature = "additional-controls")]
4563    let mut cv = CBORValidator::new(&cddl, short_cbor, None);
4564    #[cfg(not(feature = "additional-controls"))]
4565    let mut cv = CBORValidator::new(&cddl, short_cbor);
4566    assert!(cv.validate().is_err());
4567
4568    // Test byte string that's too long
4569    let long_bytes = vec![0u8; 1500];
4570    let long_cbor = ciborium::value::Value::Map(vec![(
4571      ciborium::value::Value::Text("field".to_string()),
4572      ciborium::value::Value::Bytes(long_bytes),
4573    )]);
4574
4575    #[cfg(feature = "additional-controls")]
4576    let mut cv = CBORValidator::new(&cddl, long_cbor, None);
4577    #[cfg(not(feature = "additional-controls"))]
4578    let mut cv = CBORValidator::new(&cddl, long_cbor);
4579    assert!(cv.validate().is_err());
4580
4581    Ok(())
4582  }
4583
4584  #[test]
4585  fn validate_bstr_size_exclusive_range() -> std::result::Result<(), Box<dyn std::error::Error>> {
4586    let cddl = indoc!(
4587      r#"
4588        m = { field: bstr .size (16...1000) }
4589      "#
4590    );
4591
4592    let cddl = cddl_from_str(cddl, true)?;
4593
4594    // Test valid byte string length (17 bytes - should pass)
4595    let valid_bytes = vec![0u8; 17];
4596    let valid_cbor = ciborium::value::Value::Map(vec![(
4597      ciborium::value::Value::Text("field".to_string()),
4598      ciborium::value::Value::Bytes(valid_bytes),
4599    )]);
4600
4601    #[cfg(feature = "additional-controls")]
4602    let mut cv = CBORValidator::new(&cddl, valid_cbor, None);
4603    #[cfg(not(feature = "additional-controls"))]
4604    let mut cv = CBORValidator::new(&cddl, valid_cbor);
4605    assert!(cv.validate().is_ok());
4606
4607    // Test boundary case (16 bytes - should pass)
4608    let boundary_bytes = vec![0u8; 16];
4609    let boundary_cbor = ciborium::value::Value::Map(vec![(
4610      ciborium::value::Value::Text("field".to_string()),
4611      ciborium::value::Value::Bytes(boundary_bytes),
4612    )]);
4613
4614    #[cfg(feature = "additional-controls")]
4615    let mut cv = CBORValidator::new(&cddl, boundary_cbor, None);
4616    #[cfg(not(feature = "additional-controls"))]
4617    let mut cv = CBORValidator::new(&cddl, boundary_cbor);
4618    assert!(cv.validate().is_ok());
4619
4620    Ok(())
4621  }
4622
4623  #[test]
4624  fn validate_nested_cbor() -> std::result::Result<(), Box<dyn std::error::Error>> {
4625    let cddl = indoc!(
4626      r#"
4627        root = {
4628            foo: bstr .cbor bar
4629        }
4630
4631        bar = {
4632            a: text,
4633            b: int,
4634            c: bstr
4635        }
4636        "#
4637    );
4638
4639    let cddl = cddl_from_str(cddl, true)?;
4640
4641    // Create valid inner CBOR map
4642    let inner_cbor = ciborium::cbor!({
4643        "a" => "test",
4644        "b" => -42,
4645        "c" => Value::Bytes(b"bytes".to_vec())
4646    })?;
4647
4648    // Serialize inner CBOR to bytes
4649    let mut inner_bytes = Vec::new();
4650    ciborium::ser::into_writer(&inner_cbor, &mut inner_bytes)?;
4651
4652    // Create outer CBOR map containing the bytes
4653    let outer_cbor = Value::Map(vec![(
4654      Value::Text("foo".to_string()),
4655      Value::Bytes(inner_bytes),
4656    )]);
4657
4658    // Test valid case
4659    #[cfg(feature = "additional-controls")]
4660    let mut cv = CBORValidator::new(&cddl, outer_cbor.clone(), None);
4661    #[cfg(not(feature = "additional-controls"))]
4662    let mut cv = CBORValidator::new(&cddl, outer_cbor.clone());
4663    assert!(cv.validate().is_ok());
4664
4665    // Test invalid inner CBOR (missing required field)
4666    let invalid_inner = ciborium::cbor!({
4667        "a" => "test",
4668        "b" => -42
4669        // missing "c" field
4670    })?;
4671
4672    let mut invalid_bytes = Vec::new();
4673    ciborium::ser::into_writer(&invalid_inner, &mut invalid_bytes)?;
4674
4675    let invalid_outer = Value::Map(vec![(
4676      Value::Text("foo".to_string()),
4677      Value::Bytes(invalid_bytes),
4678    )]);
4679
4680    #[cfg(feature = "additional-controls")]
4681    let mut cv = CBORValidator::new(&cddl, invalid_outer, None);
4682    #[cfg(not(feature = "additional-controls"))]
4683    let mut cv = CBORValidator::new(&cddl, invalid_outer);
4684    assert!(cv.validate().is_err());
4685
4686    Ok(())
4687  }
4688
4689  #[test]
4690  fn validate_nested_cbor_in_array() -> std::result::Result<(), Box<dyn std::error::Error>> {
4691    let cddl = indoc!(
4692      r#"
4693        root = [
4694            foo: bstr .cbor bar
4695        ]
4696
4697        bar = {
4698            a: text,
4699            b: int,
4700            c: bstr
4701        }
4702        "#
4703    );
4704
4705    let cddl = cddl_from_str(cddl, true)?;
4706
4707    // Create valid inner CBOR map
4708    let inner_cbor = ciborium::cbor!({
4709        "a" => "test",
4710        "b" => -42,
4711        "c" => Value::Bytes(b"bytes".to_vec())
4712    })?;
4713
4714    // Serialize inner CBOR to bytes
4715    let mut inner_bytes = Vec::new();
4716    ciborium::ser::into_writer(&inner_cbor, &mut inner_bytes)?;
4717
4718    // Create outer CBOR array containing the bytes
4719    let outer_cbor = Value::Array(vec![Value::Bytes(inner_bytes)]);
4720
4721    // Test valid case
4722    #[cfg(feature = "additional-controls")]
4723    let mut cv = CBORValidator::new(&cddl, outer_cbor.clone(), None);
4724    #[cfg(not(feature = "additional-controls"))]
4725    let mut cv = CBORValidator::new(&cddl, outer_cbor.clone());
4726    cv.validate()?;
4727    assert!(
4728      cv.validate().is_ok(),
4729      // "Nested array with wildcard should validate successfully"
4730    );
4731
4732    // Test invalid inner CBOR (missing required field)
4733    let invalid_inner = ciborium::cbor!({
4734        "a" => "test",
4735        "b" => -42
4736        // missing "c" field
4737    })?;
4738
4739    let mut invalid_bytes = Vec::new();
4740    ciborium::ser::into_writer(&invalid_inner, &mut invalid_bytes)?;
4741
4742    let invalid_outer = Value::Array(vec![Value::Bytes(invalid_bytes)]);
4743
4744    #[cfg(feature = "additional-controls")]
4745    let mut cv = CBORValidator::new(&cddl, invalid_outer, None);
4746    #[cfg(not(feature = "additional-controls"))]
4747    let mut cv = CBORValidator::new(&cddl, invalid_outer);
4748    assert!(cv.validate().is_err());
4749
4750    Ok(())
4751  }
4752
4753  #[test]
4754  fn validate_nested_arrays() -> std::result::Result<(), Box<dyn std::error::Error>> {
4755    // Test with explicit array type
4756    let cddl = indoc!(
4757      r#"
4758        array = [0, [* int]]
4759      "#
4760    );
4761
4762    let cbor = ciborium::cbor!([0, [1, 2]]).unwrap();
4763    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing)?;
4764
4765    let mut cv = CBORValidator::new(&cddl, cbor, None);
4766    cv.validate()?;
4767
4768    // Test with named arrays
4769    let cddl = indoc!(
4770      r#"
4771        root = [0, inner]
4772        inner = [* int]
4773      "#
4774    );
4775
4776    let cbor = ciborium::cbor!([0, [1, 2]]).unwrap();
4777    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing)?;
4778
4779    let mut cv = CBORValidator::new(&cddl, cbor, None);
4780    cv.validate()?;
4781
4782    // Test with explicit array literals
4783    let cddl = indoc!(
4784      r#"
4785        direct = [1, [2, 3]]
4786      "#
4787    );
4788
4789    let cbor = ciborium::cbor!([1, [2, 3]]).unwrap();
4790    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing)?;
4791
4792    let mut cv = CBORValidator::new(&cddl, cbor, None);
4793    cv.validate()?; // If this passes, our fix works
4794
4795    Ok(())
4796  }
4797
4798  #[test]
4799  fn validate_direct_nested_array() -> std::result::Result<(), Box<dyn std::error::Error>> {
4800    // Simplest possible case with explicit literal array
4801    let cddl = indoc!(
4802      r#"
4803        direct = [1, [2, 3]]
4804        "#
4805    );
4806
4807    let cbor = ciborium::cbor!([1, [2, 3]]).unwrap();
4808    let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing)?;
4809
4810    let mut cv = CBORValidator::new(&cddl, cbor, None);
4811
4812    // Print detailed information for debugging
4813    match cv.validate() {
4814      Ok(_) => println!("Validation successful!"),
4815      Err(e) => {
4816        eprintln!("Validation error: {}", e);
4817        if let Error::Validation(errors) = &e {
4818          for (i, err) in errors.iter().enumerate() {
4819            eprintln!("Error {}: {} at {}", i, err.reason, err.cbor_location);
4820          }
4821        }
4822        return Err(e.into());
4823      }
4824    }
4825
4826    Ok(())
4827  }
4828
4829  #[test]
4830  fn validate_recursive_structures() -> std::result::Result<(), Box<dyn std::error::Error>> {
4831    // Test case for issue #248: stack overflow with recursive structures
4832    let cddl = indoc!(
4833      r#"
4834        Tree = {
4835          root: Node
4836        }
4837
4838        Node = [
4839          value: text,
4840          children: [* Node]
4841        ]
4842      "#
4843    );
4844
4845    let cddl = cddl_from_str(cddl, true)?;
4846
4847    // Create a nested tree structure
4848    let cbor = ciborium::cbor!({
4849      "root" => ["value", [["child1", []], ["child2", []]]]
4850    })?;
4851
4852    // This should not cause a stack overflow
4853    #[cfg(feature = "additional-controls")]
4854    let mut cv = CBORValidator::new(&cddl, cbor, None);
4855    #[cfg(not(feature = "additional-controls"))]
4856    let mut cv = CBORValidator::new(&cddl, cbor);
4857
4858    // The validation should complete without stack overflow
4859    let result = cv.validate();
4860
4861    // The structure should be valid
4862    assert!(
4863      result.is_ok(),
4864      "Recursive structure validation should not cause stack overflow"
4865    );
4866
4867    Ok(())
4868  }
4869
4870  #[test]
4871  fn test_issue_221_empty_map_with_extra_keys_cbor() {
4872    // This test reproduces issue #221 for CBOR
4873    // CDDL schema defines an empty map: root = {}
4874    // CBOR has extra keys
4875    // This should FAIL validation but currently passes
4876
4877    let cddl_str = "root = {}";
4878    let cddl = cddl_from_str(cddl_str, true).unwrap();
4879
4880    // Create a CBOR map with extra keys
4881    use ciborium::Value;
4882    let cbor_data = Value::Map(vec![(
4883      Value::Text("x".to_string()),
4884      Value::Text("y".to_string()),
4885    )]);
4886
4887    #[cfg(feature = "additional-controls")]
4888    let mut validator = CBORValidator::new(&cddl, cbor_data, None);
4889    #[cfg(not(feature = "additional-controls"))]
4890    let mut validator = CBORValidator::new(&cddl, cbor_data);
4891
4892    // This should fail but currently passes (the bug)
4893    let result = validator.validate();
4894    assert!(
4895      result.is_err(),
4896      "CBOR validation should fail for extra keys in empty map schema"
4897    );
4898
4899    // Verify the error message is what we expect
4900    if let Err(Error::Validation(errors)) = result {
4901      assert_eq!(errors.len(), 1);
4902      assert!(errors[0].reason.contains("expected empty map"));
4903    } else {
4904      panic!("Expected validation error but got something else");
4905    }
4906  }
4907
4908  #[test]
4909  fn test_empty_map_schema_with_empty_cbor() -> std::result::Result<(), Box<dyn std::error::Error>>
4910  {
4911    // This should pass - empty map schema with empty CBOR map
4912    let cddl_str = "root = {}";
4913    let cddl = cddl_from_str(cddl_str, true)?;
4914
4915    // Create an empty CBOR map
4916    use ciborium::Value;
4917    let cbor_data = Value::Map(vec![]);
4918
4919    #[cfg(feature = "additional-controls")]
4920    let mut validator = CBORValidator::new(&cddl, cbor_data, None);
4921    #[cfg(not(feature = "additional-controls"))]
4922    let mut validator = CBORValidator::new(&cddl, cbor_data);
4923
4924    let result = validator.validate();
4925    assert!(
4926      result.is_ok(),
4927      "CBOR validation should pass for empty map with empty map schema"
4928    );
4929
4930    Ok(())
4931  }
4932
4933  #[test]
4934  fn test_issue_221_reproduce_exact_scenario_cbor(
4935  ) -> std::result::Result<(), Box<dyn std::error::Error>> {
4936    // This test reproduces the exact scenario from issue #221 for CBOR
4937    let cddl_str = "root = {}";
4938    let cddl = cddl_from_str(cddl_str, true)?;
4939
4940    // Create CBOR equivalent of {"x": "y"}
4941    use ciborium::Value;
4942    let cbor_data = Value::Map(vec![(
4943      Value::Text("x".to_string()),
4944      Value::Text("y".to_string()),
4945    )]);
4946
4947    #[cfg(feature = "additional-controls")]
4948    let mut validator = CBORValidator::new(&cddl, cbor_data, None);
4949    #[cfg(not(feature = "additional-controls"))]
4950    let mut validator = CBORValidator::new(&cddl, cbor_data);
4951
4952    let result = validator.validate();
4953
4954    // Before the fix, this would incorrectly pass. After the fix, it should fail.
4955    match result {
4956      Err(Error::Validation(errors)) => {
4957        assert!(!errors.is_empty(), "Should have validation errors");
4958        let error_message = &errors[0].reason;
4959        assert!(
4960          error_message.contains("expected empty map"),
4961          "Error message should indicate expected empty map, got: {}",
4962          error_message
4963        );
4964      }
4965      Ok(_) => panic!(
4966        "Issue #221 bug detected: CBOR validation incorrectly passed for extra keys in empty map"
4967      ),
4968      Err(other) => panic!("Unexpected error type: {:?}", other),
4969    }
4970
4971    Ok(())
4972  }
4973}