use serde_json::Value;
use std::marker::PhantomData;
use crate::combinators::{
ZCatch, ZDescribe, ZIntersection, ZPipe, ZRefine, ZSuperRefine, ZTransform, ZUnion2,
};
use crate::error::VldError;
use crate::input::VldInput;
use crate::modifiers::{ZDefault, ZNullable, ZNullish, ZOptional};
pub trait VldSchema: Sized {
type Output;
fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError>;
fn parse<I: VldInput + ?Sized>(&self, input: &I) -> Result<Self::Output, VldError> {
let json = input.to_json_value()?;
self.parse_value(&json)
}
#[cfg(feature = "serialize")]
fn validate<T: serde::Serialize>(&self, value: &T) -> Result<Self::Output, VldError> {
let json = serde_json::to_value(value).map_err(|e| {
VldError::single(
crate::error::IssueCode::ParseError,
format!("Serialization error: {}", e),
)
})?;
self.parse_value(&json)
}
#[cfg(feature = "serialize")]
fn is_valid<T: serde::Serialize>(&self, value: &T) -> bool {
self.validate(value).is_ok()
}
fn optional(self) -> ZOptional<Self> {
ZOptional::new(self)
}
fn nullable(self) -> ZNullable<Self> {
ZNullable::new(self)
}
fn with_default(self, value: Self::Output) -> ZDefault<Self>
where
Self::Output: Clone,
{
ZDefault::new(self, value)
}
fn refine<F>(self, check: F, message: &str) -> ZRefine<Self, F>
where
F: Fn(&Self::Output) -> bool,
{
ZRefine::new(self, check, message)
}
fn transform<F, U>(self, f: F) -> ZTransform<Self, F, U>
where
F: Fn(Self::Output) -> U,
{
ZTransform::new(self, f)
}
fn nullish(self) -> ZNullish<Self> {
ZNullish::new(self)
}
fn catch(self, fallback: Self::Output) -> ZCatch<Self>
where
Self::Output: Clone,
{
ZCatch::new(self, fallback)
}
fn pipe<S: VldSchema>(self, next: S) -> ZPipe<Self, S>
where
Self::Output: serde::Serialize,
{
ZPipe::new(self, next)
}
fn describe(self, description: &str) -> ZDescribe<Self> {
ZDescribe::new(self, description)
}
fn super_refine<F>(self, check: F) -> ZSuperRefine<Self, F>
where
F: Fn(&Self::Output, &mut VldError),
{
ZSuperRefine::new(self, check)
}
fn or<B: VldSchema>(self, other: B) -> ZUnion2<Self, B> {
ZUnion2::new(self, other)
}
fn and<B: VldSchema>(self, other: B) -> ZIntersection<Self, B> {
ZIntersection::new(self, other)
}
fn message(self, msg: impl Into<String>) -> crate::combinators::ZMessage<Self> {
crate::combinators::ZMessage::new(self, msg)
}
}
pub trait VldParse: Sized {
fn vld_parse_value(value: &serde_json::Value) -> Result<Self, crate::error::VldError>;
}
pub struct NestedSchema<T, F>
where
F: Fn(&Value) -> Result<T, VldError>,
{
parse_fn: F,
#[allow(dead_code)]
pub(crate) name: Option<&'static str>,
#[allow(dead_code)]
pub(crate) json_schema_fn: Option<fn() -> serde_json::Value>,
_phantom: PhantomData<T>,
}
impl<T, F> NestedSchema<T, F>
where
F: Fn(&Value) -> Result<T, VldError>,
{
pub fn new(f: F) -> Self {
Self {
parse_fn: f,
name: None,
json_schema_fn: None,
_phantom: PhantomData,
}
}
pub fn new_named(
f: F,
name: &'static str,
json_schema_fn: Option<fn() -> serde_json::Value>,
) -> Self {
Self {
parse_fn: f,
name: Some(name),
json_schema_fn,
_phantom: PhantomData,
}
}
}
impl<T, F> VldSchema for NestedSchema<T, F>
where
F: Fn(&Value) -> Result<T, VldError>,
{
type Output = T;
fn parse_value(&self, value: &Value) -> Result<T, VldError> {
(self.parse_fn)(value)
}
}