Skip to main content

dmc_schema/
lib.rs

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