1#[macro_export]
21macro_rules! shape {
22 ({ $($body:tt)* }) => { $crate::__shape_obj!([] $($body)*) };
23 ([ $($body:tt)* ]) => { $crate::__shape_arr!([] $($body)*) };
24 ($t:ident) => { $crate::__shape_token!($t) };
25 ($e:expr) => { $crate::Spec::from($e) };
26}
27
28#[doc(hidden)]
29#[macro_export]
30macro_rules! __shape_token {
31 (String) => {
32 $crate::Spec::from($crate::Token::String)
33 };
34 (Number) => {
35 $crate::Spec::from($crate::Token::Number)
36 };
37 (Boolean) => {
38 $crate::Spec::from($crate::Token::Boolean)
39 };
40 (Object) => {
41 $crate::Spec::from($crate::Token::Object)
42 };
43 (Array) => {
44 $crate::Spec::from($crate::Token::Array)
45 };
46 (Function) => {
47 $crate::Spec::from($crate::Token::Function)
48 };
49 (Integer) => {
50 $crate::Spec::from($crate::Token::Integer)
51 };
52 (Date) => {
53 $crate::Spec::from($crate::Token::Date)
54 };
55 (BigInt) => {
56 $crate::Spec::from($crate::Token::BigInt)
57 };
58 (Any) => {
59 $crate::Spec::from($crate::Token::Any)
60 };
61 ($other:ident) => {
62 $crate::Spec::from($other)
63 };
64}
65
66#[doc(hidden)]
67#[macro_export]
68macro_rules! __shape_key {
69 ($k:literal) => {
70 $k.to_string()
71 };
72 ($k:ident) => {
73 stringify!($k).to_string()
74 };
75}
76
77#[doc(hidden)]
78#[macro_export]
79macro_rules! __shape_obj {
80 ([$($acc:tt)*]) => { $crate::Spec::Obj(vec![$($acc)*]) };
81 ([$($acc:tt)*] , $($rest:tt)*) => { $crate::__shape_obj!([$($acc)*] $($rest)*) };
82 ([$($acc:tt)*] $k:literal : { $($v:tt)* } $($rest:tt)*) => {
83 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::shape!({ $($v)* })),] $($rest)*)
84 };
85 ([$($acc:tt)*] $k:ident : { $($v:tt)* } $($rest:tt)*) => {
86 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::shape!({ $($v)* })),] $($rest)*)
87 };
88 ([$($acc:tt)*] $k:literal : [ $($v:tt)* ] $($rest:tt)*) => {
89 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::shape!([ $($v)* ])),] $($rest)*)
90 };
91 ([$($acc:tt)*] $k:ident : [ $($v:tt)* ] $($rest:tt)*) => {
92 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::shape!([ $($v)* ])),] $($rest)*)
93 };
94 ([$($acc:tt)*] $k:literal : $v:ident , $($rest:tt)*) => {
95 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::__shape_token!($v)),] $($rest)*)
96 };
97 ([$($acc:tt)*] $k:literal : $v:ident) => {
98 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::__shape_token!($v)),])
99 };
100 ([$($acc:tt)*] $k:ident : $v:ident , $($rest:tt)*) => {
101 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::__shape_token!($v)),] $($rest)*)
102 };
103 ([$($acc:tt)*] $k:ident : $v:ident) => {
104 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::__shape_token!($v)),])
105 };
106 ([$($acc:tt)*] $k:literal : $v:expr , $($rest:tt)*) => {
107 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::Spec::from($v)),] $($rest)*)
108 };
109 ([$($acc:tt)*] $k:literal : $v:expr) => {
110 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::Spec::from($v)),])
111 };
112 ([$($acc:tt)*] $k:ident : $v:expr , $($rest:tt)*) => {
113 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::Spec::from($v)),] $($rest)*)
114 };
115 ([$($acc:tt)*] $k:ident : $v:expr) => {
116 $crate::__shape_obj!([$($acc)* ($crate::__shape_key!($k), $crate::Spec::from($v)),])
117 };
118}
119
120#[doc(hidden)]
121#[macro_export]
122macro_rules! __shape_arr {
123 ([$($acc:tt)*]) => { $crate::Spec::Arr(vec![$($acc)*]) };
124 ([$($acc:tt)*] , $($rest:tt)*) => { $crate::__shape_arr!([$($acc)*] $($rest)*) };
125 ([$($acc:tt)*] { $($v:tt)* } $($rest:tt)*) => {
126 $crate::__shape_arr!([$($acc)* $crate::shape!({ $($v)* }),] $($rest)*)
127 };
128 ([$($acc:tt)*] [ $($v:tt)* ] $($rest:tt)*) => {
129 $crate::__shape_arr!([$($acc)* $crate::shape!([ $($v)* ]),] $($rest)*)
130 };
131 ([$($acc:tt)*] $v:ident , $($rest:tt)*) => {
132 $crate::__shape_arr!([$($acc)* $crate::__shape_token!($v),] $($rest)*)
133 };
134 ([$($acc:tt)*] $v:ident) => {
135 $crate::__shape_arr!([$($acc)* $crate::__shape_token!($v),])
136 };
137 ([$($acc:tt)*] $v:expr , $($rest:tt)*) => {
138 $crate::__shape_arr!([$($acc)* $crate::Spec::from($v),] $($rest)*)
139 };
140 ([$($acc:tt)*] $v:expr) => {
141 $crate::__shape_arr!([$($acc)* $crate::Spec::from($v),])
142 };
143}
144
145#[cfg(test)]
146mod tests {
147 use crate::builders::min;
148 use crate::{Schema, Spec, Token, Value};
149
150 fn j(s: &str) -> Value {
151 Value::from(serde_json::from_str::<serde_json::Value>(s).unwrap())
152 }
153
154 #[test]
155 fn writes_specs_as_examples() {
156 let spec = shape!({
157 "name": String,
158 port: 8080,
159 "tags": [String],
160 "pair": [Number, "x"],
161 "age": min(0, Token::Integer),
162 "addr": { "zip": String, "lines": [{ "text": String }] },
163 "empty": {},
164 "anything": Any,
165 });
166 let s = Schema::new(spec);
167 let out = s
168 .validate(j(
169 r#"{"name":"x","pair":[1],"age":3,"addr":{"zip":"1","lines":[{"text":"t"}]},"empty":{"k":1}}"#,
170 ))
171 .unwrap();
172 assert_eq!(
173 serde_json::Value::from(out),
174 serde_json::json!({"name":"x","port":8080,"tags":[],"pair":[1,"x"],"age":3,"addr":{"zip":"1","lines":[{"text":"t"}]},"empty":{"k":1}})
175 );
176 assert!(matches!(shape!(String), Spec::Token(Token::String)));
177 assert!(matches!(shape!(Number), Spec::Token(Token::Number)));
178 assert!(matches!(shape!(Boolean), Spec::Token(Token::Boolean)));
179 assert!(matches!(shape!(Object), Spec::Token(Token::Object)));
180 assert!(matches!(shape!(Array), Spec::Token(Token::Array)));
181 assert!(matches!(shape!(Function), Spec::Token(Token::Function)));
182 assert!(matches!(shape!(Integer), Spec::Token(Token::Integer)));
183 assert!(matches!(shape!(Date), Spec::Token(Token::Date)));
184 assert!(matches!(shape!(BigInt), Spec::Token(Token::BigInt)));
185 assert!(matches!(shape!(Any), Spec::Token(Token::Any)));
186 let node = min(1, Token::Number);
187 assert!(matches!(shape!(node), Spec::Node(_)));
188 assert!(matches!(shape!(5), Spec::Value(Value::Num(_))));
189 assert!(matches!(shape!([]), Spec::Arr(_)));
190 assert!(matches!(shape!([1,]), Spec::Arr(_)));
191 assert!(matches!(shape!({ a: 1, }), Spec::Obj(_)));
192 assert!(matches!(
193 shape!({ "a": [Number], b: { c: String } }),
194 Spec::Obj(_)
195 ));
196 }
197}