From 210e378af6fc2fd6e8cb380382c1a07f32190fd5 Mon Sep 17 00:00:00 2001 From: anweiss <2326106+anweiss@users.noreply.github.com> Date: Fri, 4 Dec 2020 16:20:31 -0500 Subject: [PATCH] concat updates --- src/validator/json.rs | 211 +++++++++++++----------------------------- src/validator/mod.rs | 159 +++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 146 deletions(-) diff --git a/src/validator/json.rs b/src/validator/json.rs index d7d4430f..2f826bfd 100644 --- a/src/validator/json.rs +++ b/src/validator/json.rs @@ -50,6 +50,20 @@ impl std::error::Error for Error { } } +impl Error { + fn from_validator(jv: &JSONValidator, reason: String) -> Self { + Error::Validation(vec![ValidationError { + cddl_location: jv.cddl_location.clone(), + json_location: jv.json_location.clone(), + reason, + is_multi_type_choice: jv.is_multi_type_choice, + is_group_to_choice_enum: jv.is_group_to_choice_enum, + type_group_name_entry: jv.type_group_name_entry.map(|e| e.to_string()), + is_multi_group_choice: jv.is_multi_group_choice, + }]) + } +} + /// JSON validation error #[derive(Clone, Debug)] pub struct ValidationError { @@ -107,20 +121,6 @@ impl std::error::Error for ValidationError { } } -impl ValidationError { - fn from_validator(jv: &JSONValidator, reason: String) -> Self { - ValidationError { - cddl_location: jv.cddl_location.clone(), - json_location: jv.json_location.clone(), - reason, - is_multi_type_choice: jv.is_multi_type_choice, - is_group_to_choice_enum: jv.is_group_to_choice_enum, - type_group_name_entry: jv.type_group_name_entry.map(|e| e.to_string()), - is_multi_group_choice: jv.is_multi_group_choice, - } - } -} - /// JSON validator type #[derive(Clone)] pub struct JSONValidator<'a> { @@ -212,16 +212,16 @@ impl<'a> JSONValidator<'a> { array_errors: None, } } +} +impl<'a> Validator<'a, Error> for JSONValidator<'a> { /// Validate - pub fn validate(&mut self) -> std::result::Result<(), Error> { + fn validate(&mut self) -> std::result::Result<(), Error> { for r in self.cddl.rules.iter() { // First type rule is root if let Rule::Type { rule, .. } = r { if rule.generic_params.is_none() { - self - .visit_type_rule(rule) - .map_err(|e| Error::Validation(vec![e]))?; + self.visit_type_rule(rule)?; break; } } @@ -247,8 +247,8 @@ impl<'a> JSONValidator<'a> { } } -impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { - fn visit_type_rule(&mut self, tr: &TypeRule<'a>) -> visitor::Result { +impl<'a> Visitor<'a, Error> for JSONValidator<'a> { + fn visit_type_rule(&mut self, tr: &TypeRule<'a>) -> visitor::Result { if let Some(gp) = &tr.generic_params { if let Some(gr) = self .generic_rules @@ -282,7 +282,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_group_rule(&mut self, gr: &GroupRule<'a>) -> visitor::Result { + fn visit_group_rule(&mut self, gr: &GroupRule<'a>) -> visitor::Result { if let Some(gp) = &gr.generic_params { if let Some(gr) = self .generic_rules @@ -316,7 +316,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_type(&mut self, t: &Type<'a>) -> visitor::Result { + fn visit_type(&mut self, t: &Type<'a>) -> visitor::Result { if t.type_choices.len() > 1 { self.is_multi_type_choice = true; } @@ -362,7 +362,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_group(&mut self, g: &Group<'a>) -> visitor::Result { + fn visit_group(&mut self, g: &Group<'a>) -> visitor::Result { if g.group_choices.len() > 1 { self.is_multi_group_choice = true; } @@ -439,7 +439,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_group_choice(&mut self, gc: &GroupChoice<'a>) -> visitor::Result { + fn visit_group_choice(&mut self, gc: &GroupChoice<'a>) -> visitor::Result { 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() { @@ -473,7 +473,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { lower: &Type2, upper: &Type2, is_inclusive: bool, - ) -> visitor::Result { + ) -> visitor::Result { if let Value::Array(a) = &self.json { match validate_array_occurrence( self.occurrence.as_ref().take(), @@ -782,7 +782,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { target: &Type2<'a>, ctrl: &str, controller: &Type2<'a>, - ) -> visitor::Result { + ) -> visitor::Result { match lookup_control_from_str(ctrl) { t @ Some(Token::EQ) => match target { Type2::Typename { ident, .. } => { @@ -945,109 +945,29 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } t @ Some(Token::CAT) => { self.ctrl = t; - match target { - Type2::TextValue { value: target, .. } => match controller { - Type2::TextValue { - value: controller, .. - } => { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - Type2::Typename { ident, .. } => { - for controller in string_literals_from_ident(self.cddl, ident).iter() { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - } - Type2::UTF8ByteString { - value: controller, .. - } => match std::str::from_utf8(controller) { - Ok(controller) => { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - Err(e) => self.add_error(format!("error parsing byte string: {}", e)), - }, - _ => unimplemented!(), - }, - Type2::Typename { ident, .. } => { - // Only grab the first type choice literal from the target per - // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 - if let Some(target) = string_literals_from_ident(self.cddl, ident).first() { - match controller { - Type2::TextValue { - value: controller, .. - } => { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - Type2::Typename { ident, .. } => { - for controller in string_literals_from_ident(self.cddl, ident).iter() { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - } - Type2::UTF8ByteString { - value: controller, .. - } => match std::str::from_utf8(controller) { - Ok(controller) => { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - Err(e) => self.add_error(format!("error parsing byte string: {}", e)), - }, - _ => unimplemented!(), - } - } + let mut values = Vec::new(); + if let Err(e) = cat_operation(self.cddl, target, controller, &mut values) { + self.add_error(e); + } + + let mut success = false; + let mut errors = Vec::new(); + for v in values.iter() { + let mut jv = self.clone(); + jv.visit_value(&(&**v).into())?; + if jv.errors.is_empty() { + success = true; + break; } - Type2::UTF8ByteString { value: target, .. } => match std::str::from_utf8(target) { - Ok(target) => match controller { - Type2::TextValue { - value: controller, .. - } => { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - Type2::Typename { ident, .. } => { - for controller in string_literals_from_ident(self.cddl, ident).iter() { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - } - Type2::UTF8ByteString { - value: controller, .. - } => match std::str::from_utf8(controller) { - Ok(controller) => { - let s = format!("{}{}", target, controller); - let mut jv = self.clone(); - jv.visit_value(&(&*s).into())?; - self.errors.append(&mut jv.errors) - } - Err(e) => self.add_error(format!("error parsing byte string: {}", e)), - }, - _ => unimplemented!(), - }, - Err(e) => self.add_error(format!("error parsing byte string: {}", e)), - }, - _ => unimplemented!(), + + errors.append(&mut jv.errors) } + + if !success { + self.errors.append(&mut errors) + } + self.ctrl = None; } _ => { @@ -1058,7 +978,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_type2(&mut self, t2: &Type2<'a>) -> visitor::Result { + fn visit_type2(&mut self, t2: &Type2<'a>) -> visitor::Result { match t2 { Type2::TextValue { value, .. } => self.visit_value(&token::Value::TEXT(value)), Type2::Map { group, .. } => match &self.json { @@ -1372,7 +1292,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { } } - fn visit_identifier(&mut self, ident: &Identifier<'a>) -> visitor::Result { + fn visit_identifier(&mut self, ident: &Identifier<'a>) -> visitor::Result { if let Some(name) = self.eval_generic_rule { if let Some(gr) = self .generic_rules @@ -1613,7 +1533,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { fn visit_value_member_key_entry( &mut self, entry: &ValueMemberKeyEntry<'a>, - ) -> visitor::Result { + ) -> visitor::Result { if let Some(occur) = &entry.occur { self.visit_occurrence(occur)?; } @@ -1683,7 +1603,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { fn visit_type_groupname_entry( &mut self, entry: &TypeGroupnameEntry<'a>, - ) -> visitor::Result { + ) -> visitor::Result { self.type_group_name_entry = Some(entry.name.ident); walk_type_groupname_entry(self, entry)?; self.type_group_name_entry = None; @@ -1691,7 +1611,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_memberkey(&mut self, mk: &MemberKey<'a>) -> visitor::Result { + fn visit_memberkey(&mut self, mk: &MemberKey<'a>) -> visitor::Result { if let MemberKey::Type1 { is_cut, .. } = mk { self.is_cut_present = *is_cut; } @@ -1699,7 +1619,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { walk_memberkey(self, mk) } - fn visit_value(&mut self, value: &token::Value<'a>) -> visitor::Result { + fn visit_value(&mut self, value: &token::Value<'a>) -> visitor::Result { let error: Option = match &self.json { Value::Number(n) => match value { token::Value::INT(v) => match n.as_i64() { @@ -1787,17 +1707,13 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { let re = regex::Regex::new( &format_regex( serde_json::from_str::(&format!("\"{}\"", t)) - .map_err(|e| ValidationError::from_validator(self, e.to_string()))? + .map_err(Error::JSONParsing)? .as_str() - .ok_or_else(|| { - ValidationError::from_validator(self, "malformed regex".to_string()) - })?, + .ok_or_else(|| Error::from_validator(self, "malformed regex".to_string()))?, ) - .ok_or_else(|| { - ValidationError::from_validator(self, "malformed regex".to_string()) - })?, + .ok_or_else(|| Error::from_validator(self, "malformed regex".to_string()))?, ) - .map_err(|e| ValidationError::from_validator(self, e.to_string()))?; + .map_err(|e| Error::from_validator(self, e.to_string()))?; if re.is_match(s) { None @@ -1808,6 +1724,11 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { _ => { if s == t { None + } else if let Some(Token::CAT) = &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 { @@ -1959,7 +1880,7 @@ impl<'a> Visitor<'a, ValidationError> for JSONValidator<'a> { Ok(()) } - fn visit_occurrence(&mut self, o: &Occurrence) -> visitor::Result { + fn visit_occurrence(&mut self, o: &Occurrence) -> visitor::Result { self.occurrence = Some(o.occur.clone()); Ok(()) @@ -1975,12 +1896,10 @@ mod tests { fn validate() -> std::result::Result<(), Box> { let cddl = indoc!( r#" - a = "foo" .cat ' - bar - baz - '"# + a = "foo" .cat b + b = "bar" / "baz" / "test""# ); - let json = r#""foo\n bar\n baz\n""#; + let json = r#""foobar""#; let mut lexer = lexer_from_str(cddl); let cddl = cddl_from_str(&mut lexer, cddl, true).map_err(json::Error::CDDLParsing); diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 2bc132ad..85269946 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -4,6 +4,8 @@ pub mod cbor; /// JSON validation implementation pub mod json; +use std::error::Error; + use cbor::CBORValidator; use json::JSONValidator; use serde::de::Deserialize; @@ -15,8 +17,19 @@ use crate::{ }, cddl_from_str, lexer_from_str, token::*, + visitor::Visitor, }; +trait Validator<'a, E: Error>: Visitor<'a, E> { + fn validate(&mut self) -> std::result::Result<(), E>; + fn add_error(&mut self, reason: String); +} + +trait Validatable {} + +impl Validatable for serde_cbor::Value {} +impl Validatable for serde_json::Value {} + /// Validate JSON string from a given CDDL document string pub fn validate_json_from_str(cddl: &str, json: &str) -> json::Result { let mut lexer = lexer_from_str(cddl); @@ -708,3 +721,149 @@ pub fn format_regex(input: &str) -> Option { Some(formatted_regex) } + +/// Concatenate target and controller +pub fn cat_operation( + cddl: &CDDL, + target: &Type2, + controller: &Type2, + literals: &mut Vec, +) -> Result<(), String> { + match target { + Type2::TextValue { value, .. } => match controller { + Type2::TextValue { + value: controller, .. + } => literals.push(format!("{}{}", value, controller)), + Type2::Typename { ident, .. } => { + for controller in string_literals_from_ident(cddl, ident).iter() { + literals.push(format!("{}{}", value, controller)); + } + } + Type2::UTF8ByteString { + value: controller, .. + } => match std::str::from_utf8(controller) { + Ok(controller) => literals.push(format!("{}{}", value, controller)), + Err(e) => return Err(format!("error parsing byte string: {}", e)), + }, + Type2::B16ByteString { + value: controller, .. + } => { + let mut buf = [0u8; 1024]; + match base16::decode_slice(&controller[..], &mut buf) { + Ok(_) => match std::str::from_utf8(&buf) { + Ok(controller) => literals.push(format!("{}{}", value, controller)), + Err(e) => return Err(format!("error parsing byte string: {}", e)), + }, + Err(e) => { + return Err(format!( + "error decoding base16 encoded byte string literal: {}", + e + )) + } + } + } + Type2::B64ByteString { + value: controller, .. + } => { + let mut buf = [0u8; 1024]; + match base64::decode_config_slice(&controller[..], base64::URL_SAFE, &mut buf) { + Ok(_) => match std::str::from_utf8(&buf) { + Ok(controller) => literals.push(format!("{}{}", value, controller)), + Err(e) => return Err(format!("error parsing byte string: {}", e)), + }, + Err(e) => { + return Err(format!( + "error decoding base64 encoded byte string literal: {}", + e + )) + } + } + } + Type2::ParenthesizedType { pt: controller, .. } => { + for controller in controller.type_choices.iter() { + if controller.type1.operator.is_none() { + cat_operation(cddl, target, &controller.type1.type2, literals)?; + } + } + } + _ => unimplemented!(), + }, + Type2::Typename { ident, .. } => { + // Only grab the first type choice literal from the target per + // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 + if let Some(value) = string_literals_from_ident(cddl, ident).first() { + match controller { + Type2::TextValue { + value: controller, .. + } => literals.push(format!("{}{}", value, controller)), + Type2::Typename { ident, .. } => { + let mut literals = Vec::new(); + for controller in string_literals_from_ident(cddl, ident).iter() { + literals.push(format!("{}{}", value, controller)); + } + } + Type2::UTF8ByteString { + value: controller, .. + } => match std::str::from_utf8(controller) { + Ok(controller) => { + literals.push(format!("{}{}", value, controller)); + } + Err(e) => return Err(format!("error parsing byte string: {}", e)), + }, + Type2::ParenthesizedType { pt: controller, .. } => { + for controller in controller.type_choices.iter() { + if controller.type1.operator.is_none() { + cat_operation(cddl, target, &controller.type1.type2, literals)?; + } + } + } + _ => unimplemented!(), + } + } else { + unimplemented!() + } + } + Type2::ParenthesizedType { pt: target, .. } => { + // Only grab the first type choice literal from the target per + // https://github.com/cbor-wg/cddl-control/issues/2#issuecomment-729253368 + if let Some(tc) = target.type_choices.first() { + // Ignore nested operator + if tc.type1.operator.is_none() { + return cat_operation(cddl, &tc.type1.type2, controller, literals); + } + } + + return Err("invalid target type in .cat control operator".to_string()); + } + Type2::UTF8ByteString { value, .. } => match std::str::from_utf8(value) { + Ok(value) => match controller { + Type2::TextValue { + value: controller, .. + } => literals.push(format!("{}{}", value, controller)), + Type2::Typename { ident, .. } => { + for controller in string_literals_from_ident(cddl, ident).iter() { + literals.push(format!("{}{}", value, controller)); + } + } + Type2::UTF8ByteString { + value: controller, .. + } => match std::str::from_utf8(controller) { + Ok(controller) => literals.push(format!("{}{}", value, controller)), + Err(e) => return Err(format!("error parsing byte string: {}", e)), + }, + Type2::ParenthesizedType { pt: controller, .. } => { + for controller in controller.type_choices.iter() { + if controller.type1.operator.is_none() { + cat_operation(cddl, target, &controller.type1.type2, literals)?; + } + } + } + _ => unimplemented!(), + }, + Err(e) => return Err(format!("error parsing byte string: {}", e)), + }, + _ => unimplemented!(), + } + + Ok(()) +}