use super::*;
use crate::element::*;
use crate::error::{Error, Result};
use crate::Hash;
use serde::{Deserialize, Deserializer, Serialize};
use std::default::Default;
#[inline]
fn is_false(v: &bool) -> bool {
!v
}
fn get_validator<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<Box<Validator>>, D::Error> {
Ok(Some(Box::new(Validator::deserialize(deserializer)?)))
}
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct HashValidator {
#[serde(skip_serializing_if = "String::is_empty")]
pub comment: String,
#[serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "get_validator"
)]
pub link: Option<Box<Validator>>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub schema: Vec<Option<Hash>>,
#[serde(rename = "in", skip_serializing_if = "Vec::is_empty")]
pub in_list: Vec<Hash>,
#[serde(rename = "nin", skip_serializing_if = "Vec::is_empty")]
pub nin_list: Vec<Hash>,
#[serde(skip_serializing_if = "is_false")]
pub query: bool,
#[serde(skip_serializing_if = "is_false")]
pub link_ok: bool,
#[serde(skip_serializing_if = "is_false")]
pub schema_ok: bool,
}
impl HashValidator {
pub fn new() -> Self {
Self::default()
}
pub fn link(mut self, link: Validator) -> Self {
self.link = Some(Box::new(link));
self
}
pub fn schema_add(mut self, add: impl Into<Hash>) -> Self {
self.schema.push(Some(add.into()));
self
}
pub fn schema_self(mut self) -> Self {
self.schema.push(None);
self
}
pub fn in_add(mut self, add: impl Into<Hash>) -> Self {
self.in_list.push(add.into());
self
}
pub fn nin_add(mut self, add: impl Into<Hash>) -> Self {
self.nin_list.push(add.into());
self
}
pub fn query(mut self, query: bool) -> Self {
self.query = query;
self
}
pub fn link_ok(mut self, link_ok: bool) -> Self {
self.link_ok = link_ok;
self
}
pub fn schema_ok(mut self, schema_ok: bool) -> Self {
self.schema_ok = schema_ok;
self
}
pub fn build(self) -> Validator {
Validator::Hash(Box::new(self))
}
pub(crate) fn validate<'c>(
&'c self,
parser: &mut Parser,
checklist: &mut Option<Checklist<'c>>,
) -> Result<()> {
let elem = parser
.next()
.ok_or_else(|| Error::FailValidate("Expected a hash".to_string()))??;
let val = if let Element::Hash(v) = elem {
v
} else {
return Err(Error::FailValidate(format!(
"Expected Hash, got {}",
elem.name()
)));
};
if !self.in_list.is_empty() && !self.in_list.iter().any(|v| *v == val) {
return Err(Error::FailValidate(
"Timestamp is not on `in` list".to_string(),
));
}
if self.nin_list.iter().any(|v| *v == val) {
return Err(Error::FailValidate(
"Timestamp is on `nin` list".to_string(),
));
}
if let Some(checklist) = checklist {
match (self.schema.is_empty(), self.link.as_ref()) {
(false, Some(link)) => checklist.insert(val, Some(&self.schema), Some(link)),
(false, None) => checklist.insert(val, Some(&self.schema), None),
(true, Some(link)) => checklist.insert(val, None, Some(link)),
_ => (),
}
}
Ok(())
}
fn query_check_self(&self, types: &BTreeMap<String, Validator>, other: &HashValidator) -> bool {
let initial_check = (self.query || (other.in_list.is_empty() && other.nin_list.is_empty()))
&& (self.link_ok || other.link.is_none())
&& (self.schema_ok || other.schema.is_empty());
if !initial_check {
return false;
}
if self.link_ok {
match (&self.link, &other.link) {
(None, None) => true,
(Some(_), None) => true,
(None, Some(_)) => false,
(Some(s), Some(o)) => s.query_check(types, o.as_ref()),
}
} else {
true
}
}
pub(crate) fn query_check(
&self,
types: &BTreeMap<String, Validator>,
other: &Validator,
) -> bool {
match other {
Validator::Hash(other) => self.query_check_self(types, other),
Validator::Multi(list) => list.iter().all(|other| match other {
Validator::Hash(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 = HashValidator::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 = HashValidator::deserialize(&mut de).unwrap();
println!("{}", de.get_debug().unwrap());
assert_eq!(schema, decoded);
}
#[test]
fn verify_simple() {
let mut schema = HashValidator {
link: Some(Box::new(HashValidator::default().build())),
..HashValidator::default()
};
schema
.schema
.push(Some(Hash::new(b"Pretend I am a real schema")));
schema.schema.push(None);
let mut ser = FogSerializer::default();
Hash::new(b"Data to make a hash")
.serialize(&mut ser)
.unwrap();
let encoded = ser.finish();
let mut parser = Parser::new(&encoded);
let fake_schema = Hash::new(b"Pretend I, too, am a real schema");
let fake_types = BTreeMap::new();
let mut checklist = Some(Checklist::new(&fake_schema, &fake_types));
schema
.validate(&mut parser, &mut checklist)
.expect("should succeed as a validator");
}
}