use std::borrow::Cow;
use std::fmt;
use deser_core::{Error, ErrorAttachment, ErrorKind};
pub trait Validator<T: ?Sized> {
fn validate(value: &T) -> Result<(), Violation>;
}
impl<T: ?Sized> Validator<T> for () {
fn validate(_value: &T) -> Result<(), Violation> {
Ok(())
}
}
macro_rules! impl_tuple {
($($name:ident),*) => {
impl<T: ?Sized, $($name: Validator<T>),*> Validator<T> for ($($name,)*) {
fn validate(value: &T) -> Result<(), Violation> {
$($name::validate(value)?;)*
Ok(())
}
}
};
}
impl_tuple!(A);
impl_tuple!(A, B);
impl_tuple!(A, B, C);
impl_tuple!(A, B, C, D);
impl_tuple!(A, B, C, D, E);
impl_tuple!(A, B, C, D, E, F);
#[derive(Debug, Clone, PartialEq)]
pub enum Param {
Int(i128),
Str(Cow<'static, str>),
}
impl fmt::Display for Param {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Param::Int(value) => fmt::Display::fmt(value, f),
Param::Str(value) => fmt::Display::fmt(value, f),
}
}
}
impl From<i128> for Param {
fn from(value: i128) -> Param {
Param::Int(value)
}
}
impl From<usize> for Param {
fn from(value: usize) -> Param {
Param::Int(value as i128)
}
}
impl From<&'static str> for Param {
fn from(value: &'static str) -> Param {
Param::Str(Cow::Borrowed(value))
}
}
impl From<String> for Param {
fn from(value: String) -> Param {
Param::Str(Cow::Owned(value))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Violation {
code: Cow<'static, str>,
message: Cow<'static, str>,
params: Vec<(&'static str, Param)>,
}
impl Violation {
pub fn new<C, M>(code: C, message: M) -> Violation
where
C: Into<Cow<'static, str>>,
M: Into<Cow<'static, str>>,
{
Violation {
code: code.into(),
message: message.into(),
params: Vec::new(),
}
}
pub fn with_param<P: Into<Param>>(mut self, name: &'static str, value: P) -> Violation {
self.params.push((name, value.into()));
self
}
pub fn code(&self) -> &str {
&self.code
}
pub fn message(&self) -> &str {
&self.message
}
pub fn params(&self) -> &[(&'static str, Param)] {
&self.params
}
pub fn param(&self, name: &str) -> Option<&Param> {
self.params
.iter()
.find(|(key, _)| *key == name)
.map(|(_, value)| value)
}
pub fn into_error(self) -> Error {
let mut err = Error::new(
ErrorKind::InvalidValue,
format!("invalid value: {}", self.message),
);
err.set_attachment(self);
err
}
}
impl fmt::Display for Violation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl ErrorAttachment for Violation {}