#![cfg(feature = "std")]
#![cfg(feature = "cbor")]
#![cfg(not(feature = "lsp"))]
use super::*;
use crate::{
ast::*,
token,
visitor::{self, *},
};
use std::{
borrow::Cow,
collections::HashMap,
convert::TryFrom,
fmt::{self, Write},
};
use chrono::{TimeZone, Utc};
use ciborium::value::Value;
use serde_json;
#[cfg(feature = "additional-controls")]
use crate::validator::control::{
abnf_from_complex_controller, cat_operation, plus_operation, validate_abnf,
};
pub type Result<T> = std::result::Result<(), Error<T>>;
#[derive(Debug)]
pub enum Error<T: std::fmt::Debug> {
Validation(Vec<ValidationError>),
CBORParsing(ciborium::de::Error<T>),
JSONParsing(serde_json::Error),
CDDLParsing(String),
UTF8Parsing(std::str::Utf8Error),
}
impl<T: std::fmt::Debug> fmt::Display for Error<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Validation(errors) => {
let mut error_str = String::new();
for e in errors.iter() {
let _ = writeln!(error_str, "{}", e);
}
write!(f, "{}", error_str)
}
Error::CBORParsing(error) => write!(f, "error parsing cbor: {}", error),
Error::JSONParsing(error) => write!(f, "error parsing json string: {}", error),
Error::CDDLParsing(error) => write!(f, "error parsing CDDL: {}", error),
Error::UTF8Parsing(error) => write!(f, "error parsing utf8: {}", error),
}
}
}
impl<T: std::fmt::Debug + 'static> std::error::Error for Error<T> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::CBORParsing(error) => Some(error),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct ValidationError {
pub reason: String,
pub cddl_location: String,
pub cbor_location: String,
pub is_multi_type_choice: bool,
pub is_multi_group_choice: bool,
pub is_group_to_choice_enum: bool,
pub type_group_name_entry: Option<String>,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut error_str = String::from("error validating");
if self.is_multi_group_choice {
error_str.push_str(" group choice");
}
if self.is_multi_type_choice {
error_str.push_str(" type choice");
}
if self.is_group_to_choice_enum {
error_str.push_str(" type choice in group to choice enumeration");
}
if let Some(entry) = &self.type_group_name_entry {
let _ = write!(error_str, " group entry associated with rule \"{}\"", entry);
}
write!(
f,
"{} at cbor location {}: {}",
error_str, self.cbor_location, self.reason
)
}
}
impl std::error::Error for ValidationError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl<T: std::fmt::Debug> Error<T> {
fn from_validator(cv: &CBORValidator, reason: String) -> Self {
Error::Validation(vec![ValidationError {
cddl_location: cv.cddl_location.clone(),
cbor_location: cv.cbor_location.clone(),
reason,
is_multi_type_choice: cv.is_multi_type_choice,
is_group_to_choice_enum: cv.is_group_to_choice_enum,
type_group_name_entry: cv.type_group_name_entry.map(|e| e.to_string()),
is_multi_group_choice: cv.is_multi_group_choice,
}])
}
}
#[derive(Clone)]
pub struct CBORValidator<'a> {
cddl: &'a CDDL<'a>,
cbor: Value,
errors: Vec<ValidationError>,
cddl_location: String,
cbor_location: String,
occurrence: Option<Occur>,
group_entry_idx: Option<usize>,
object_value: Option<Value>,
is_member_key: bool,
is_cut_present: bool,
cut_value: Option<Type1<'a>>,
eval_generic_rule: Option<&'a str>,
generic_rules: Vec<GenericRule<'a>>,
ctrl: Option<token::ControlOperator>,
is_group_to_choice_enum: bool,
is_multi_type_choice: bool,
is_multi_group_choice: bool,
type_group_name_entry: Option<&'a str>,
advance_to_next_entry: bool,
is_ctrl_map_equality: bool,
entry_counts: Option<Vec<EntryCount>>,
validated_keys: Option<Vec<Value>>,
values_to_validate: Option<Vec<Value>>,
validating_value: bool,
valid_array_items: Option<Vec<usize>>,
array_errors: Option<HashMap<usize, Vec<ValidationError>>>,
is_colon_shortcut_present: bool,
is_root: bool,
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "additional-controls")]
enabled_features: Option<&'a [&'a str]>,
#[cfg(target_arch = "wasm32")]
#[cfg(feature = "additional-controls")]
enabled_features: Option<Box<[JsValue]>>,
#[cfg(feature = "additional-controls")]
has_feature_errors: bool,
#[cfg(feature = "additional-controls")]
disabled_features: Option<Vec<String>>,
}
#[derive(Clone, Debug)]
struct GenericRule<'a> {
name: &'a str,
params: Vec<&'a str>,
args: Vec<Type1<'a>>,
}
impl<'a> CBORValidator<'a> {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "additional-controls")]
pub fn new(cddl: &'a CDDL<'a>, cbor: Value, enabled_features: Option<&'a [&'a str]>) -> Self {
CBORValidator {
cddl,
cbor,
errors: Vec::default(),
cddl_location: String::new(),
cbor_location: String::new(),
occurrence: None,
group_entry_idx: None,
object_value: None,
is_member_key: false,
is_cut_present: false,
cut_value: None,
eval_generic_rule: None,
generic_rules: Vec::new(),
ctrl: None,
is_group_to_choice_enum: false,
is_multi_type_choice: false,
is_multi_group_choice: false,
type_group_name_entry: None,
advance_to_next_entry: false,
is_ctrl_map_equality: false,
entry_counts: None,
validated_keys: None,
values_to_validate: None,
validating_value: false,
valid_array_items: None,
array_errors: None,
is_colon_shortcut_present: false,
is_root: false,
enabled_features,
has_feature_errors: false,
disabled_features: None,
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "additional-controls"))]
pub fn new(cddl: &'a CDDL<'a>, cbor: Value) -> Self {
CBORValidator {
cddl,
cbor,
errors: Vec::default(),
cddl_location: String::new(),
cbor_location: String::new(),
occurrence: None,
group_entry_idx: None,
object_value: None,
is_member_key: false,
is_cut_present: false,
cut_value: None,
eval_generic_rule: None,
generic_rules: Vec::new(),
ctrl: None,
is_group_to_choice_enum: false,
is_multi_type_choice: false,
is_multi_group_choice: false,
type_group_name_entry: None,
advance_to_next_entry: false,
is_ctrl_map_equality: false,
entry_counts: None,
validated_keys: None,
values_to_validate: None,
validating_value: false,
valid_array_items: None,
array_errors: None,
is_colon_shortcut_present: false,
is_root: false,
}
}
#[cfg(target_arch = "wasm32")]
#[cfg(feature = "additional-controls")]
pub fn new(cddl: &'a CDDL<'a>, cbor: Value, enabled_features: Option<Box<[JsValue]>>) -> Self {
CBORValidator {
cddl,
cbor,
errors: Vec::default(),
cddl_location: String::new(),
cbor_location: String::new(),
occurrence: None,
group_entry_idx: None,
object_value: None,
is_member_key: false,
is_cut_present: false,
cut_value: None,
eval_generic_rule: None,
generic_rules: Vec::new(),
ctrl: None,
is_group_to_choice_enum: false,
is_multi_type_choice: false,
is_multi_group_choice: false,
type_group_name_entry: None,
advance_to_next_entry: false,
is_ctrl_map_equality: false,
entry_counts: None,
validated_keys: None,
values_to_validate: None,
validating_value: false,
valid_array_items: None,
array_errors: None,
is_colon_shortcut_present: false,
is_root: false,
enabled_features,
has_feature_errors: false,
disabled_features: None,
}
}
#[cfg(target_arch = "wasm32")]
#[cfg(not(feature = "additional-controls"))]
pub fn new(cddl: &'a CDDL<'a>, cbor: Value) -> Self {
CBORValidator {
cddl,
cbor,
errors: Vec::default(),
cddl_location: String::new(),
cbor_location: String::new(),
occurrence: None,
group_entry_idx: None,
object_value: None,
is_member_key: false,
is_cut_present: false,
cut_value: None,
eval_generic_rule: None,
generic_rules: Vec::new(),
ctrl: None,
is_group_to_choice_enum: false,
is_multi_type_choice: false,
is_multi_group_choice: false,
type_group_name_entry: None,
advance_to_next_entry: false,
is_ctrl_map_equality: false,
entry_counts: None,
validated_keys: None,
values_to_validate: None,
validating_value: false,
valid_array_items: None,
array_errors: None,
is_colon_shortcut_present: false,
is_root: false,
}
}
fn validate_array_items<T: std::fmt::Debug + 'static>(
&mut self,
token: &ArrayItemToken,
) -> visitor::Result<Error<T>>
where
cbor::Error<T>: From<cbor::Error<std::io::Error>>,
{
if let Value::Array(a) = &self.cbor {
if self.is_member_key {
return Ok(());
}
match validate_array_occurrence(
self.occurrence.as_ref(),
self.entry_counts.as_ref().map(|ec| &ec[..]),
a,
) {
Ok((iter_items, allow_empty_array)) => {
if iter_items {
for (idx, v) in a.iter().enumerate() {
if let Some(indices) = &self.valid_array_items {
if self.is_multi_type_choice && indices.contains(&idx) {
continue;
}
}
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, v.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.ctrl = self.ctrl;
cv.is_multi_type_choice = self.is_multi_type_choice;
let _ = write!(cv.cbor_location, "{}/{}", self.cbor_location, idx);
match token {
ArrayItemToken::Value(value) => cv.visit_value(value)?,
ArrayItemToken::Range(lower, upper, is_inclusive) => {
cv.visit_range(lower, upper, *is_inclusive)?
}
ArrayItemToken::Group(group) => cv.visit_group(group)?,
ArrayItemToken::Identifier(ident) => cv.visit_identifier(ident)?,
}
if self.is_multi_type_choice && cv.errors.is_empty() {
if let Some(indices) = &mut self.valid_array_items {
indices.push(idx);
} else {
self.valid_array_items = Some(vec![idx]);
}
continue;
}
if let Some(errors) = &mut self.array_errors {
if let Some(error) = errors.get_mut(&idx) {
error.append(&mut cv.errors);
} else {
errors.insert(idx, cv.errors);
}
} else {
let mut errors = HashMap::new();
errors.insert(idx, cv.errors);
self.array_errors = Some(errors)
}
}
} else {
let idx = if !self.is_multi_type_choice {
self.group_entry_idx.take()
} else {
self.group_entry_idx
};
if let Some(idx) = idx {
if let Some(v) = a.get(idx) {
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv =
CBORValidator::new(self.cddl, v.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, v.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.ctrl = self.ctrl;
let _ = write!(cv.cbor_location, "{}/{}", self.cbor_location, idx);
match token {
ArrayItemToken::Value(value) => cv.visit_value(value)?,
ArrayItemToken::Range(lower, upper, is_inclusive) => {
cv.visit_range(lower, upper, *is_inclusive)?
}
ArrayItemToken::Group(group) => cv.visit_group(group)?,
ArrayItemToken::Identifier(ident) => cv.visit_identifier(ident)?,
}
self.errors.append(&mut cv.errors);
} else if !allow_empty_array {
self.add_error(token.error_msg(Some(idx)));
}
} else if !self.is_multi_type_choice {
self.add_error(format!("{}, got {:?}", token.error_msg(None), self.cbor));
}
}
}
Err(errors) => {
for e in errors.into_iter() {
self.add_error(e);
}
}
}
}
Ok(())
}
}
impl<'a, 'b, T: std::fmt::Debug + 'static> Validator<'a, 'b, cbor::Error<T>> for CBORValidator<'a>
where
cbor::Error<T>: From<cbor::Error<std::io::Error>>,
{
fn validate(&mut self) -> std::result::Result<(), cbor::Error<T>> {
for r in self.cddl.rules.iter() {
if let Rule::Type { rule, .. } = r {
if rule.generic_params.is_none() {
self.is_root = true;
self.visit_type_rule(rule)?;
self.is_root = false;
break;
}
}
}
if !self.errors.is_empty() {
return Err(Error::Validation(self.errors.clone()));
}
Ok(())
}
fn add_error(&mut self, reason: String) {
self.errors.push(ValidationError {
reason,
cddl_location: self.cddl_location.clone(),
cbor_location: self.cbor_location.clone(),
is_multi_type_choice: self.is_multi_type_choice,
is_multi_group_choice: self.is_multi_group_choice,
is_group_to_choice_enum: self.is_group_to_choice_enum,
type_group_name_entry: self.type_group_name_entry.map(|e| e.to_string()),
});
}
}
impl<'a, 'b, T: std::fmt::Debug + 'static> Visitor<'a, 'b, Error<T>> for CBORValidator<'a>
where
cbor::Error<T>: From<cbor::Error<std::io::Error>>,
{
fn visit_type_rule(&mut self, tr: &TypeRule<'a>) -> visitor::Result<Error<T>> {
if let Some(gp) = &tr.generic_params {
if let Some(gr) = self
.generic_rules
.iter_mut()
.find(|r| r.name == tr.name.ident)
{
gr.params = gp.params.iter().map(|p| p.param.ident).collect();
} else {
self.generic_rules.push(GenericRule {
name: tr.name.ident,
params: gp.params.iter().map(|p| p.param.ident).collect(),
args: vec![],
});
}
}
let type_choice_alternates = type_choice_alternates_from_ident(self.cddl, &tr.name);
if !type_choice_alternates.is_empty() {
self.is_multi_type_choice = true;
}
let error_count = self.errors.len();
for t in type_choice_alternates {
let cur_errors = self.errors.len();
self.visit_type(t)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
return Ok(());
}
}
self.visit_type(&tr.value)
}
fn visit_group_rule(&mut self, gr: &GroupRule<'a>) -> visitor::Result<Error<T>> {
if let Some(gp) = &gr.generic_params {
if let Some(gr) = self
.generic_rules
.iter_mut()
.find(|r| r.name == gr.name.ident)
{
gr.params = gp.params.iter().map(|p| p.param.ident).collect();
} else {
self.generic_rules.push(GenericRule {
name: gr.name.ident,
params: gp.params.iter().map(|p| p.param.ident).collect(),
args: vec![],
});
}
}
let group_choice_alternates = group_choice_alternates_from_ident(self.cddl, &gr.name);
if !group_choice_alternates.is_empty() {
self.is_multi_group_choice = true;
}
let error_count = self.errors.len();
for ge in group_choice_alternates {
let cur_errors = self.errors.len();
self.visit_group_entry(ge)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
return Ok(());
}
}
self.visit_group_entry(&gr.entry)
}
fn visit_type(&mut self, t: &Type<'a>) -> visitor::Result<Error<T>> {
if t.type_choices.len() > 1 {
self.is_multi_type_choice = true;
}
let initial_error_count = self.errors.len();
for type_choice in t.type_choices.iter() {
if matches!(self.cbor, Value::Array(_)) {
let error_count = self.errors.len();
self.visit_type_choice(type_choice)?;
#[cfg(feature = "additional-controls")]
if self.errors.len() == error_count
&& !self.has_feature_errors
&& self.disabled_features.is_none()
{
let type_choice_error_count = self.errors.len() - initial_error_count;
if type_choice_error_count > 0 {
for _ in 0..type_choice_error_count {
self.errors.pop();
}
}
}
#[cfg(not(feature = "additional-controls"))]
if self.errors.len() == error_count {
let type_choice_error_count = self.errors.len() - initial_error_count;
if type_choice_error_count > 0 {
for _ in 0..type_choice_error_count {
self.errors.pop();
}
}
}
continue;
}
let error_count = self.errors.len();
self.visit_type_choice(type_choice)?;
#[cfg(feature = "additional-controls")]
if self.errors.len() == error_count
&& !self.has_feature_errors
&& self.disabled_features.is_none()
{
let type_choice_error_count = self.errors.len() - initial_error_count;
if type_choice_error_count > 0 {
for _ in 0..type_choice_error_count {
self.errors.pop();
}
}
return Ok(());
}
#[cfg(not(feature = "additional-controls"))]
if self.errors.len() == error_count {
let type_choice_error_count = self.errors.len() - initial_error_count;
if type_choice_error_count > 0 {
for _ in 0..type_choice_error_count {
self.errors.pop();
}
}
return Ok(());
}
}
Ok(())
}
fn visit_group(&mut self, g: &Group<'a>) -> visitor::Result<Error<T>> {
if g.group_choices.len() > 1 {
self.is_multi_group_choice = true;
}
if self.is_ctrl_map_equality {
if let Some(t) = &self.ctrl {
if let Value::Map(m) = &self.cbor {
let entry_counts = entry_counts_from_group(self.cddl, g);
let len = m.len();
if let ControlOperator::EQ | ControlOperator::NE = t {
if !validate_entry_count(&entry_counts, len) {
for ec in entry_counts.iter() {
if let Some(occur) = &ec.entry_occurrence {
self.add_error(format!(
"expected array with length per occurrence {}",
occur,
));
} else {
self.add_error(format!(
"expected array with length {}, got {}",
ec.count, len
));
}
}
return Ok(());
}
}
}
}
}
self.is_ctrl_map_equality = false;
let initial_error_count = self.errors.len();
for group_choice in g.group_choices.iter() {
let error_count = self.errors.len();
self.visit_group_choice(group_choice)?;
if self.errors.len() == error_count {
let group_choice_error_count = self.errors.len() - initial_error_count;
if group_choice_error_count > 0 {
for _ in 0..group_choice_error_count {
self.errors.pop();
}
}
return Ok(());
}
}
Ok(())
}
fn visit_group_choice(&mut self, gc: &GroupChoice<'a>) -> visitor::Result<Error<T>> {
if self.is_group_to_choice_enum {
let initial_error_count = self.errors.len();
for tc in type_choices_from_group_choice(self.cddl, gc).iter() {
let error_count = self.errors.len();
self.visit_type_choice(tc)?;
if self.errors.len() == error_count {
let type_choice_error_count = self.errors.len() - initial_error_count;
if type_choice_error_count > 0 {
for _ in 0..type_choice_error_count {
self.errors.pop();
}
}
return Ok(());
}
}
return Ok(());
}
for (idx, ge) in gc.group_entries.iter().enumerate() {
self.group_entry_idx = Some(idx);
self.visit_group_entry(&ge.0)?;
}
Ok(())
}
fn visit_range(
&mut self,
lower: &Type2,
upper: &Type2,
is_inclusive: bool,
) -> visitor::Result<Error<T>> {
if let Value::Array(_) = &self.cbor {
return self.validate_array_items(&ArrayItemToken::Range(lower, upper, is_inclusive));
}
match lower {
Type2::IntValue { value: l, .. } => match upper {
Type2::IntValue { value: u, .. } => {
let error_str = if is_inclusive {
format!(
"expected integer to be in range {} <= value <= {}, got {:?}",
l, u, self.cbor
)
} else {
format!(
"expected integer to be in range {} < value < {}, got {:?}",
l, u, self.cbor
)
};
match &self.cbor {
Value::Integer(i) => {
if is_inclusive {
if i128::from(*i) < *l as i128 || i128::from(*i) > *u as i128 {
self.add_error(error_str);
} else {
return Ok(());
}
} else if i128::from(*i) <= *l as i128 || i128::from(*i) >= *u as i128 {
self.add_error(error_str);
return Ok(());
} else {
return Ok(());
}
}
_ => {
self.add_error(error_str);
return Ok(());
}
}
}
Type2::UintValue { value: u, .. } => {
let error_str = if is_inclusive {
format!(
"expected integer to be in range {} <= value <= {}, got {:?}",
l, u, self.cbor
)
} else {
format!(
"expected integer to be in range {} < value < {}, got {:?}",
l, u, self.cbor
)
};
match &self.cbor {
Value::Integer(i) => {
if is_inclusive {
if i128::from(*i) < *l as i128 || i128::from(*i) > *u as i128 {
self.add_error(error_str);
} else {
return Ok(());
}
} else if i128::from(*i) <= *l as i128 || i128::from(*i) >= *u as i128 {
self.add_error(error_str);
return Ok(());
} else {
return Ok(());
}
}
_ => {
self.add_error(error_str);
return Ok(());
}
}
}
_ => {
self.add_error(format!(
"invalid cddl range. upper value must be an integer type. got {}",
upper
));
return Ok(());
}
},
Type2::UintValue { value: l, .. } => match upper {
Type2::UintValue { value: u, .. } => {
let error_str = if is_inclusive {
format!(
"expected uint to be in range {} <= value <= {}, got {:?}",
l, u, self.cbor
)
} else {
format!(
"expected uint to be in range {} < value < {}, got {:?}",
l, u, self.cbor
)
};
match &self.cbor {
Value::Integer(i) => {
if is_inclusive {
if i128::from(*i) < *l as i128 || i128::from(*i) > *u as i128 {
self.add_error(error_str);
} else {
return Ok(());
}
} else if i128::from(*i) <= *l as i128 || i128::from(*i) >= *u as i128 {
self.add_error(error_str);
return Ok(());
} else {
return Ok(());
}
}
Value::Text(s) => match self.ctrl {
Some(ControlOperator::SIZE) => {
let len = s.len();
let s = s.clone();
if is_inclusive {
if s.len() < *l || s.len() > *u {
self.add_error(format!(
"expected \"{}\" string length to be in the range {} <= value <= {}, got {}",
s, l, u, len
));
}
return Ok(());
} else if s.len() <= *l || s.len() >= *u {
self.add_error(format!(
"expected \"{}\" string length to be in the range {} < value < {}, got {}",
s, l, u, len
));
return Ok(());
}
}
_ => {
self.add_error("string value cannot be validated against a range without the .size control operator".to_string());
return Ok(());
}
},
_ => {
self.add_error(error_str);
return Ok(());
}
}
}
_ => {
self.add_error(format!(
"invalid cddl range. upper value must be a uint type. got {}",
upper
));
return Ok(());
}
},
Type2::FloatValue { value: l, .. } => match upper {
Type2::FloatValue { value: u, .. } => {
let error_str = if is_inclusive {
format!(
"expected float to be in range {} <= value <= {}, got {:?}",
l, u, self.cbor
)
} else {
format!(
"expected float to be in range {} < value < {}, got {:?}",
l, u, self.cbor
)
};
match &self.cbor {
Value::Float(f) => {
if is_inclusive {
if *f < *l || *f > *u {
self.add_error(error_str);
} else {
return Ok(());
}
} else if *f <= *l || *f >= *u {
self.add_error(error_str);
return Ok(());
} else {
return Ok(());
}
}
_ => {
self.add_error(error_str);
return Ok(());
}
}
}
_ => {
self.add_error(format!(
"invalid cddl range. upper value must be a float type. got {}",
upper
));
return Ok(());
}
},
_ => {
self.add_error(
"invalid cddl range. upper and lower values must be either integers or floats"
.to_string(),
);
return Ok(());
}
}
Ok(())
}
fn visit_control_operator(
&mut self,
target: &Type2<'a>,
ctrl: ControlOperator,
controller: &Type2<'a>,
) -> visitor::Result<Error<T>> {
if let Type2::Typename {
ident: target_ident,
..
} = target
{
if let Type2::Typename {
ident: controller_ident,
..
} = controller
{
if let Some(name) = self.eval_generic_rule {
if let Some(gr) = self
.generic_rules
.iter()
.cloned()
.find(|gr| gr.name == name)
{
for (idx, gp) in gr.params.iter().enumerate() {
if let Some(arg) = gr.args.get(idx) {
if *gp == target_ident.ident {
let t2 = Type2::from(arg.clone());
if *gp == controller_ident.ident {
return self.visit_control_operator(&t2, ctrl, &t2);
}
return self.visit_control_operator(&arg.type2, ctrl, controller);
}
}
}
}
}
}
if let Some(name) = self.eval_generic_rule {
if let Some(gr) = self
.generic_rules
.iter()
.cloned()
.find(|gr| gr.name == name)
{
for (idx, gp) in gr.params.iter().enumerate() {
if let Some(arg) = gr.args.get(idx) {
if *gp == target_ident.ident {
let t2 = Type2::from(arg.clone());
return self.visit_control_operator(&t2, ctrl, controller);
}
}
}
}
}
}
match ctrl {
ControlOperator::EQ => {
match target {
Type2::Typename { ident, .. } => {
if is_ident_string_data_type(self.cddl, ident)
|| is_ident_numeric_data_type(self.cddl, ident)
{
return self.visit_type2(controller);
}
}
Type2::Array { group, .. } => {
if let Value::Array(_) = &self.cbor {
let entry_counts = entry_counts_from_group(self.cddl, group);
self.entry_counts = Some(entry_counts);
self.visit_type2(controller)?;
self.entry_counts = None;
return Ok(());
}
}
Type2::Map { .. } => {
if let Value::Map(_) = &self.cbor {
self.ctrl = Some(ctrl);
self.is_ctrl_map_equality = true;
self.visit_type2(controller)?;
self.ctrl = None;
self.is_ctrl_map_equality = false;
return Ok(());
}
}
_ => self.add_error(format!(
"target for .eq operator must be a string, numerical, array or map data type, got {}",
target
)),
}
Ok(())
}
ControlOperator::NE => {
match target {
Type2::Typename { ident, .. } => {
if is_ident_string_data_type(self.cddl, ident)
|| is_ident_numeric_data_type(self.cddl, ident)
{
self.ctrl = Some(ctrl);
self.visit_type2(controller)?;
self.ctrl = None;
return Ok(());
}
}
Type2::Array { .. } => {
if let Value::Array(_) = &self.cbor {
self.ctrl = Some(ctrl);
self.visit_type2(controller)?;
self.ctrl = None;
return Ok(());
}
}
Type2::Map { .. } => {
if let Value::Map(_) = &self.cbor {
self.ctrl = Some(ctrl);
self.is_ctrl_map_equality = true;
self.visit_type2(controller)?;
self.ctrl = None;
self.is_ctrl_map_equality = false;
return Ok(());
}
}
_ => self.add_error(format!(
"target for .ne operator must be a string, numerical, array or map data type, got {}",
target
)),
}
Ok(())
}
ControlOperator::LT | ControlOperator::GT | ControlOperator::GE | ControlOperator::LE => {
match target {
Type2::Typename { ident, .. } if is_ident_numeric_data_type(self.cddl, ident) => {
self.ctrl = Some(ctrl);
self.visit_type2(controller)?;
self.ctrl = None;
Ok(())
}
_ => {
self.add_error(format!(
"target for .lt, .gt, .ge or .le operator must be a numerical data type, got {}",
target
));
Ok(())
}
}
}
ControlOperator::SIZE => match target {
Type2::Typename { ident, .. }
if is_ident_string_data_type(self.cddl, ident)
|| is_ident_uint_data_type(self.cddl, ident)
|| is_ident_byte_string_data_type(self.cddl, ident) =>
{
self.ctrl = Some(ctrl);
self.visit_type2(controller)?;
self.ctrl = None;
Ok(())
}
_ => {
self.add_error(format!(
"target for .size must a string or uint data type, got {}",
target
));
Ok(())
}
},
ControlOperator::AND => {
self.ctrl = Some(ctrl);
self.visit_type2(target)?;
self.visit_type2(controller)?;
self.ctrl = None;
Ok(())
}
ControlOperator::WITHIN => {
self.ctrl = Some(ctrl);
let error_count = self.errors.len();
self.visit_type2(target)?;
let no_errors = self.errors.len() == error_count;
self.visit_type2(controller)?;
if no_errors && self.errors.len() > error_count {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
self.add_error(format!(
"expected type {} .within type {}, got {:?}",
target, controller, self.cbor,
));
}
self.ctrl = None;
Ok(())
}
ControlOperator::DEFAULT => {
self.ctrl = Some(ctrl);
let error_count = self.errors.len();
self.visit_type2(target)?;
if self.errors.len() != error_count {
#[cfg(feature = "ast-span")]
if let Some(Occur::Optional { .. }) = self.occurrence.take() {
self.add_error(format!(
"expected default value {}, got {:?}",
controller, self.cbor
));
}
#[cfg(not(feature = "ast-span"))]
if let Some(Occur::Optional {}) = self.occurrence.take() {
self.add_error(format!(
"expected default value {}, got {:?}",
controller, self.cbor
));
}
}
self.ctrl = None;
Ok(())
}
ControlOperator::REGEXP | ControlOperator::PCRE => {
self.ctrl = Some(ctrl);
match target {
Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
match self.cbor {
Value::Text(_) | Value::Array(_) => self.visit_type2(controller)?,
_ => self.add_error(format!(
".regexp/.pcre control can only be matched against CBOR string, got {:?}",
self.cbor
)),
}
}
_ => self.add_error(format!(
".regexp/.pcre control can only be matched against string data type, got {}",
target
)),
}
self.ctrl = None;
Ok(())
}
ControlOperator::CBOR | ControlOperator::CBORSEQ => {
self.ctrl = Some(ctrl);
match target {
Type2::Typename { ident, .. } if is_ident_byte_string_data_type(self.cddl, ident) => {
match &self.cbor {
Value::Bytes(_) | Value::Array(_) => self.visit_type2(controller)?,
_ => self.add_error(format!(
"{} control can only be matched against a CBOR byte string, got {:?}",
ctrl, self.cbor
)),
}
}
_ => self.add_error(format!(
".cbor control can only be matched against a byte string data type, got {}",
target
)),
}
self.ctrl = None;
Ok(())
}
ControlOperator::BITS => {
self.ctrl = Some(ctrl);
match target {
Type2::Typename { ident, .. }
if is_ident_byte_string_data_type(self.cddl, ident)
|| is_ident_uint_data_type(self.cddl, ident) =>
{
match &self.cbor {
Value::Bytes(_) | Value::Array(_) => self.visit_type2(controller)?,
Value::Integer(i) if i128::from(*i) >= 0i128 => self.visit_type2(controller)?,
_ => self.add_error(format!(
"{} control can only be matched against a CBOR byte string or uint, got {:?}",
ctrl, self.cbor,
)),
}
}
_ => self.add_error(format!(
".bits control can only be matched against a byte string data type, got {}",
target
)),
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
ControlOperator::CAT => {
self.ctrl = Some(ctrl);
match cat_operation(self.cddl, target, controller, false) {
Ok(values) => {
let error_count = self.errors.len();
for v in values.iter() {
let cur_errors = self.errors.len();
self.visit_type2(v)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
break;
}
}
}
Err(e) => self.add_error(e),
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
ControlOperator::DET => {
self.ctrl = Some(ctrl);
match cat_operation(self.cddl, target, controller, true) {
Ok(values) => {
let error_count = self.errors.len();
for v in values.iter() {
let cur_errors = self.errors.len();
self.visit_type2(v)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
break;
}
}
}
Err(e) => self.add_error(e),
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
ControlOperator::PLUS => {
self.ctrl = Some(ctrl);
match plus_operation(self.cddl, target, controller) {
Ok(values) => {
let error_count = self.errors.len();
for v in values.iter() {
let cur_errors = self.errors.len();
self.visit_type2(v)?;
self.visit_type2(v)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
break;
}
}
}
Err(e) => self.add_error(e),
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
ControlOperator::ABNF => {
self.ctrl = Some(ctrl);
match target {
Type2::Typename { ident, .. } if is_ident_string_data_type(self.cddl, ident) => {
match self.cbor {
Value::Text(_) | Value::Array(_) => {
if let Type2::ParenthesizedType { pt, .. } = controller {
match abnf_from_complex_controller(self.cddl, pt) {
Ok(values) => {
let error_count = self.errors.len();
for v in values.iter() {
let cur_errors = self.errors.len();
self.visit_type2(v)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
break;
}
}
}
Err(e) => self.add_error(e),
}
} else {
self.visit_type2(controller)?
}
}
_ => self.add_error(format!(
".abnf control can only be matched against a cbor string, got {:?}",
self.cbor,
)),
}
}
_ => self.add_error(format!(
".abnf can only be matched against string data type, got {}",
target,
)),
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
ControlOperator::ABNFB => {
self.ctrl = Some(ctrl);
match target {
Type2::Typename { ident, .. } if is_ident_byte_string_data_type(self.cddl, ident) => {
match self.cbor {
Value::Bytes(_) | Value::Array(_) => {
if let Type2::ParenthesizedType { pt, .. } = controller {
match abnf_from_complex_controller(self.cddl, pt) {
Ok(values) => {
let error_count = self.errors.len();
for v in values.iter() {
let cur_errors = self.errors.len();
self.visit_type2(v)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
break;
}
}
}
Err(e) => self.add_error(e),
}
} else {
self.visit_type2(controller)?
}
}
_ => self.add_error(format!(
".abnfb control can only be matched against cbor bytes, got {:?}",
self.cbor,
)),
}
}
_ => self.add_error(format!(
".abnfb can only be matched against byte string target data type, got {}",
target,
)),
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
#[cfg(not(target_arch = "wasm32"))]
ControlOperator::FEATURE => {
self.ctrl = Some(ctrl);
if let Some(ef) = self.enabled_features {
let tv = text_value_from_type2(self.cddl, controller);
if let Some(Type2::TextValue { value, .. }) = tv {
if ef.contains(&&**value) {
let err_count = self.errors.len();
self.visit_type2(target)?;
if self.errors.len() > err_count {
self.has_feature_errors = true;
}
self.ctrl = None;
} else {
self
.disabled_features
.get_or_insert(vec![value.to_string()])
.push(value.to_string());
}
} else if let Some(Type2::UTF8ByteString { value, .. }) = tv {
let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?;
if ef.contains(&value) {
let err_count = self.errors.len();
self.visit_type2(target)?;
if self.errors.len() > err_count {
self.has_feature_errors = true;
}
self.ctrl = None;
} else {
self
.disabled_features
.get_or_insert(vec![value.to_string()])
.push(value.to_string());
}
}
}
self.ctrl = None;
Ok(())
}
#[cfg(feature = "additional-controls")]
#[cfg(target_arch = "wasm32")]
ControlOperator::FEATURE => {
self.ctrl = Some(ctrl);
if let Some(ef) = &self.enabled_features {
let tv = text_value_from_type2(self.cddl, controller);
if let Some(Type2::TextValue { value, .. }) = tv {
if ef.contains(&JsValue::from(value.as_ref())) {
let err_count = self.errors.len();
self.visit_type2(target)?;
if self.errors.len() > err_count {
self.has_feature_errors = true;
}
self.ctrl = None;
} else {
self
.disabled_features
.get_or_insert(vec![value.to_string()])
.push(value.to_string());
}
} else if let Some(Type2::UTF8ByteString { value, .. }) = tv {
let value = std::str::from_utf8(value).map_err(Error::UTF8Parsing)?;
if ef.contains(&JsValue::from(value)) {
let err_count = self.errors.len();
self.visit_type2(target)?;
if self.errors.len() > err_count {
self.has_feature_errors = true;
}
self.ctrl = None;
} else {
self
.disabled_features
.get_or_insert(vec![value.to_string()])
.push(value.to_string());
}
}
}
self.ctrl = None;
Ok(())
}
}
}
fn visit_type2(&mut self, t2: &Type2<'a>) -> visitor::Result<Error<T>> {
if matches!(self.ctrl, Some(ControlOperator::CBOR)) {
if let Value::Bytes(b) = &self.cbor {
let value = ciborium::de::from_reader(&b[..]);
match value {
Ok(value) => {
let current_location = self.cbor_location.clone();
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(self.cddl, value, self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, value, self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, value);
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.visit_type2(t2)?;
if cv.errors.is_empty() {
self.cbor_location = current_location;
return Ok(());
}
self.errors.append(&mut cv.errors);
}
Err(e) => {
self.add_error(format!("error decoding embedded CBOR, {}", e));
}
}
}
return Ok(());
} else if matches!(self.ctrl, Some(ControlOperator::CBORSEQ)) {
if let Value::Bytes(b) = &self.cbor {
let value = ciborium::de::from_reader(&b[..]);
match value {
Ok(Value::Array(_)) => {
let current_location = self.cbor_location.clone();
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(
self.cddl,
value.unwrap_or(Value::Null),
self.enabled_features.clone(),
);
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(
self.cddl,
value.unwrap_or(Value::Null),
self.enabled_features,
);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, value.unwrap_or(Value::Null));
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.visit_type2(t2)?;
if cv.errors.is_empty() {
self.cbor_location = current_location;
return Ok(());
}
self.errors.append(&mut cv.errors);
}
Err(e) => {
self.add_error(format!("error decoding embedded CBOR, {}", e));
}
Ok(v) => self.add_error(format!(
"embedded CBOR must be a CBOR sequence, got {:?}",
v
)),
}
}
return Ok(());
}
match t2 {
Type2::TextValue { value, .. } => self.visit_value(&token::Value::TEXT(value.clone())),
Type2::Map { group, .. } => match &self.cbor {
Value::Map(m) => {
if self.is_member_key {
let current_location = self.cbor_location.clone();
for (k, v) in m.iter() {
#[cfg(feature = "additional-controls")]
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, k.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.visit_type2(t2)?;
if cv.errors.is_empty() {
self.object_value = Some(v.clone());
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.cbor_location = current_location;
return Ok(());
}
self.errors.append(&mut cv.errors);
}
return Ok(());
}
#[allow(clippy::needless_collect)]
let m = m.iter().map(|entry| entry.0.clone()).collect::<Vec<_>>();
self.visit_group(group)?;
if self.values_to_validate.is_none() {
for k in m.into_iter() {
if let Some(keys) = &self.validated_keys {
if !keys.contains(&k) {
self.add_error(format!("unexpected key {:?}", k));
}
}
}
}
self.is_cut_present = false;
self.cut_value = None;
Ok(())
}
Value::Array(_) => self.validate_array_items(&ArrayItemToken::Group(group)),
_ => {
self.add_error(format!("expected map object {}, got {:?}", t2, self.cbor));
Ok(())
}
},
Type2::Array { group, .. } => match &self.cbor {
Value::Array(a) => {
if group.group_choices.len() == 1
&& group.group_choices[0].group_entries.is_empty()
&& !a.is_empty()
&& !matches!(
self.ctrl,
Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
)
{
self.add_error(format!("expected empty array, got {:?}", self.cbor));
return Ok(());
}
let entry_counts = entry_counts_from_group(self.cddl, group);
self.entry_counts = Some(entry_counts);
self.visit_group(group)?;
self.entry_counts = None;
if let Some(errors) = &mut self.array_errors {
if let Some(indices) = &self.valid_array_items {
for idx in indices.iter() {
errors.remove(idx);
}
}
for error in errors.values_mut() {
self.errors.append(error);
}
}
self.valid_array_items = None;
self.array_errors = None;
Ok(())
}
Value::Map(m) if self.is_member_key => {
let current_location = self.cbor_location.clone();
let entry_counts = entry_counts_from_group(self.cddl, group);
self.entry_counts = Some(entry_counts);
for (k, v) in m.iter() {
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, k.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, k.clone());
cv.generic_rules = self.generic_rules.clone();
cv.entry_counts = self.entry_counts.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.visit_type2(t2)?;
if cv.errors.is_empty() {
self.object_value = Some(v.clone());
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.cbor_location = current_location;
return Ok(());
}
self.errors.append(&mut cv.errors);
}
self.entry_counts = None;
Ok(())
}
_ => {
self.add_error(format!("expected array type, got {:?}", self.cbor));
Ok(())
}
},
Type2::ChoiceFromGroup {
ident,
generic_args,
..
} => {
if let Some(ga) = generic_args {
if let Some(rule) = rule_from_ident(self.cddl, ident) {
if let Some(gr) = self
.generic_rules
.iter_mut()
.find(|gr| gr.name == ident.ident)
{
for arg in ga.args.iter() {
gr.args.push((*arg.arg).clone());
}
} else if let Some(params) = generic_params_from_rule(rule) {
self.generic_rules.push(GenericRule {
name: ident.ident,
params,
args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
});
}
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv =
CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = Some(ident.ident);
cv.is_group_to_choice_enum = true;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.visit_rule(rule)?;
self.errors.append(&mut cv.errors);
return Ok(());
}
}
if group_rule_from_ident(self.cddl, ident).is_none() {
self.add_error(format!(
"rule {} must be a group rule to turn it into a choice",
ident
));
return Ok(());
}
self.is_group_to_choice_enum = true;
self.visit_identifier(ident)?;
self.is_group_to_choice_enum = false;
Ok(())
}
Type2::ChoiceFromInlineGroup { group, .. } => {
self.is_group_to_choice_enum = true;
self.visit_group(group)?;
self.is_group_to_choice_enum = false;
Ok(())
}
Type2::Typename {
ident,
generic_args,
..
} => {
if let Some(ga) = generic_args {
if let Some(rule) = rule_from_ident(self.cddl, ident) {
if let Some(gr) = self
.generic_rules
.iter_mut()
.find(|gr| gr.name == ident.ident)
{
for arg in ga.args.iter() {
gr.args.push((*arg.arg).clone());
}
} else if let Some(params) = generic_params_from_rule(rule) {
self.generic_rules.push(GenericRule {
name: ident.ident,
params,
args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
});
}
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv =
CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = Some(ident.ident);
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.visit_rule(rule)?;
self.errors.append(&mut cv.errors);
return Ok(());
}
}
let type_choice_alternates = type_choice_alternates_from_ident(self.cddl, ident);
if !type_choice_alternates.is_empty() {
self.is_multi_type_choice = true;
}
let error_count = self.errors.len();
for t in type_choice_alternates {
let cur_errors = self.errors.len();
self.visit_type(t)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
return Ok(());
}
}
self.visit_identifier(ident)
}
Type2::IntValue { value, .. } => self.visit_value(&token::Value::INT(*value)),
Type2::UintValue { value, .. } => self.visit_value(&token::Value::UINT(*value)),
Type2::FloatValue { value, .. } => self.visit_value(&token::Value::FLOAT(*value)),
Type2::ParenthesizedType { pt, .. } => self.visit_type(pt),
Type2::Unwrap {
ident,
generic_args,
..
} => {
if let Some(Type2::TaggedData { t, .. }) = tag_from_token(&lookup_ident(ident.ident)) {
return self.visit_type(&t);
}
if let Some(ga) = generic_args {
if let Some(rule) = unwrap_rule_from_ident(self.cddl, ident) {
if let Some(gr) = self
.generic_rules
.iter_mut()
.find(|gr| gr.name == ident.ident)
{
for arg in ga.args.iter() {
gr.args.push((*arg.arg).clone());
}
} else if let Some(params) = generic_params_from_rule(rule) {
self.generic_rules.push(GenericRule {
name: ident.ident,
params,
args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
});
}
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv =
CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = Some(ident.ident);
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.visit_rule(rule)?;
self.errors.append(&mut cv.errors);
return Ok(());
}
}
if let Some(rule) = unwrap_rule_from_ident(self.cddl, ident) {
return self.visit_rule(rule);
}
self.add_error(format!(
"cannot unwrap identifier {}, rule not found",
ident
));
Ok(())
}
Type2::TaggedData { tag, t, .. } => match &self.cbor {
Value::Tag(actual_tag, value) => {
if let Some(tag) = tag {
if *tag as u64 != *actual_tag {
self.add_error(format!(
"expected tagged data #6.{}({}), got {:?}",
tag, t, self.cbor
));
return Ok(());
}
} else if *actual_tag > 0 {
self.add_error(format!(
"expected tagged data #6({}), got {:?}",
t, self.cbor
));
return Ok(());
}
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(
self.cddl,
value.as_ref().clone(),
self.enabled_features.clone(),
);
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, value.as_ref().clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, value.as_ref().clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.visit_type(t)?;
self.errors.append(&mut cv.errors);
Ok(())
}
_ => {
if let Some(tag) = tag {
self.add_error(format!(
"expected tagged data #6.{}({}), got {:?}",
tag, t, self.cbor
));
} else {
self.add_error(format!(
"expected tagged data #6({}), got {:?}",
t, self.cbor
));
}
Ok(())
}
},
Type2::DataMajorType { mt, constraint, .. } => match &self.cbor {
Value::Integer(i) => {
match mt {
0u8 => match constraint {
Some(c) if i128::from(*i) == *c as i128 && i128::from(*i) >= 0i128 => return Ok(()),
Some(c) => {
self.add_error(format!(
"expected uint data type with constraint {} (#{}.{}), got {:?}",
c, mt, c, self.cbor
));
return Ok(());
}
_ => {
if i128::from(*i).is_negative() {
self.add_error(format!(
"expected uint data type (#{}), got {:?}",
mt, self.cbor
));
return Ok(());
}
}
},
1u8 => match constraint {
Some(c) if i128::from(*i) == 0i128 - *c as i128 => return Ok(()),
Some(c) => {
self.add_error(format!(
"expected nint type with constraint {} (#{}.{}), got {:?}",
c, mt, c, self.cbor
));
return Ok(());
}
_ => {
if i128::from(*i) >= 0i128 {
self.add_error(format!(
"expected nint data type (#{}), got {:?}",
mt, self.cbor
));
return Ok(());
}
}
},
_ => self.add_error(format!(
"expected major type {} with constraint {:?}, got {:?}",
mt, constraint, self.cbor
)),
}
Ok(())
}
Value::Bytes(b) => {
match mt {
2u8 => match constraint {
Some(c) if *c == b.len() => return Ok(()),
Some(c) => self.add_error(format!(
"expected byte string type with constraint {} (#{}.{}), got {:?}",
c, mt, c, self.cbor
)),
_ => return Ok(()),
},
_ => self.add_error(format!(
"expected major type {} with constraint {:?}, got {:?}",
mt, constraint, self.cbor
)),
}
Ok(())
}
Value::Text(t) => {
match mt {
3u8 => match constraint {
Some(c) if *c == t.len() => return Ok(()),
Some(c) => self.add_error(format!(
"expected text string type with constraint {} (#{}.{}), got {:?}",
c, mt, c, self.cbor
)),
_ => return Ok(()),
},
_ => self.add_error(format!(
"expected major type {} with constraint {:?}, got {:?}",
mt, constraint, self.cbor
)),
}
Ok(())
}
Value::Array(a) => {
match mt {
4u8 => match constraint {
Some(c) if *c == a.len() => return Ok(()),
Some(c) => self.add_error(format!(
"expected array type with constraint {} (#{}.{}), got {:?}",
c, mt, c, self.cbor
)),
_ => return Ok(()),
},
_ => self.add_error(format!(
"expected major type {} with constraint {:?}, got {:?}",
mt, constraint, self.cbor
)),
}
Ok(())
}
Value::Map(m) => {
match mt {
5u8 => match constraint {
Some(c) if *c == m.len() => return Ok(()),
Some(c) => self.add_error(format!(
"expected map type with constraint {} (#{}.{}), got {:?}",
c, mt, c, self.cbor
)),
_ => return Ok(()),
},
_ => self.add_error(format!(
"expected major type {} with constraint {:?}, got {:?}",
mt, constraint, self.cbor
)),
}
Ok(())
}
Value::Float(_f) => {
match mt {
7u8 => match constraint {
Some(_c) => unimplemented!(),
_ => return Ok(()),
},
_ => self.add_error(format!(
"expected major type {} with constraint {:?}, got {:?}",
mt, constraint, self.cbor
)),
}
Ok(())
}
_ => {
if let Some(constraint) = constraint {
self.add_error(format!(
"expected major type #{}.{}, got {:?}",
mt, constraint, self.cbor
));
} else {
self.add_error(format!("expected major type #{}, got {:?}", mt, self.cbor));
}
Ok(())
}
},
#[cfg(feature = "ast-span")]
Type2::Any { .. } => Ok(()),
#[cfg(not(feature = "ast-span"))]
Type2::Any {} => Ok(()),
_ => {
self.add_error(format!(
"unsupported data type for validating cbor, got {}",
t2
));
Ok(())
}
}
}
fn visit_identifier(&mut self, ident: &Identifier<'a>) -> visitor::Result<Error<T>> {
if let Some(name) = self.eval_generic_rule {
if let Some(gr) = self
.generic_rules
.iter()
.cloned()
.find(|gr| gr.name == name)
{
for (idx, gp) in gr.params.iter().enumerate() {
if *gp == ident.ident {
if let Some(arg) = gr.args.get(idx) {
return self.visit_type1(arg);
}
}
}
}
}
if !self.is_colon_shortcut_present {
if let Some(r) = rule_from_ident(self.cddl, ident) {
return self.visit_rule(r);
}
}
if is_ident_any_type(self.cddl, ident) {
return Ok(());
}
match &self.cbor {
Value::Null if is_ident_null_data_type(self.cddl, ident) => Ok(()),
Value::Bytes(_) if is_ident_byte_string_data_type(self.cddl, ident) => Ok(()),
Value::Bool(b) => {
if is_ident_bool_data_type(self.cddl, ident) {
return Ok(());
}
if ident_matches_bool_value(self.cddl, ident, *b) {
return Ok(());
}
self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
Ok(())
}
Value::Integer(i) => {
if is_ident_uint_data_type(self.cddl, ident) {
if i128::from(*i).is_negative() {
self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
}
Ok(())
} else if is_ident_integer_data_type(self.cddl, ident) {
Ok(())
} else if is_ident_time_data_type(self.cddl, ident) {
if let chrono::LocalResult::None =
Utc.timestamp_millis_opt((i128::from(*i) * 1000) as i64)
{
let i = *i;
self.add_error(format!(
"expected time data type, invalid UNIX timestamp {:?}",
i,
));
}
Ok(())
} else {
self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
Ok(())
}
}
Value::Float(f) => {
if is_ident_float_data_type(self.cddl, ident) {
Ok(())
} else if is_ident_time_data_type(self.cddl, ident) {
if let chrono::LocalResult::None = Utc.timestamp_millis_opt((*f * 1000f64) as i64) {
let f = *f;
self.add_error(format!(
"expected time data type, invalid UNIX timestamp {:?}",
f,
));
}
Ok(())
} else {
self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
Ok(())
}
}
Value::Text(s) => {
if is_ident_uri_data_type(self.cddl, ident) {
if let Err(e) = uriparse::URI::try_from(&**s) {
self.add_error(format!("expected URI data type, decoding error: {}", e));
}
} else if is_ident_b64url_data_type(self.cddl, ident) {
if let Err(e) = base64_url::decode(s) {
self.add_error(format!(
"expected base64 URL data type, decoding error: {}",
e
));
}
} else if is_ident_tdate_data_type(self.cddl, ident) {
if let Err(e) = chrono::DateTime::parse_from_rfc3339(s) {
self.add_error(format!("expected tdate data type, decoding error: {}", e));
}
} else if is_ident_string_data_type(self.cddl, ident) {
return Ok(());
} else {
self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
}
Ok(())
}
Value::Array(_) => self.validate_array_items(&ArrayItemToken::Identifier(ident)),
Value::Map(m) => {
match &self.occurrence {
#[cfg(feature = "ast-span")]
Some(Occur::Optional { .. }) | None => {
if is_ident_string_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Text(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_integer_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Integer(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_bool_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bool(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_null_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_byte_string_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bytes(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_float_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_some()
{
self.add_error(format!(
"expected object value of type {}, got object",
ident.ident
));
return Ok(());
}
self.visit_value(&token::Value::TEXT(ident.ident.into()))
}
#[cfg(not(feature = "ast-span"))]
Some(Occur::Optional {}) | None => {
if is_ident_string_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Text(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_integer_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Integer(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_bool_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bool(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_null_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_byte_string_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bytes(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_float_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else {
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_some()
{
self.add_error(format!(
"expected object value of type {}, got object",
ident.ident
));
return Ok(());
}
self.visit_value(&token::Value::TEXT(ident.ident.into()))
}
Some(occur) => {
let mut errors = Vec::new();
if is_ident_string_data_type(self.cddl, ident) {
let values_to_validate = m
.iter()
.filter_map(|(k, v)| {
if let Some(keys) = &self.validated_keys {
if !keys.contains(k) {
if matches!(k, Value::Text(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
} else {
None
}
} else if matches!(k, Value::Text(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
})
.collect::<Vec<_>>();
self.values_to_validate = Some(values_to_validate);
}
if is_ident_integer_data_type(self.cddl, ident) {
let mut errors = Vec::new();
let values_to_validate = m
.iter()
.filter_map(|(k, v)| {
if let Some(keys) = &self.validated_keys {
if !keys.contains(k) {
if matches!(k, Value::Integer(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
} else {
None
}
} else if matches!(k, Value::Integer(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
})
.collect::<Vec<_>>();
self.values_to_validate = Some(values_to_validate);
}
if is_ident_bool_data_type(self.cddl, ident) {
let mut errors = Vec::new();
let values_to_validate = m
.iter()
.filter_map(|(k, v)| {
if let Some(keys) = &self.validated_keys {
if !keys.contains(k) {
if matches!(k, Value::Bool(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
} else {
None
}
} else if matches!(k, Value::Bool(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
})
.collect::<Vec<_>>();
self.values_to_validate = Some(values_to_validate);
}
if is_ident_byte_string_data_type(self.cddl, ident) {
let mut errors = Vec::new();
let values_to_validate = m
.iter()
.filter_map(|(k, v)| {
if let Some(keys) = &self.validated_keys {
if !keys.contains(k) {
if matches!(k, Value::Bytes(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
} else {
None
}
} else if matches!(k, Value::Bytes(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
})
.collect::<Vec<_>>();
self.values_to_validate = Some(values_to_validate);
}
if is_ident_null_data_type(self.cddl, ident) {
let mut errors = Vec::new();
let values_to_validate = m
.iter()
.filter_map(|(k, v)| {
if let Some(keys) = &self.validated_keys {
if !keys.contains(k) {
if matches!(k, Value::Null) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
} else {
None
}
} else if matches!(k, Value::Null) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
})
.collect::<Vec<_>>();
self.values_to_validate = Some(values_to_validate);
}
if is_ident_float_data_type(self.cddl, ident) {
let mut errors = Vec::new();
let values_to_validate = m
.iter()
.filter_map(|(k, v)| {
if let Some(keys) = &self.validated_keys {
if !keys.contains(k) {
if matches!(k, Value::Float(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
} else {
None
}
} else if matches!(k, Value::Float(_)) {
Some(v.clone())
} else {
errors.push(format!("key of type {} required, got {:?}", ident, k));
None
}
})
.collect::<Vec<_>>();
self.values_to_validate = Some(values_to_validate);
}
if !errors.is_empty() {
for e in errors.into_iter() {
self.add_error(e);
}
return Ok(());
}
#[cfg(feature = "ast-span")]
if let Occur::ZeroOrMore { .. } | Occur::OneOrMore { .. } = occur {
if let Occur::OneOrMore { .. } = occur {
if m.is_empty() {
self.add_error(format!(
"map cannot be empty, one or more entries with key type {} required",
ident
));
return Ok(());
}
}
} else if let Occur::Exact { lower, upper, .. } = occur {
if let Some(values_to_validate) = &self.values_to_validate {
if let Some(lower) = lower {
if let Some(upper) = upper {
if values_to_validate.len() < *lower || values_to_validate.len() > *upper {
if lower == upper {
self.add_error(format!(
"object must contain exactly {} entries of key of type {}",
lower, ident,
));
} else {
self.add_error(format!(
"object must contain between {} and {} entries of key of type {}",
lower, upper, ident,
));
}
return Ok(());
}
}
if values_to_validate.len() < *lower {
self.add_error(format!(
"object must contain at least {} entries of key of type {}",
lower, ident,
));
return Ok(());
}
}
if let Some(upper) = upper {
if values_to_validate.len() > *upper {
self.add_error(format!(
"object must contain no more than {} entries of key of type {}",
upper, ident,
));
return Ok(());
}
}
return Ok(());
}
}
#[cfg(not(feature = "ast-span"))]
if let Occur::ZeroOrMore {} | Occur::OneOrMore {} = occur {
if let Occur::OneOrMore {} = occur {
if m.is_empty() {
self.add_error(format!(
"object cannot be empty, one or more entries with key type {} required",
ident
));
return Ok(());
}
}
} else if let Occur::Exact { lower, upper } = occur {
if let Some(values_to_validate) = &self.values_to_validate {
if let Some(lower) = lower {
if let Some(upper) = upper {
if values_to_validate.len() < *lower || values_to_validate.len() > *upper {
if lower == upper {
self.add_error(format!(
"object must contain exactly {} entries of key of type {}",
lower, ident,
));
} else {
self.add_error(format!(
"object must contain between {} and {} entries of key of type {}",
lower, upper, ident,
));
}
return Ok(());
}
}
if values_to_validate.len() < *lower {
self.add_error(format!(
"object must contain at least {} entries of key of type {}",
lower, ident,
));
return Ok(());
}
}
if let Some(upper) = upper {
if values_to_validate.len() > *upper {
self.add_error(format!(
"object must contain no more than {} entries of key of type {}",
upper, ident,
));
return Ok(());
}
}
return Ok(());
}
}
if is_ident_string_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Text(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
|| (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
{
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_integer_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Integer(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
|| (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
{
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_bool_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bool(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
|| (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
{
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_null_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
|| (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
{
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_byte_string_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Bytes(_))) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
|| (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
{
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if is_ident_float_data_type(self.cddl, ident) && !self.validating_value {
if let Some((k, v)) = m.iter().find(|(k, _)| matches!(k, Value::Null)) {
self
.validated_keys
.get_or_insert(vec![k.clone()])
.push(k.clone());
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{:?}", v);
} else if (!matches!(occur, Occur::ZeroOrMore { .. }) && m.is_empty())
|| (matches!(occur, Occur::ZeroOrMore { .. }) && !m.is_empty())
{
self.add_error(format!("map requires entry key of type {}", ident));
}
return Ok(());
}
if token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_some()
{
self.add_error(format!(
"expected object value of type {}, got object",
ident.ident
));
return Ok(());
}
self.visit_value(&token::Value::TEXT(ident.ident.into()))
}
}
}
_ => {
if let Some(cut_value) = self.cut_value.take() {
self.add_error(format!(
"cut present for member key {}. expected type {}, got {:?}",
cut_value, ident, self.cbor
));
} else {
self.add_error(format!("expected type {}, got {:?}", ident, self.cbor));
}
Ok(())
}
}
}
fn visit_value_member_key_entry(
&mut self,
entry: &ValueMemberKeyEntry<'a>,
) -> visitor::Result<Error<T>> {
if let Some(occur) = &entry.occur {
self.visit_occurrence(occur)?;
}
let current_location = self.cbor_location.clone();
if let Some(mk) = &entry.member_key {
let error_count = self.errors.len();
self.is_member_key = true;
self.visit_memberkey(mk)?;
self.is_member_key = false;
if self.errors.len() != error_count {
self.advance_to_next_entry = true;
return Ok(());
}
}
if let Some(values) = &self.values_to_validate {
for v in values.iter() {
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, v.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, v.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.validating_value = true;
cv.visit_type(&entry.entry_type)?;
self.cbor_location = current_location.clone();
self.errors.append(&mut cv.errors);
if entry.occur.is_some() {
self.occurrence = None;
}
}
return Ok(());
}
if let Some(v) = self.object_value.take() {
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv = CBORValidator::new(self.cddl, v, self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, v, self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, v);
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = self.eval_generic_rule;
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.is_multi_group_choice = self.is_multi_group_choice;
cv.cbor_location.push_str(&self.cbor_location);
cv.type_group_name_entry = self.type_group_name_entry;
cv.visit_type(&entry.entry_type)?;
self.cbor_location = current_location;
self.errors.append(&mut cv.errors);
if entry.occur.is_some() {
self.occurrence = None;
}
Ok(())
} else if !self.advance_to_next_entry {
self.visit_type(&entry.entry_type)
} else {
Ok(())
}
}
fn visit_type_groupname_entry(
&mut self,
entry: &TypeGroupnameEntry<'a>,
) -> visitor::Result<Error<T>> {
self.type_group_name_entry = Some(entry.name.ident);
if let Some(ga) = &entry.generic_args {
if let Some(rule) = rule_from_ident(self.cddl, &entry.name) {
if let Some(gr) = self
.generic_rules
.iter_mut()
.find(|gr| gr.name == entry.name.ident)
{
for arg in ga.args.iter() {
gr.args.push((*arg.arg).clone());
}
} else if let Some(params) = generic_params_from_rule(rule) {
self.generic_rules.push(GenericRule {
name: entry.name.ident,
params,
args: ga.args.iter().cloned().map(|arg| *arg.arg).collect(),
});
}
#[cfg(all(feature = "additional-controls", target_arch = "wasm32"))]
let mut cv =
CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features.clone());
#[cfg(all(feature = "additional-controls", not(target_arch = "wasm32")))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone(), self.enabled_features);
#[cfg(not(feature = "additional-controls"))]
let mut cv = CBORValidator::new(self.cddl, self.cbor.clone());
cv.generic_rules = self.generic_rules.clone();
cv.eval_generic_rule = Some(entry.name.ident);
cv.is_multi_type_choice = self.is_multi_type_choice;
cv.visit_rule(rule)?;
self.errors.append(&mut cv.errors);
return Ok(());
}
}
let type_choice_alternates = type_choice_alternates_from_ident(self.cddl, &entry.name);
if !type_choice_alternates.is_empty() {
self.is_multi_type_choice = true;
}
let error_count = self.errors.len();
for t in type_choice_alternates {
let cur_errors = self.errors.len();
self.visit_type(t)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
return Ok(());
}
}
let error_count = self.errors.len();
let group_choice_alternates = group_choice_alternates_from_ident(self.cddl, &entry.name);
if !group_choice_alternates.is_empty() {
self.is_multi_group_choice = true;
}
for ge in group_choice_alternates {
let cur_errors = self.errors.len();
self.visit_group_entry(ge)?;
if self.errors.len() == cur_errors {
for _ in 0..self.errors.len() - error_count {
self.errors.pop();
}
return Ok(());
}
}
walk_type_groupname_entry(self, entry)?;
self.type_group_name_entry = None;
Ok(())
}
fn visit_memberkey(&mut self, mk: &MemberKey<'a>) -> visitor::Result<Error<T>> {
match mk {
MemberKey::Type1 { is_cut, .. } => {
self.is_cut_present = *is_cut;
walk_memberkey(self, mk)?;
self.is_cut_present = false;
}
MemberKey::Bareword { .. } => {
self.is_colon_shortcut_present = true;
walk_memberkey(self, mk)?;
self.is_colon_shortcut_present = false;
}
_ => return walk_memberkey(self, mk),
}
Ok(())
}
fn visit_value(&mut self, value: &token::Value<'a>) -> visitor::Result<Error<T>> {
let error: Option<String> = match &self.cbor {
Value::Integer(i) => match value {
token::Value::INT(v) => match &self.ctrl {
Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
if i128::from(*i) != *v as i128 =>
{
None
}
Some(ControlOperator::LT) if i128::from(*i) < *v as i128 => None,
Some(ControlOperator::LE) if i128::from(*i) <= *v as i128 => None,
Some(ControlOperator::GT) if i128::from(*i) > *v as i128 => None,
Some(ControlOperator::GE) if i128::from(*i) >= *v as i128 => None,
#[cfg(feature = "additional-controls")]
Some(ControlOperator::PLUS) => {
if i128::from(*i) == *v as i128 {
None
} else {
Some(format!("expected computed .plus value {}, got {:?}", v, i))
}
}
#[cfg(feature = "additional-controls")]
None | Some(ControlOperator::FEATURE) => {
if i128::from(*i) == *v as i128 {
None
} else {
Some(format!("expected value {}, got {:?}", v, i))
}
}
#[cfg(not(feature = "additional-controls"))]
None => {
if i128::from(*i) == *v as i128 {
None
} else {
Some(format!("expected value {}, got {:?}", v, i))
}
}
_ => Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
i
)),
},
token::Value::UINT(v) => match &self.ctrl {
Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
if i128::from(*i) != *v as i128 =>
{
None
}
Some(ControlOperator::LT) if i128::from(*i) < *v as i128 => None,
Some(ControlOperator::LE) if i128::from(*i) <= *v as i128 => None,
Some(ControlOperator::GT) if i128::from(*i) > *v as i128 => None,
Some(ControlOperator::GE) if i128::from(*i) >= *v as i128 => None,
Some(ControlOperator::SIZE) => match 256i128.checked_pow(*v as u32) {
Some(n) if i128::from(*i) < n => None,
_ => Some(format!("expected value .size {}, got {:?}", v, i)),
},
Some(ControlOperator::BITS) => {
if let Some(sv) = 1u32.checked_shl(*v as u32) {
if (i128::from(*i) & sv as i128) != 0 {
None
} else {
Some(format!("expected uint .bits {}, got {:?}", v, i))
}
} else {
Some(format!("expected uint .bits {}, got {:?}", v, i))
}
}
#[cfg(feature = "additional-controls")]
Some(ControlOperator::PLUS) => {
if i128::from(*i) == *v as i128 {
None
} else {
Some(format!("expected computed .plus value {}, got {:?}", v, i))
}
}
#[cfg(feature = "additional-controls")]
None | Some(ControlOperator::FEATURE) => {
if i128::from(*i) == *v as i128 {
None
} else {
Some(format!("expected value {}, got {:?}", v, i))
}
}
#[cfg(not(feature = "additional-controls"))]
None => {
if i128::from(*i) == *v as i128 {
None
} else {
Some(format!("expected value {}, got {:?}", v, i))
}
}
_ => Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
i
)),
},
_ => Some(format!("expected {}, got {:?}", value, i)),
},
Value::Float(f) => match value {
token::Value::FLOAT(v) => match &self.ctrl {
Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT)
if (*f - *v).abs() > std::f64::EPSILON =>
{
None
}
Some(ControlOperator::LT) if *f < *v as f64 => None,
Some(ControlOperator::LE) if *f <= *v as f64 => None,
Some(ControlOperator::GT) if *f > *v as f64 => None,
Some(ControlOperator::GE) if *f >= *v as f64 => None,
#[cfg(feature = "additional-controls")]
Some(ControlOperator::PLUS) => {
if (*f - *v).abs() < std::f64::EPSILON {
None
} else {
Some(format!("expected computed .plus value {}, got {:?}", v, f))
}
}
#[cfg(feature = "additional-controls")]
None | Some(ControlOperator::FEATURE) => {
if (*f - *v).abs() < std::f64::EPSILON {
None
} else {
Some(format!("expected value {}, got {:?}", v, f))
}
}
#[cfg(not(feature = "additional-controls"))]
None => {
if (*f - *v).abs() < std::f64::EPSILON {
None
} else {
Some(format!("expected value {}, got {:?}", v, f))
}
}
_ => Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
f
)),
},
_ => Some(format!("expected {}, got {:?}", value, f)),
},
Value::Text(s) => match value {
token::Value::TEXT(t) => match &self.ctrl {
Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT) => {
if s != t {
None
} else {
Some(format!("expected {} .ne to \"{}\"", value, s))
}
}
Some(ControlOperator::REGEXP) | Some(ControlOperator::PCRE) => {
let re = regex::Regex::new(
&format_regex(
serde_json::from_str::<serde_json::Value>(&format!("\"{}\"", t))
.map_err(Error::JSONParsing)?
.as_str()
.ok_or_else(|| Error::from_validator(self, "malformed regex".to_string()))?,
)
.ok_or_else(|| Error::from_validator(self, "malformed regex".to_string()))?,
)
.map_err(|e| Error::from_validator(self, e.to_string()))?;
if re.is_match(s) {
None
} else {
Some(format!("expected \"{}\" to match regex \"{}\"", s, t))
}
}
#[cfg(feature = "additional-controls")]
Some(ControlOperator::ABNF) => validate_abnf(t, s)
.err()
.map(|e| format!("\"{}\" is not valid against abnf: {}", s, e)),
_ => {
#[cfg(feature = "additional-controls")]
if s == t {
None
} else if let Some(ControlOperator::CAT) | Some(ControlOperator::DET) = &self.ctrl {
Some(format!(
"expected value to match concatenated string {}, got \"{}\"",
value, s
))
} else if let Some(ctrl) = &self.ctrl {
Some(format!("expected value {} {}, got \"{}\"", ctrl, value, s))
} else {
Some(format!("expected value {} got \"{}\"", value, s))
}
#[cfg(not(feature = "additional-controls"))]
if s == t {
None
} else if let Some(ctrl) = &self.ctrl {
Some(format!("expected value {} {}, got \"{}\"", ctrl, value, s))
} else {
Some(format!("expected value {} got \"{}\"", value, s))
}
}
},
token::Value::UINT(u) => match &self.ctrl {
Some(ControlOperator::SIZE) => {
if s.len() == *u {
None
} else {
Some(format!("expected \"{}\" .size {}, got {}", s, u, s.len()))
}
}
_ => Some(format!("expected {}, got {}", u, s)),
},
token::Value::BYTE(token::ByteValue::UTF8(b)) if s.as_bytes() == b.as_ref() => None,
token::Value::BYTE(token::ByteValue::B16(b)) if s.as_bytes() == b.as_ref() => None,
token::Value::BYTE(token::ByteValue::B64(b)) if s.as_bytes() == b.as_ref() => None,
_ => Some(format!("expected {}, got \"{}\"", value, s)),
},
Value::Bytes(b) => match value {
token::Value::UINT(v) => match &self.ctrl {
Some(ControlOperator::SIZE) => {
if b.len() == *v {
None
} else {
Some(format!("expected \"{:?}\" .size {}, got {}", b, v, b.len()))
}
}
Some(ControlOperator::BITS) => {
if let Some(rsv) = v.checked_shr(3) {
if let Some(s) = b.get(rsv) {
if let Some(lsv) = 1u32.checked_shl(*v as u32 & 7) {
if (*s as u32 & lsv) != 0 {
None
} else {
Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
b
))
}
} else {
Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
b
))
}
} else {
Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
b
))
}
} else {
Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
b
))
}
}
_ => Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
v,
b
)),
},
#[cfg(feature = "additional-controls")]
token::Value::TEXT(t) => match &self.ctrl {
Some(ControlOperator::ABNFB) => {
validate_abnf(t, std::str::from_utf8(b).map_err(Error::UTF8Parsing)?)
.err()
.map(|e| {
format!(
"cbor bytes \"{:?}\" are not valid against abnf {}: {}",
b, t, e
)
})
}
_ => Some(format!(
"expected value {} {}, got {:?}",
self.ctrl.unwrap(),
t,
b
)),
},
_ => Some(format!("expected {}, got {:?}", value, b)),
},
Value::Array(_) => {
self.validate_array_items(&ArrayItemToken::Value(value))?;
None
}
Value::Map(o) => {
if self.is_cut_present {
self.cut_value = Some(Type1::from(value.clone()));
}
if let token::Value::TEXT(Cow::Borrowed("any")) = value {
return Ok(());
}
let k = token_value_into_cbor_value(value.clone());
#[cfg(feature = "ast-span")]
if let Some(v) = o
.iter()
.find_map(|entry| if entry.0 == k { Some(&entry.1) } else { None })
{
self.validated_keys.get_or_insert(vec![k.clone()]).push(k);
self.object_value = Some(v.clone());
let _ = write!(self.cbor_location, "/{}", value);
None
} else if let Some(Occur::Optional { .. }) | Some(Occur::ZeroOrMore { .. }) =
&self.occurrence.take()
{
self.advance_to_next_entry = true;
None
} else if let Some(ControlOperator::NE) | Some(ControlOperator::DEFAULT) = &self.ctrl {
None
} else {
Some(format!("object missing key: \"{}\"", value))
}
#[cfg(not(feature = "ast-span"))]
if let Some(v) = o
.iter()
.find_map(|entry| if entry.0 == k { Some(&entry.1) } else { None })
{
self.validated_keys.get_or_insert(vec![k.clone()]).push(k);
self.object_value = Some(v.clone());
self.cbor_location.push_str(&format!("/{}", value));
None
} else if let Some(Occur::Optional {}) | Some(Occur::ZeroOrMore {}) =
&self.occurrence.take()
{
self.advance_to_next_entry = true;
None
} else if let Some(Token::NE) | Some(Token::DEFAULT) = &self.ctrl {
None
} else {
Some(format!("object missing key: \"{}\"", value))
}
}
_ => Some(format!("expected {}, got {:?}", value, self.cbor)),
};
if let Some(e) = error {
self.add_error(e);
}
Ok(())
}
fn visit_occurrence(&mut self, o: &Occurrence<'a>) -> visitor::Result<Error<T>> {
self.occurrence = Some(o.occur);
Ok(())
}
}
pub fn token_value_into_cbor_value(value: token::Value) -> ciborium::value::Value {
match value {
token::Value::UINT(i) => ciborium::value::Value::Integer(i.into()),
token::Value::INT(i) => ciborium::value::Value::Integer(i.into()),
token::Value::FLOAT(f) => ciborium::value::Value::Float(f),
token::Value::TEXT(t) => ciborium::value::Value::Text(t.to_string()),
token::Value::BYTE(b) => match b {
ByteValue::UTF8(b) | ByteValue::B16(b) | ByteValue::B64(b) => {
ciborium::value::Value::Bytes(b.into_owned())
}
},
}
}
#[cfg(test)]
#[cfg(not(target_arch = "wasm32"))]
mod tests {
use super::*;
use ciborium::cbor;
use indoc::indoc;
#[cfg(not(feature = "additional-controls"))]
#[test]
fn validate() -> std::result::Result<(), Box<dyn std::error::Error>> {
let cddl = indoc!(
r#"
tcpflagbytes = bstr .bits flags
flags = &(
fin: 8,
syn: 9,
rst: 10,
psh: 11,
ack: 12,
urg: 13,
ece: 14,
cwr: 15,
ns: 0,
) / (4..7) ; data offset bits
"#
);
let cbor = ciborium::value::Value::Bytes(vec![0x90, 0x6d]);
let mut lexer = lexer_from_str(cddl);
let cddl = cddl_from_str(&mut lexer, cddl, true)?;
let mut cv = CBORValidator::new(&cddl, cbor);
cv.validate()?;
Ok(())
}
#[cfg(feature = "additional-controls")]
#[test]
fn validate_abnfb() -> std::result::Result<(), Box<dyn std::error::Error>> {
let cddl = indoc!(
r#"
oid = bytes .abnfb ("oid" .det cbor-tags-oid)
roid = bytes .abnfb ("roid" .det cbor-tags-oid)
cbor-tags-oid = '
oid = 1*arc
roid = *arc
arc = [nlsb] %x00-7f
nlsb = %x81-ff *%x80-ff
'
"#
);
let sha256_oid = "2.16.840.1.101.3.4.2.1";
let cbor = ciborium::value::Value::Bytes(sha256_oid.as_bytes().to_vec());
let cddl = cddl_from_str(cddl, true)?;
let mut cv = CBORValidator::new(&cddl, cbor, None);
cv.validate()?;
Ok(())
}
#[cfg(feature = "additional-controls")]
#[test]
fn validate_feature() -> std::result::Result<(), Box<dyn std::error::Error>> {
let cddl = indoc!(
r#"
v = JC<"v", 2>
JC<J, C> = J .feature "json" / C .feature "cbor"
"#
);
let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
if let Err(e) = &cddl {
println!("{}", e);
}
let cbor = ciborium::value::Value::Integer(2.into());
let cddl = cddl.unwrap();
let mut cv = CBORValidator::new(&cddl, cbor, Some(&["cbor"]));
cv.validate()?;
Ok(())
}
#[test]
fn validate_type_choice_alternate() -> std::result::Result<(), Box<dyn std::error::Error>> {
let cddl = indoc!(
r#"
tester = [ $vals ]
$vals /= 12
$vals /= 13
"#
);
let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
if let Err(e) = &cddl {
println!("{}", e);
}
let cbor = ciborium::cbor!([13]).unwrap();
let cddl = cddl.unwrap();
let mut cv = CBORValidator::new(&cddl, cbor, None);
cv.validate()?;
Ok(())
}
#[test]
fn validate_group_choice_alternate_in_array(
) -> std::result::Result<(), Box<dyn std::error::Error>> {
let cddl = indoc!(
r#"
tester = [$$val]
$$val //= (
type: 10,
data: uint
)
$$val //= (
type: 11,
data: tstr
)
"#
);
let cddl = cddl_from_str(cddl, true).map_err(json::Error::CDDLParsing);
if let Err(e) = &cddl {
println!("{}", e);
}
let cbor = ciborium::cbor!([11, "test"]).unwrap();
let cddl = cddl.unwrap();
let mut cv = CBORValidator::new(&cddl, cbor, None);
cv.validate()?;
Ok(())
}
}