1use crate::builders::fault_node;
5use crate::expr::{expr_apply, key_expr_node, split_key_expr};
6use crate::node::{Kind, Node, Token};
7use crate::spec::Spec;
8use crate::value::Value;
9use indexmap::IndexMap;
10use regex::Regex;
11use std::collections::HashMap;
12
13#[derive(Clone, Debug)]
15pub struct Options {
16 pub key_expr: bool,
18 pub meta: bool,
20 pub meta_suffix: String,
22 pub val_expr: bool,
25 pub val_expr_mark: String,
27}
28
29impl Default for Options {
30 fn default() -> Self {
31 Options {
32 key_expr: true,
33 meta: false,
34 meta_suffix: "$$".to_string(),
35 val_expr: false,
36 val_expr_mark: String::new(),
37 }
38 }
39}
40
41impl Options {
42 fn mark(&self) -> &str {
43 if self.val_expr_mark.is_empty() {
44 &self.meta_suffix
45 } else {
46 &self.val_expr_mark
47 }
48 }
49}
50
51pub fn normalize(spec: Spec) -> Node {
53 normalize_with(spec, &Options::default())
54}
55
56pub fn normalize_with(spec: Spec, opts: &Options) -> Node {
58 match spec {
59 Spec::Node(n) => *n,
60 Spec::Token(t) => type_token_node(t.kind()),
61 Spec::Regex(re) => regexp_node(re),
62 Spec::Regexp(src) => regexp_node_src(&src),
63 Spec::Value(v) => literal_node_with(v, opts),
64 Spec::Arr(items) => normalize_array_with(items, opts),
65 Spec::Obj(pairs) => normalize_object_with(pairs, opts),
66 }
67}
68
69pub fn literal_node(v: Value) -> Node {
71 literal_node_with(v, &Options::default())
72}
73
74fn literal_node_with(v: Value, opts: &Options) -> Node {
75 match v {
76 Value::Undefined => Node::of(Kind::Any),
78 Value::Null => Node {
79 has_default: true,
80 default: Value::Null,
81 literal: Some(Value::Null),
82 ..Node::of(Kind::Null)
83 },
84 Value::Str(s) => Node {
85 has_default: true,
86 empty: s.is_empty(),
87 default: Value::Str(s.clone()),
88 literal: Some(Value::Str(s)),
89 ..Node::of(Kind::String)
90 },
91 Value::Bool(b) => Node {
92 has_default: true,
93 default: Value::Bool(b),
94 literal: Some(Value::Bool(b)),
95 ..Node::of(Kind::Boolean)
96 },
97 Value::Num(n) if n.is_nan() => nan_node(),
98 Value::Num(n) => Node {
99 has_default: true,
100 default: Value::Num(n),
101 literal: Some(Value::Num(n)),
102 ..Node::of(Kind::Number)
103 },
104 Value::BigInt(b) => Node {
105 has_default: true,
106 default: Value::BigInt(b.clone()),
107 literal: Some(Value::BigInt(b)),
108 ..Node::of(Kind::BigInt)
109 },
110 Value::Date(ms) => Node {
111 has_default: true,
112 default: Value::Date(ms),
113 literal: Some(Value::Date(ms)),
114 ..Node::of(Kind::Date)
115 },
116 Value::Func(id) => Node {
117 has_default: true,
118 default: Value::Func(id),
119 literal: Some(Value::Func(id)),
120 ..Node::of(Kind::Function)
121 },
122 Value::Arr(items) => {
123 normalize_array_with(items.into_iter().map(Spec::Value).collect(), opts)
124 }
125 Value::Obj(m) => normalize_object_with(
126 m.into_iter().map(|(k, v)| (k, Spec::Value(v))).collect(),
127 opts,
128 ),
129 }
130}
131
132pub(crate) fn nan_node() -> Node {
133 Node {
134 has_default: true,
135 default: Value::Num(f64::NAN),
136 literal: Some(Value::Num(f64::NAN)),
137 ..Node::of(Kind::NaN)
138 }
139}
140
141pub(crate) fn regexp_node(re: Regex) -> Node {
145 regexp_node_src(re.as_str())
146}
147
148pub(crate) fn regexp_node_src(src: &str) -> Node {
150 match crate::regexp::compile_regexp(src) {
151 Ok(engine) => Node {
152 regexp: Some(engine),
153 regexp_src: src.to_string(),
154 required: true,
155 required_set: true,
156 ..Node::of(Kind::Regexp)
157 },
158 Err(msg) => crate::builders::fault_node(msg),
159 }
160}
161
162pub(crate) fn type_token_node(kind: Kind) -> Node {
165 let mut n = Node {
166 required: kind != Kind::Any,
167 required_set: true,
168 kind_set: true,
169 has_default: true,
170 default: Node::zero_for(kind),
171 ..Node::of(kind)
172 };
173 if kind == Kind::Object {
177 n.obj_rest = Some(Box::new(Node::of(Kind::Any)));
178 }
179 n
180}
181
182pub(crate) fn normalize_array_with(items: Vec<Spec>, opts: &Options) -> Node {
183 let mut n = Node {
184 default: Value::Arr(Vec::new()),
185 ..Node::of(Kind::Array)
186 };
187 match items.len() {
188 0 => {}
189 1 => {
190 n.arr_child = Some(Box::new(normalize_with(
191 items.into_iter().next().unwrap(),
192 opts,
193 )))
194 }
195 _ => n.arr_children = items.into_iter().map(|s| normalize_with(s, opts)).collect(),
196 }
197 n
198}
199
200pub(crate) fn normalize_object_with(pairs: Vec<(String, Spec)>, opts: &Options) -> Node {
203 let mut n = Node {
204 default: Value::Obj(Default::default()),
205 obj_children: IndexMap::with_capacity(pairs.len()),
206 ..Node::of(Kind::Object)
207 };
208 if pairs.is_empty() {
209 n.obj_rest = Some(Box::new(Node::of(Kind::Any)));
210 return n;
211 }
212
213 let names: Vec<String> = pairs.iter().map(|(k, _)| k.clone()).collect();
214 let suffix = opts.meta_suffix.as_str();
215 let meta_base = |k: &str| -> Option<String> {
218 if !opts.meta || k == suffix {
219 return None;
220 }
221 let base = k.strip_suffix(suffix)?;
222 names.iter().any(|n| n == base).then(|| base.to_string())
223 };
224 let mut pending: HashMap<String, IndexMap<String, Value>> = HashMap::new();
225 for (k, v) in &pairs {
226 if let Some(base) = meta_base(k) {
227 let mut meta = IndexMap::new();
228 match v {
229 Spec::Value(Value::Str(s)) => {
230 meta.insert("short".to_string(), Value::Str(s.clone()));
231 }
232 Spec::Obj(entries) => {
233 for (mk, mv) in entries {
234 if let Spec::Value(mv) = mv {
235 meta.insert(mk.clone(), mv.clone());
236 }
237 }
238 }
239 Spec::Value(other) => {
240 meta.insert("value".to_string(), other.clone());
241 }
242 _ => {}
243 }
244 pending.insert(base, meta);
245 }
246 }
247
248 let mut val_expr: Option<String> = None;
249 for (k, v) in pairs {
250 if meta_base(&k).is_some() {
251 continue;
252 }
253 if opts.val_expr && k == opts.mark() {
254 if let Spec::Value(Value::Str(src)) = &v {
255 val_expr = Some(src.clone());
256 continue;
257 }
258 }
259 let (real_key, mut cn) = match split_key_expr(&k).filter(|_| opts.key_expr) {
260 Some((name, src)) => (name, key_expr_node(&src, v)),
261 None => (k, normalize_with(v, opts)),
262 };
263 if let Some(meta) = pending.remove(&real_key) {
264 cn.meta.extend(meta);
265 }
266 n.obj_children.insert(real_key, cn);
267 }
268
269 if let Some(src) = val_expr {
272 n = match expr_apply(&src, Spec::from(n)) {
273 Ok(applied) => applied,
274 Err(e) => fault_node(format!("Shape: value expression {:?}: {}", src, e)),
275 };
276 }
277 n
278}
279
280impl From<Token> for Node {
281 fn from(t: Token) -> Self {
282 type_token_node(t.kind())
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use crate::spec::{arr, obj};
290
291 #[test]
292 fn literals_are_optional_defaults() {
293 let n = normalize(Spec::from("x"));
294 assert!(n.kind == Kind::String && n.has_default && !n.required && !n.empty);
295 assert!(normalize(Spec::from("")).empty);
296 assert_eq!(normalize(Spec::from(true)).kind, Kind::Boolean);
297 assert_eq!(normalize(Spec::from(1.5)).kind, Kind::Number);
298 assert_eq!(normalize(Spec::from(f64::NAN)).kind, Kind::NaN);
299 assert_eq!(normalize(Spec::Value(Value::Null)).kind, Kind::Null);
300 assert_eq!(normalize(Spec::Value(Value::Undefined)).kind, Kind::Any);
301 assert_eq!(normalize(Spec::Value(Value::Date(1))).kind, Kind::Date);
302 assert_eq!(normalize(Spec::Value(Value::Func(1))).kind, Kind::Function);
303 assert_eq!(
304 normalize(Spec::from(num_bigint::BigInt::from(1))).kind,
305 Kind::BigInt
306 );
307 let n = normalize(Spec::Value(Value::Arr(vec![Value::from(1)])));
308 assert!(n.arr_child.is_some());
309 let mut m = crate::value::Map::new();
310 m.insert("a".into(), Value::from(1));
311 let n = normalize(Spec::Value(Value::Obj(m)));
312 assert_eq!(n.obj_children.len(), 1);
313 }
314
315 #[test]
316 fn tokens_are_required() {
317 let n = normalize(Spec::from(Token::String));
318 assert!(n.required && n.has_default && n.default == Value::Str(String::new()));
319 assert!(!normalize(Spec::from(Token::Any)).required);
320 let o = normalize(Spec::from(Token::Object));
321 assert!(o.is_open());
322 let a = normalize(Spec::from(Token::Array));
325 assert!(a.arr_child.is_none() && a.arr_children.is_empty());
326 let n: Node = Token::Number.into();
327 assert_eq!(n.kind, Kind::Number);
328 let r = normalize(Spec::from(Regex::new("^a$").unwrap()));
329 assert!(r.kind == Kind::Regexp && r.required);
330 assert!(matches!(
331 normalize(Spec::from(Node::of(Kind::Never))).kind,
332 Kind::Never
333 ));
334 }
335
336 #[test]
337 fn containers() {
338 assert!(normalize(arr::<Spec>([])).arr_child.is_none());
339 assert!(normalize(arr([Token::String])).arr_child.is_some());
340 assert_eq!(
341 normalize(arr([Spec::from(Token::String), Spec::from(1)]))
342 .arr_children
343 .len(),
344 2
345 );
346 let o = normalize(obj([("b", 1), ("a", 2)]));
347 let keys: Vec<&String> = o.obj_children.keys().collect();
348 assert_eq!(keys, vec!["b", "a"]);
349 assert!(!o.is_open());
350 assert!(normalize(obj::<&str, Spec>([])).is_open());
351 }
352}
353
354#[cfg(test)]
355mod option_tests {
356 use super::*;
357 use crate::spec::obj;
358 use crate::Schema;
359
360 fn j(s: &str) -> Value {
361 Value::from(serde_json::from_str::<serde_json::Value>(s).unwrap())
362 }
363
364 #[test]
365 fn meta_sidecars() {
366 let opts = Options {
367 meta: true,
368 ..Default::default()
369 };
370 let s = Schema::with_options(
371 obj([
372 ("a", Spec::from(1)),
373 ("a$$", Spec::from("short text")),
374 ("b", Spec::from(2)),
375 (
376 "b$$",
377 obj([("k", Spec::from(1)), ("z", Spec::from(Token::Number))]),
378 ),
379 ("c", Spec::from(3)),
380 ("c$$", Spec::from(7)),
381 ("d", Spec::from(4)),
382 ("d$$", Spec::from(Token::String)),
383 ("e$$", Spec::from(5)),
384 ("$$", Spec::from(6)),
385 ]),
386 &opts,
387 );
388 let c = &s.node().obj_children;
389 assert_eq!(c["a"].meta["short"], Value::from("short text"));
390 assert_eq!(c["b"].meta["k"], Value::from(1));
391 assert!(!c["b"].meta.contains_key("z"));
392 assert_eq!(c["c"].meta["value"], Value::from(7));
393 assert!(c["d"].meta.is_empty());
394 assert!(!c.contains_key("a$$"));
395 assert_eq!(c["e$$"].default, Value::from(5));
396 assert_eq!(c["$$"].default, Value::from(6));
397 let custom = Options {
398 meta: true,
399 meta_suffix: "__".into(),
400 ..Default::default()
401 };
402 let s = Schema::with_options(
403 obj([("a", Spec::from(1)), ("a__", Spec::from("s"))]),
404 &custom,
405 );
406 assert_eq!(s.node().obj_children["a"].meta["short"], Value::from("s"));
407 let s = Schema::new(obj([("a", Spec::from(1)), ("a$$", Spec::from("s"))]));
409 assert!(s.node().obj_children.contains_key("a$$"));
410 }
411
412 #[test]
413 fn value_expressions_apply_to_the_object() {
414 let opts = Options {
415 val_expr: true,
416 ..Default::default()
417 };
418 let s = Schema::with_options(
419 obj([("a", Spec::from(1)), ("$$", Spec::from("Open"))]),
420 &opts,
421 );
422 assert!(s.node().is_open());
423 assert_eq!(
424 serde_json::Value::from(s.validate(j(r#"{"a":2,"z":1}"#)).unwrap()),
425 serde_json::json!({"a":2,"z":1})
426 );
427 let s = Schema::with_options(
428 obj([("a", Spec::from(1)), ("$$", Spec::from("Min(2)"))]),
429 &opts,
430 );
431 assert!(s.validate(j(r#"{"a":2}"#)).is_err());
432 let s = Schema::with_options(obj([("a", Spec::from(1)), ("$$", Spec::from(5))]), &opts);
433 assert!(s.node().obj_children.contains_key("$$"));
434 let s = Schema::with_options(
435 obj([("a", Spec::from(1)), ("$$", Spec::from("bogus"))]),
436 &opts,
437 );
438 assert_eq!(
439 s.validate(j(r#"{"a":2}"#)).unwrap_err().to_string(),
440 "Shape: value expression \"bogus\": Shape: unexpected token bogus in builder expression bogus"
441 );
442 let marked = Options {
443 val_expr: true,
444 val_expr_mark: "@".into(),
445 ..Default::default()
446 };
447 let s = Schema::with_options(
448 obj([("a", Spec::from(1)), ("@", Spec::from("Open"))]),
449 &marked,
450 );
451 assert!(s.node().is_open());
452 let s = Schema::new(obj([("a", Spec::from(1)), ("$$", Spec::from("Open"))]));
454 assert!(!s.node().is_open());
455 }
456
457 #[test]
458 fn key_expressions_can_be_turned_off() {
459 let s = Schema::new(obj([("a: Min(1)", Spec::from(0))]));
460 assert!(s.node().obj_children.contains_key("a"));
461 let opts = Options {
462 key_expr: false,
463 ..Default::default()
464 };
465 let s = Schema::with_options(obj([("a: Min(1)", Spec::from(0))]), &opts);
466 assert!(s.node().obj_children.contains_key("a: Min(1)"));
467 }
468}