use std::borrow::Cow;
use std::fmt;
use crate::error::{FieldPath, FormaError, FormaIssue, IssueCode, IssueParams};
use crate::value::Value;
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a formars `Schema`",
label = "expected a schema built from the `string()`, `number()`, `bool()` or `coerced()` builders"
)]
pub trait Schema {
type Input;
type Output;
fn parse(&self, input: &Self::Input) -> Result<Self::Output, FormaError>;
}
pub trait DynSchema: Send + Sync {
fn validate_value(&self, v: &Value) -> Vec<FormaIssue>;
fn shape(&self) -> &ShapeNode;
fn metadata(&self) -> &FieldMeta;
}
#[derive(Clone, Debug, PartialEq)]
pub struct ShapeNode {
pub kind: ShapeKind,
pub constraints: Vec<ConstraintDesc>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ShapeKind {
Str,
Number {
integer: bool,
},
Bool,
Coerced,
Object {
fields: Vec<ObjectFieldDesc>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectFieldDesc {
pub key: Box<str>,
pub child: ShapeNode,
}
pub mod sealed {
pub trait Sealed {}
}
pub trait ObjectChild: fmt::Debug + Send + Sync + sealed::Sealed {
fn validate_at(
&self,
v: &Value,
path: &FieldPath,
fail_fast: bool,
) -> Result<Value, Vec<FormaIssue>>;
fn shape_node(&self) -> ShapeNode;
fn meta(&self) -> &FieldMeta;
fn clone_boxed(&self) -> Box<dyn ObjectChild>;
}
#[derive(Debug)]
pub struct Nested<S> {
inner: S,
}
impl<S> Nested<S> {
#[must_use]
pub fn new(inner: S) -> Self {
Self { inner }
}
}
impl<S> ObjectChild for Nested<S>
where
S: fmt::Debug + Send + Sync + AsRef<crate::types::object::ObjectSchema> + Clone + 'static,
{
fn validate_at(
&self,
v: &Value,
path: &FieldPath,
fail_fast: bool,
) -> Result<Value, Vec<FormaIssue>> {
self.inner.as_ref().validate_at(v, path, fail_fast)
}
fn shape_node(&self) -> ShapeNode {
self.inner.as_ref().shape_node()
}
fn meta(&self) -> &FieldMeta {
self.inner.as_ref().meta()
}
fn clone_boxed(&self) -> Box<dyn ObjectChild> {
Box::new(Self {
inner: self.inner.clone(),
})
}
}
impl<S> sealed::Sealed for Nested<S> {}
#[derive(Clone, Debug, PartialEq)]
pub struct ConstraintDesc {
pub code: IssueCode,
pub params: IssueParams,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FieldMeta {
pub label: Option<Cow<'static, str>>,
pub description: Option<Cow<'static, str>>,
pub placeholder: Option<Cow<'static, str>>,
pub extra: Vec<(Cow<'static, str>, Value)>,
}
pub(crate) fn issue_at_root(
code: IssueCode,
message: Cow<'static, str>,
params: IssueParams,
) -> FormaIssue {
FormaIssue {
path: crate::error::FieldPath::ROOT,
code,
message,
params,
}
}