Skip to main content

dmc_schema/
lib.rs

1//! Schema builder. `s::*`
2//!
3//! ```
4//! use dmc_schema::{s, BoxSchema, Ctx, Schema};
5//! use serde_json::json;
6//!
7//! let schema = s::object(vec![
8//!     ("title".into(), s::string().max(99).boxed()),
9//!     ("draft".into(), s::default_(s::boolean().boxed(), json!(false)).boxed()),
10//! ]);
11//! let ctx = Ctx::empty();
12//! let out = schema.parse(&json!({"title": "Hello"}), &ctx).unwrap();
13//! assert_eq!(out["title"], "Hello");
14//! assert_eq!(out["draft"], false);
15//! ```
16
17mod asset;
18mod compile;
19mod ctx;
20mod error;
21mod markdown;
22mod modifiers;
23mod primitives;
24
25pub use asset::*;
26pub use compile::compile_descriptor;
27pub use ctx::{AssetPipeline, Ctx};
28pub use error::ValidationError;
29pub use markdown::*;
30pub use modifiers::*;
31pub use primitives::*;
32
33use serde_json::Value;
34
35pub trait Schema: Send + Sync {
36  fn parse(&self, value: &Value, ctx: &Ctx) -> Result<Value, ValidationError>;
37}
38
39pub mod s {
40  use super::*;
41
42  pub fn string() -> StringSchema {
43    StringSchema::default()
44  }
45  pub fn number() -> NumberSchema {
46    NumberSchema::default()
47  }
48  pub fn boolean() -> BooleanSchema {
49    BooleanSchema
50  }
51  pub fn array(item: Box<dyn Schema>) -> ArraySchema {
52    ArraySchema { item, min: None, max: None }
53  }
54  pub fn object(fields: Vec<(String, Box<dyn Schema>)>) -> ObjectSchema {
55    ObjectSchema { fields, passthrough: false }
56  }
57  pub fn enum_(variants: Vec<Value>) -> EnumSchema {
58    EnumSchema { variants }
59  }
60  pub fn literal(expected: Value) -> LiteralSchema {
61    LiteralSchema { expected }
62  }
63  pub fn union(variants: Vec<Box<dyn Schema>>) -> UnionSchema {
64    UnionSchema { variants }
65  }
66  pub fn record(value: Box<dyn Schema>) -> RecordSchema {
67    RecordSchema { value }
68  }
69  pub fn tuple(items: Vec<Box<dyn Schema>>) -> TupleSchema {
70    TupleSchema { items }
71  }
72  pub fn intersection(left: Box<dyn Schema>, right: Box<dyn Schema>) -> IntersectionSchema {
73    IntersectionSchema { left, right }
74  }
75  pub fn discriminated_union(
76    discriminator: impl Into<String>,
77    variants: Vec<Box<dyn Schema>>,
78  ) -> DiscriminatedUnionSchema {
79    DiscriminatedUnionSchema { discriminator: discriminator.into(), variants }
80  }
81  pub fn coerce_string() -> CoerceSchema {
82    CoerceSchema { target: CoerceTarget::String }
83  }
84  pub fn coerce_number() -> CoerceSchema {
85    CoerceSchema { target: CoerceTarget::Number }
86  }
87  pub fn coerce_boolean() -> CoerceSchema {
88    CoerceSchema { target: CoerceTarget::Boolean }
89  }
90  pub fn coerce_date() -> CoerceSchema {
91    CoerceSchema { target: CoerceTarget::Date }
92  }
93
94  pub fn optional(inner: Box<dyn Schema>) -> OptionalSchema {
95    OptionalSchema { inner }
96  }
97  pub fn nullable(inner: Box<dyn Schema>) -> NullableSchema {
98    NullableSchema { inner }
99  }
100  pub fn default_(inner: Box<dyn Schema>, fallback: Value) -> DefaultSchema {
101    DefaultSchema { inner, fallback }
102  }
103  pub fn transform<F>(inner: Box<dyn Schema>, func: F) -> TransformSchema
104  where
105    F: Fn(Value) -> Value + Send + Sync + 'static,
106  {
107    TransformSchema { inner, func: Box::new(func) }
108  }
109  pub fn refine<F>(inner: Box<dyn Schema>, predicate: F) -> RefineSchema
110  where
111    F: Fn(&Value) -> Result<(), String> + Send + Sync + 'static,
112  {
113    RefineSchema { inner, predicate: Box::new(predicate) }
114  }
115  pub fn super_refine<F>(inner: Box<dyn Schema>, predicate: F) -> SuperRefineSchema
116  where
117    F: Fn(&Value, &mut Vec<String>) + Send + Sync + 'static,
118  {
119    SuperRefineSchema { inner, predicate: Box::new(predicate) }
120  }
121
122  pub fn raw() -> RawSchema {
123    RawSchema
124  }
125  pub fn markdown() -> MarkdownSchema {
126    MarkdownSchema
127  }
128  pub fn mdx() -> MdxSchema {
129    MdxSchema
130  }
131  pub fn toc() -> TocSchema {
132    TocSchema
133  }
134  pub fn metadata() -> MetadataSchema {
135    MetadataSchema
136  }
137  pub fn excerpt() -> ExcerptSchema {
138    ExcerptSchema::default()
139  }
140  pub fn path() -> PathSchema {
141    PathSchema::default()
142  }
143  pub fn slug() -> SlugSchema {
144    SlugSchema::default()
145  }
146  pub fn unique() -> UniqueSchema {
147    UniqueSchema::default()
148  }
149  pub fn isodate() -> IsodateSchema {
150    IsodateSchema
151  }
152  pub fn file() -> FileSchema {
153    FileSchema::default()
154  }
155  pub fn image() -> ImageSchema {
156    ImageSchema::default()
157  }
158}
159
160pub trait BoxSchema: Schema + Sized + 'static {
161  fn boxed(self) -> Box<dyn Schema> {
162    Box::new(self)
163  }
164}
165impl<T: Schema + Sized + 'static> BoxSchema for T {}