use super::*;
use crate::error::{Error, Result};
use crate::{de::FogDeserializer, element::*, value::Value, value_ref::ValueRef};
use regex::Regex;
use serde::{Deserialize, Deserializer, Serialize};
use std::default::Default;
#[inline]
fn is_false(v: &bool) -> bool {
!v
}
#[inline]
fn u32_is_zero(v: &u32) -> bool {
*v == 0
}
#[inline]
fn u32_is_max(v: &u32) -> bool {
*v == u32::MAX
}
#[inline]
fn normalize_is_none(v: &Normalize) -> bool {
matches!(v, Normalize::None)
}
#[inline]
fn key_validator_is_default(v: &KeyValidator) -> bool {
v.matches.is_none()
&& normalize_is_none(&v.normalize)
&& u32_is_max(&v.max_len)
&& u32_is_zero(&v.min_len)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct KeyValidator {
#[serde(skip_serializing_if = "Option::is_none", with = "serde_regex")]
pub matches: Option<Box<Regex>>,
#[serde(skip_serializing_if = "normalize_is_none")]
pub normalize: Normalize,
#[serde(skip_serializing_if = "u32_is_max")]
pub max_len: u32,
#[serde(skip_serializing_if = "u32_is_zero")]
pub min_len: u32,
}
impl KeyValidator {
pub fn new() -> Self {
Self::default()
}
pub fn matches(mut self, matches: Regex) -> Self {
self.matches = Some(Box::new(matches));
self
}
pub fn normalize(mut self, normalize: Normalize) -> Self {
self.normalize = normalize;
self
}
pub fn max_len(mut self, max_len: u32) -> Self {
self.max_len = max_len;
self
}
pub fn min_len(mut self, min_len: u32) -> Self {
self.min_len = min_len;
self
}
fn validate<'a>(&self, parser: &mut Parser<'a>) -> Result<&'a str> {
let elem = parser
.next()
.ok_or_else(|| Error::FailValidate("expected a key string".to_string()))??;
let val = if let Element::Str(v) = elem {
v
} else {
return Err(Error::FailValidate(format!(
"expected Str key, got {}",
elem.name()
)));
};
if (val.len() as u32) > self.max_len {
return Err(Error::FailValidate(
"Key is longer than max_len".to_string(),
));
}
if (val.len() as u32) < self.min_len {
return Err(Error::FailValidate(
"Key is shorter than min_len".to_string(),
));
}
use unicode_normalization::{
is_nfc_quick, is_nfkc_quick, IsNormalized, UnicodeNormalization,
};
if let Some(ref regex) = self.matches {
match self.normalize {
Normalize::None => {
if !regex.is_match(val) {
return Err(Error::FailValidate(
"Key doesn't match regular expression".to_string(),
));
}
}
Normalize::NFC => {
let temp_string: String;
let val = match is_nfc_quick(val.chars()) {
IsNormalized::Yes => val,
_ => {
temp_string = val.nfc().collect::<String>();
temp_string.as_str()
}
};
if !regex.is_match(val) {
return Err(Error::FailValidate(
"Key doesn't match regular expression".to_string(),
));
}
}
Normalize::NFKC => {
let temp_string: String;
let val = match is_nfkc_quick(val.chars()) {
IsNormalized::Yes => val,
_ => {
temp_string = val.nfkc().collect::<String>();
temp_string.as_str()
}
};
if !regex.is_match(val) {
return Err(Error::FailValidate(
"Key doesn't match regular expression".to_string(),
));
}
}
}
}
Ok(val)
}
}
impl PartialEq for KeyValidator {
fn eq(&self, rhs: &Self) -> bool {
(self.normalize == rhs.normalize)
&& (self.max_len == rhs.max_len)
&& (self.min_len == rhs.min_len)
&& match (&self.matches, &rhs.matches) {
(None, None) => true,
(Some(_), None) => false,
(None, Some(_)) => false,
(Some(lhs), Some(rhs)) => lhs.as_str() == rhs.as_str(),
}
}
}
impl Default for KeyValidator {
fn default() -> Self {
Self {
matches: None,
max_len: u32::MAX,
min_len: u32::MIN,
normalize: Normalize::None,
}
}
}
fn get_validator<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<Box<Validator>>, D::Error> {
Ok(Some(Box::new(Validator::deserialize(deserializer)?)))
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct MapValidator {
#[serde(skip_serializing_if = "String::is_empty")]
pub comment: String,
#[serde(skip_serializing_if = "u32_is_max")]
pub max_len: u32,
#[serde(skip_serializing_if = "u32_is_zero")]
pub min_len: u32,
#[serde(skip_serializing_if = "key_validator_is_default")]
pub keys: KeyValidator,
#[serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "get_validator"
)]
pub values: Option<Box<Validator>>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub req: BTreeMap<String, Validator>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub opt: BTreeMap<String, Validator>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub ban: Vec<String>,
#[serde(rename = "in", skip_serializing_if = "Vec::is_empty")]
pub in_list: Vec<BTreeMap<String, Value>>,
#[serde(rename = "nin", skip_serializing_if = "Vec::is_empty")]
pub nin_list: Vec<BTreeMap<String, Value>>,
#[serde(skip_serializing_if = "is_false")]
pub query: bool,
#[serde(skip_serializing_if = "is_false")]
pub size: bool,
#[serde(skip_serializing_if = "is_false")]
pub map_ok: bool,
#[serde(skip_serializing_if = "is_false")]
pub match_keys: bool,
#[serde(skip_serializing_if = "is_false")]
pub len_keys: bool,
}
impl Default for MapValidator {
fn default() -> Self {
Self {
comment: String::new(),
max_len: u32::MAX,
min_len: u32::MIN,
keys: KeyValidator::default(),
values: None,
req: BTreeMap::new(),
opt: BTreeMap::new(),
ban: Vec::new(),
in_list: Vec::new(),
nin_list: Vec::new(),
query: false,
size: false,
map_ok: false,
match_keys: false,
len_keys: false,
}
}
}
impl MapValidator {
pub fn new() -> Self {
Self::default()
}
pub fn comment(mut self, comment: impl Into<String>) -> Self {
self.comment = comment.into();
self
}
pub fn values(mut self, values: Validator) -> Self {
self.values = Some(Box::new(values));
self
}
pub fn req_add(mut self, key: impl Into<String>, req: Validator) -> Self {
self.req.insert(key.into(), req);
self
}
pub fn opt_add(mut self, key: impl Into<String>, opt: Validator) -> Self {
self.opt.insert(key.into(), opt);
self
}
pub fn ban_add(mut self, ban: impl Into<String>) -> Self {
self.ban.push(ban.into());
self
}
pub fn keys(mut self, keys: KeyValidator) -> Self {
self.keys = keys;
self
}
pub fn max_len(mut self, max_len: u32) -> Self {
self.max_len = max_len;
self
}
pub fn min_len(mut self, min_len: u32) -> Self {
self.min_len = min_len;
self
}
pub fn in_add(mut self, add: impl Into<BTreeMap<String, Value>>) -> Self {
self.in_list.push(add.into());
self
}
pub fn nin_add(mut self, add: impl Into<BTreeMap<String, Value>>) -> Self {
self.nin_list.push(add.into());
self
}
pub fn query(mut self, query: bool) -> Self {
self.query = query;
self
}
pub fn size(mut self, size: bool) -> Self {
self.size = size;
self
}
pub fn map_ok(mut self, map_ok: bool) -> Self {
self.map_ok = map_ok;
self
}
pub fn match_keys(mut self, match_keys: bool) -> Self {
self.match_keys = match_keys;
self
}
pub fn len_keys(mut self, len_keys: bool) -> Self {
self.len_keys = len_keys;
self
}
pub fn build(self) -> Validator {
Validator::Map(self)
}
pub(crate) fn validate<'de, 'c>(
&'c self,
types: &'c BTreeMap<String, Validator>,
mut parser: Parser<'de>,
mut checklist: Option<Checklist<'c>>,
) -> Result<(Parser<'de>, Option<Checklist<'c>>)> {
let val_parser = parser.clone();
let elem = parser
.next()
.ok_or_else(|| Error::FailValidate("Expected a map".to_string()))??;
let len = if let Element::Map(len) = elem {
len
} else {
return Err(Error::FailValidate(format!(
"Expected Map, got {}",
elem.name()
)));
};
if (len as u32) > self.max_len {
return Err(Error::FailValidate(format!(
"Map is {} pairs, longer than maximum allowed of {}",
len, self.max_len
)));
}
if (len as u32) < self.min_len {
return Err(Error::FailValidate(format!(
"Map is {} pairs, shorter than minimum allowed of {}",
len, self.min_len
)));
}
if !self.in_list.is_empty() || !self.nin_list.is_empty() {
let mut de = FogDeserializer::from_parser(val_parser);
let map = BTreeMap::<&str, ValueRef>::deserialize(&mut de)?;
if !self.in_list.is_empty() {
let in_pass = self.in_list.iter().any(|v| {
v.len() == map.len()
&& v.iter()
.zip(map.iter())
.all(|((ks, vs), (ko, vo))| (ks == ko) && (vs == vo))
});
if !in_pass {
return Err(Error::FailValidate("Map is not on `in` list".to_string()));
}
}
let nin_pass = !self.nin_list.iter().any(|v| {
v.len() == map.len()
&& v.iter()
.zip(map.iter())
.all(|((ks, vs), (ko, vo))| (ks == ko) && (vs == vo))
});
if !nin_pass {
return Err(Error::FailValidate("Map is on `nin` list".to_string()));
}
}
let mut reqs_found = 0;
for _ in 0..len {
let key = self.keys.validate(&mut parser)?;
if self.ban.iter().any(|k| k == key) {
return Err(Error::FailValidate(format!(
"Map key {:?} is on the ban list",
key
)));
}
let (p, c) = if let Some(validator) = self
.req
.get(key)
.map(|v| {
reqs_found += 1;
v
})
.or_else(|| self.opt.get(key))
.or_else(|| self.values.as_deref())
{
validator.validate(types, parser, checklist)?
} else {
return Err(Error::FailValidate(format!(
"Map key {:?} has no corresponding validator",
key
)));
};
parser = p;
checklist = c;
}
if reqs_found != self.req.len() {
return Err(Error::FailValidate(format!(
"Map did not have all required key-value pairs (missing {})",
reqs_found
)));
}
Ok((parser, checklist))
}
fn query_check_self(&self, types: &BTreeMap<String, Validator>, other: &MapValidator) -> bool {
let initial_check = (self.query || (other.in_list.is_empty() && other.nin_list.is_empty()))
&& (self.size || (u32_is_max(&other.max_len) && u32_is_zero(&other.min_len)))
&& (self.map_ok
|| (other.req.is_empty()
&& other.opt.is_empty()
&& other.ban.is_empty()
&& other.values.is_none()))
&& (self.match_keys || other.keys.matches.is_none())
&& (self.len_keys
|| (u32_is_max(&other.keys.max_len) && u32_is_zero(&other.keys.min_len)));
if !initial_check {
return false;
}
if self.map_ok {
let values_ok = match (&self.values, &other.values) {
(None, None) => true,
(Some(_), None) => true,
(None, Some(_)) => false,
(Some(s), Some(o)) => s.query_check(types, o.as_ref()),
};
if !values_ok {
return false;
}
let req_ok = other.req.iter().all(|(ko, kv)| {
self.req
.get(ko)
.or_else(|| self.opt.get(ko))
.or_else(|| self.values.as_deref())
.map(|v| v.query_check(types, kv))
.unwrap_or(false)
});
if !req_ok {
return false;
}
let opt_ok = other.opt.iter().all(|(ko, kv)| {
self.req
.get(ko)
.or_else(|| self.opt.get(ko))
.or_else(|| self.values.as_deref())
.map(|v| v.query_check(types, kv))
.unwrap_or(false)
});
opt_ok
} else {
true
}
}
pub(crate) fn query_check(
&self,
types: &BTreeMap<String, Validator>,
other: &Validator,
) -> bool {
match other {
Validator::Map(other) => self.query_check_self(types, other),
Validator::Multi(list) => list.iter().all(|other| match other {
Validator::Map(other) => self.query_check_self(types, other),
_ => false,
}),
Validator::Any => true,
_ => false,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{de::FogDeserializer, ser::FogSerializer};
#[test]
fn ser_default() {
let schema = MapValidator::default();
let mut ser = FogSerializer::default();
schema.serialize(&mut ser).unwrap();
let expected: Vec<u8> = vec![0x80];
let actual = ser.finish();
println!("expected: {:x?}", expected);
println!("actual: {:x?}", actual);
assert_eq!(expected, actual);
let mut de = FogDeserializer::with_debug(&actual, " ");
let decoded = MapValidator::deserialize(&mut de).unwrap();
println!("{}", de.get_debug().unwrap());
assert_eq!(schema, decoded);
}
}