use super::*;
use crate::error::{Error, Result};
use crate::{de::FogDeserializer, element::*, value::Value, value_ref::ValueRef};
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
}
fn get_str_validator<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<Box<StrValidator>>, D::Error> {
Ok(Some(Box::new(StrValidator::deserialize(deserializer)?)))
}
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 = "Option::is_none",
deserialize_with = "get_str_validator"
)]
pub keys: Option<Box<StrValidator>>,
#[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(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 = "Vec::is_empty")]
pub same_len: Vec<String>,
#[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 same_len_ok: bool,
}
impl Default for MapValidator {
fn default() -> Self {
Self {
comment: String::new(),
max_len: u32::MAX,
min_len: u32::MIN,
keys: None,
values: None,
req: BTreeMap::new(),
opt: BTreeMap::new(),
in_list: Vec::new(),
nin_list: Vec::new(),
same_len: Vec::new(),
query: false,
size: false,
map_ok: false,
same_len_ok: 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 keys(mut self, keys: StrValidator) -> Self {
self.keys = Some(Box::new(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 same_len_add(mut self, add: impl Into<String>) -> Self {
self.same_len.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 same_len_ok(mut self, same_len_ok: bool) -> Self {
self.same_len_ok = same_len_ok;
self
}
pub fn build(self) -> Validator {
Validator::Map(Box::new(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;
let mut array_len: Option<usize> = None;
let mut array_len_cnt = 0;
for _ in 0..len {
let elem = parser
.next()
.ok_or_else(|| Error::FailValidate("expected a key string".to_string()))??;
let key = if let Element::Str(v) = elem {
v
} else {
return Err(Error::FailValidate(format!(
"expected Str, got {}",
elem.name()
)));
};
if self.same_len.iter().any(|s| s == key) {
let elem = parser.peek().ok_or_else(|| {
Error::FailValidate("expected an array element".to_string())
})??;
let Element::Array(len) = elem else {
return Err(Error::FailValidate(format!(
"expected array for key {:?}, got {}",
key, elem.name()
)));
};
if let Some(array_len) = array_len {
if array_len != len {
return Err(Error::FailValidate(format!(
"expected array of length {} for key {:?}, but length was {}",
array_len, key, len
)));
}
} else {
array_len = Some(len);
}
array_len_cnt += 1;
}
let (p, c) = if let Some(validator) = self.req.get(key) {
reqs_found += 1;
validator.validate(types, parser, checklist)?
} else if let Some(validator) = self.opt.get(key) {
validator.validate(types, parser, checklist)?
} else if let Some(validator) = &self.values {
if let Some(keys) = &self.keys {
keys.validate_str(key)?;
}
validator.validate(types, parser, checklist)?
} else {
return Err(Error::FailValidate(format!(
"Map key {:?} has no corresponding validator",
key
)));
};
parser = p;
checklist = c;
}
if array_len.is_some() && array_len_cnt != self.same_len.len() {
return Err(Error::FailValidate(
"Map had some, but not all, of the keys listed in `same_len`".into(),
));
}
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.same_len_ok || other.same_len.is_empty())
&& (self.map_ok
|| (other.req.is_empty()
&& other.opt.is_empty()
&& other.keys.is_none()
&& other.values.is_none()));
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 keys_ok = match (&self.keys, &other.keys) {
(None, None) => true,
(Some(_), None) => true,
(None, Some(_)) => false,
(Some(s), Some(o)) => s.query_check_str(o.as_ref()),
};
if !keys_ok {
return false;
}
let req_ok = other.req.iter().all(|(ko, kv)| {
self.req
.get(ko)
.or_else(|| self.opt.get(ko))
.or(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(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);
}
#[test]
fn same_len() {
let schema = MapValidator::new()
.values(ArrayValidator::new().build())
.same_len_add("a")
.same_len_add("b");
#[derive(Clone, Debug, Serialize, Deserialize)]
struct Test {
#[serde(skip_serializing_if = "Vec::is_empty")]
a: Vec<u8>,
#[serde(skip_serializing_if = "Vec::is_empty")]
b: Vec<u8>,
}
let test = Test {
a: vec![0, 1],
b: vec![2, 3],
};
let mut ser = FogSerializer::default();
test.serialize(&mut ser).unwrap();
let serialized = ser.finish();
let parser = Parser::new(&serialized);
assert!(schema.validate(&BTreeMap::new(), parser, None).is_ok());
let test = Test {
a: vec![],
b: vec![],
};
let mut ser = FogSerializer::default();
test.serialize(&mut ser).unwrap();
let serialized = ser.finish();
let parser = Parser::new(&serialized);
assert!(schema.validate(&BTreeMap::new(), parser, None).is_ok());
let test = Test {
a: vec![2, 3],
b: vec![],
};
let mut ser = FogSerializer::default();
test.serialize(&mut ser).unwrap();
let serialized = ser.finish();
let parser = Parser::new(&serialized);
assert!(schema.validate(&BTreeMap::new(), parser, None).is_err());
let test = Test {
a: vec![2, 3],
b: vec![1, 2, 3],
};
let mut ser = FogSerializer::default();
test.serialize(&mut ser).unwrap();
let serialized = ser.finish();
let parser = Parser::new(&serialized);
assert!(schema.validate(&BTreeMap::new(), parser, None).is_err());
}
}