Skip to main content

shape/
spec.rs

1//! A spec by example: the value a spec is written as, before it is compiled
2//! into a node. Rust has no untyped literal, so a spec is built from these
3//! conversions, from the builders, or from the `shape!` macro.
4
5use crate::node::{Node, Token};
6use crate::value::{Map, Value};
7use num_bigint::BigInt;
8use regex::Regex;
9
10/// A spec by example.
11#[derive(Clone, Debug)]
12pub enum Spec {
13    /// A compiled node: a builder's result.
14    Node(Box<Node>),
15    /// A type token: a required kind.
16    Token(Token),
17    /// A literal: optional, its own default.
18    Value(Value),
19    /// An object spec, keys in order.
20    Obj(Vec<(String, Spec)>),
21    /// An array spec: empty, one child shape, or a tuple.
22    Arr(Vec<Spec>),
23    /// A regular expression: a required string matching it.
24    Regex(Regex),
25    /// A regexp as its pattern text, in the shared subset: what the string
26    /// form reads, and what a JSON Schema pattern imports.
27    Regexp(String),
28}
29
30impl From<Node> for Spec {
31    fn from(n: Node) -> Self {
32        Spec::Node(Box::new(n))
33    }
34}
35impl From<Token> for Spec {
36    fn from(t: Token) -> Self {
37        Spec::Token(t)
38    }
39}
40impl From<Value> for Spec {
41    fn from(v: Value) -> Self {
42        Spec::Value(v)
43    }
44}
45impl From<Regex> for Spec {
46    fn from(r: Regex) -> Self {
47        Spec::Regex(r)
48    }
49}
50impl From<&str> for Spec {
51    fn from(s: &str) -> Self {
52        Spec::Value(Value::from(s))
53    }
54}
55impl From<String> for Spec {
56    fn from(s: String) -> Self {
57        Spec::Value(Value::from(s))
58    }
59}
60impl From<bool> for Spec {
61    fn from(b: bool) -> Self {
62        Spec::Value(Value::from(b))
63    }
64}
65impl From<f64> for Spec {
66    fn from(n: f64) -> Self {
67        Spec::Value(Value::from(n))
68    }
69}
70impl From<i64> for Spec {
71    fn from(n: i64) -> Self {
72        Spec::Value(Value::from(n))
73    }
74}
75impl From<i32> for Spec {
76    fn from(n: i32) -> Self {
77        Spec::Value(Value::from(n))
78    }
79}
80impl From<BigInt> for Spec {
81    fn from(b: BigInt) -> Self {
82        Spec::Value(Value::from(b))
83    }
84}
85impl From<Vec<Spec>> for Spec {
86    fn from(a: Vec<Spec>) -> Self {
87        Spec::Arr(a)
88    }
89}
90impl<const N: usize> From<[Spec; N]> for Spec {
91    fn from(a: [Spec; N]) -> Self {
92        Spec::Arr(a.to_vec())
93    }
94}
95impl<const N: usize> From<[(&str, Spec); N]> for Spec {
96    fn from(pairs: [(&str, Spec); N]) -> Self {
97        Spec::Obj(pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
98    }
99}
100impl From<Vec<(String, Spec)>> for Spec {
101    fn from(pairs: Vec<(String, Spec)>) -> Self {
102        Spec::Obj(pairs)
103    }
104}
105
106/// An object spec from pairs, in order: `obj([("a", 1.into()), ("b", Token::String.into())])`.
107pub fn obj<K: Into<String>, S: Into<Spec>>(pairs: impl IntoIterator<Item = (K, S)>) -> Spec {
108    Spec::Obj(
109        pairs
110            .into_iter()
111            .map(|(k, v)| (k.into(), v.into()))
112            .collect(),
113    )
114}
115
116/// An array spec: `arr([Token::String.into()])` is an array of strings, more
117/// than one item a tuple, none an array of anything.
118pub fn arr<S: Into<Spec>>(items: impl IntoIterator<Item = S>) -> Spec {
119    Spec::Arr(items.into_iter().map(Into::into).collect())
120}
121
122/// The literal null spec.
123pub fn null() -> Spec {
124    Spec::Value(Value::Null)
125}
126
127/// A literal object value as a spec, its properties the defaults.
128pub fn from_map(m: Map) -> Spec {
129    Spec::Value(Value::Obj(m))
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn conversions() {
138        assert!(matches!(Spec::from(Node::default()), Spec::Node(_)));
139        assert!(matches!(
140            Spec::from(Token::String),
141            Spec::Token(Token::String)
142        ));
143        assert!(matches!(Spec::from(Value::Null), Spec::Value(Value::Null)));
144        assert!(matches!(
145            Spec::from(Regex::new("a").unwrap()),
146            Spec::Regex(_)
147        ));
148        assert!(matches!(Spec::from("s"), Spec::Value(Value::Str(_))));
149        assert!(matches!(
150            Spec::from(String::from("s")),
151            Spec::Value(Value::Str(_))
152        ));
153        assert!(matches!(Spec::from(true), Spec::Value(Value::Bool(true))));
154        assert!(matches!(Spec::from(1.5), Spec::Value(Value::Num(_))));
155        assert!(matches!(Spec::from(1i64), Spec::Value(Value::Num(_))));
156        assert!(matches!(Spec::from(1i32), Spec::Value(Value::Num(_))));
157        assert!(matches!(
158            Spec::from(BigInt::from(1)),
159            Spec::Value(Value::BigInt(_))
160        ));
161        assert!(matches!(Spec::from(vec![Spec::from(1)]), Spec::Arr(_)));
162        assert!(matches!(Spec::from([Spec::from(1)]), Spec::Arr(_)));
163        assert!(matches!(Spec::from([("a", Spec::from(1))]), Spec::Obj(_)));
164        assert!(matches!(
165            Spec::from(vec![("a".to_string(), Spec::from(1))]),
166            Spec::Obj(_)
167        ));
168        assert!(matches!(obj([("a", 1)]), Spec::Obj(_)));
169        assert!(matches!(arr([Token::String]), Spec::Arr(_)));
170        assert!(matches!(null(), Spec::Value(Value::Null)));
171        assert!(matches!(from_map(Map::new()), Spec::Value(Value::Obj(_))));
172    }
173}