1use crate::context::Context;
7use crate::error::ValidationError;
8use crate::node::{Kind, Node};
9use crate::normalize::normalize;
10use crate::spec::Spec;
11use crate::validate::{validate_node, Cur, Walk};
12use crate::value::{Map, Value};
13use std::fmt;
14
15#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct ArguError(pub String);
18
19impl fmt::Display for ArguError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 f.write_str(&self.0)
22 }
23}
24
25impl std::error::Error for ArguError {}
26
27#[derive(Clone, Debug)]
29pub struct Argu {
30 name: String,
31}
32
33#[derive(Clone, Debug)]
35pub struct Signature {
36 prefix: String,
37 keys: Vec<String>,
38 nodes: Vec<Node>,
39}
40
41impl Argu {
42 pub fn new(name: impl Into<String>) -> Argu {
43 Argu { name: name.into() }
44 }
45
46 pub fn validate<I, K, S>(
48 &self,
49 args: Vec<Value>,
50 whence: &str,
51 spec: I,
52 ) -> Result<Map, ArguError>
53 where
54 I: IntoIterator<Item = (K, S)>,
55 K: Into<String>,
56 S: Into<Spec>,
57 {
58 self.signature(whence, spec)?.apply(args)
59 }
60
61 pub fn signature<I, K, S>(&self, whence: &str, spec: I) -> Result<Signature, ArguError>
63 where
64 I: IntoIterator<Item = (K, S)>,
65 K: Into<String>,
66 S: Into<Spec>,
67 {
68 let mut keys = Vec::new();
69 let mut nodes = Vec::new();
70 for (k, s) in spec {
71 keys.push(k.into());
72 let mut n = normalize(s.into());
73 crate::prepare_node(&mut n);
74 nodes.push(n);
75 }
76 if keys.is_empty() {
77 return Err(ArguError(format!("{}: empty argument spec", self.name)));
78 }
79 let prefix = if whence.is_empty() {
80 self.name.clone()
81 } else {
82 format!("{} ({})", self.name, whence)
83 };
84 Ok(Signature {
85 prefix,
86 keys,
87 nodes,
88 })
89 }
90}
91
92fn is_rest(n: &Node) -> bool {
93 n.kind == Kind::Array && n.arr_rest.is_some()
94}
95
96fn run(n: &Node, val: Value, key: &str, is_match: bool) -> (Value, ValidationError) {
97 let mut ctx = Context::new();
98 ctx.terse = is_match;
99 let mut verr = ValidationError {
100 terse: is_match,
101 ..Default::default()
102 };
103 let defs = std::collections::HashMap::new();
105 let mut w = Walk {
106 ctx: &mut ctx,
107 defs: &defs,
108 is_match,
109 path: if key.is_empty() {
110 Vec::new()
111 } else {
112 vec![crate::context::PathPart::Key(key.into())]
113 },
114 paths: true,
115 };
116 let mut val = val;
117 let kept = validate_node(n, Cur::Mut(&mut val), key, false, &mut w, &mut verr);
118 if !kept {
119 val = Value::Undefined;
120 }
121 (val, verr)
122}
123
124impl Signature {
125 pub fn apply(&self, args: Vec<Value>) -> Result<Map, ArguError> {
127 let mut out = Map::new();
128 let mut idx = 0usize;
129 let require = |n: &Node, v: Value, key: &str| -> Result<Value, ArguError> {
130 let (val, verr) = run(n, v, key, false);
131 if verr.has_any() {
132 return Err(ArguError(format!("{}: {}", self.prefix, verr)));
133 }
134 Ok(val)
135 };
136 for (key, n) in self.keys.iter().zip(self.nodes.iter()) {
137 if is_rest(n) {
138 let rem: Vec<Value> = args[idx.min(args.len())..].to_vec();
142 let child = n.arr_rest.as_deref().unwrap();
143 let mut validated = Vec::with_capacity(rem.len());
144 for v in rem {
145 validated.push(require(child, v, key)?);
146 }
147 out.insert(key.clone(), Value::Arr(validated));
148 idx = args.len();
149 } else if n.skippable {
150 if idx >= args.len() {
151 out.insert(key.clone(), Value::Undefined);
152 continue;
153 }
154 let mut tester = n.clone();
157 tester.skippable = false;
158 tester.silent = false;
159 let (_, probe) = run(&tester, args[idx].clone(), "", true);
160 if !probe.has_any() {
161 out.insert(key.clone(), require(&tester, args[idx].clone(), key)?);
162 idx += 1;
163 } else {
164 out.insert(key.clone(), Value::Undefined);
165 }
166 } else {
167 let v = args.get(idx).cloned().unwrap_or(Value::Undefined);
168 out.insert(key.clone(), require(n, v, key)?);
169 if idx < args.len() {
170 idx += 1;
171 }
172 }
173 }
174 if idx < args.len() {
175 let expected = self.nodes.iter().filter(|n| !is_rest(n)).count();
176 return Err(ArguError(format!(
177 "{}: Too many arguments for type signature (was {}, expected {})",
178 self.prefix,
179 args.len(),
180 expected
181 )));
182 }
183 Ok(out)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use crate::builders::{rest, skip};
191 use crate::node::Token;
192
193 #[test]
194 fn matches_positional_arguments() {
195 let argu = Argu::new("mylib");
196 let sig = argu
197 .signature(
198 "foo",
199 [
200 ("a", Spec::from(Token::Number)),
201 ("b", Spec::from(Token::String)),
202 ],
203 )
204 .unwrap();
205 let out = sig.apply(vec![Value::from(2), Value::from("x")]).unwrap();
206 assert_eq!(
207 serde_json::Value::from(Value::Obj(out)),
208 serde_json::json!({"a": 2, "b": "x"})
209 );
210 assert_eq!(
211 sig.apply(vec![Value::from(2)]).unwrap_err().0,
212 "mylib (foo): Validation failed for property \"b\" because the property is missing."
213 );
214 assert_eq!(
215 sig.apply(vec![Value::from("z"), Value::from("x")]).unwrap_err().0,
216 "mylib (foo): Validation failed for property \"a\" with string \"z\" because the string is not of type number."
217 );
218 assert_eq!(
219 sig.apply(vec![Value::from(1), Value::from("x"), Value::from(3)])
220 .unwrap_err()
221 .0,
222 "mylib (foo): Too many arguments for type signature (was 3, expected 2)"
223 );
224 let out = argu
226 .validate(
227 vec![Value::from("x")],
228 "",
229 [
230 ("a", Spec::from(skip(Token::Number))),
231 ("b", Spec::from(Token::String)),
232 ],
233 )
234 .unwrap();
235 assert_eq!(
236 serde_json::Value::from(Value::Obj(out)),
237 serde_json::json!({"b": "x"})
238 );
239 let out = argu
240 .validate(
241 vec![Value::from(1), Value::from("x")],
242 "",
243 [
244 ("a", Spec::from(skip(Token::Number))),
245 ("b", Spec::from(Token::String)),
246 ],
247 )
248 .unwrap();
249 assert_eq!(
250 serde_json::Value::from(Value::Obj(out)),
251 serde_json::json!({"a": 1, "b": "x"})
252 );
253 let out = argu
254 .validate(vec![], "", [("a", Spec::from(skip(Token::Number)))])
255 .unwrap();
256 assert_eq!(
257 serde_json::Value::from(Value::Obj(out)),
258 serde_json::json!({})
259 );
260 let out = argu
262 .validate(
263 vec![Value::from(1), Value::from(2), Value::from(3)],
264 "",
265 [
266 ("a", Spec::from(Token::Number)),
267 (
268 "d",
269 Spec::from(rest(Token::Number, crate::spec::arr::<Spec>([]))),
270 ),
271 ],
272 )
273 .unwrap();
274 assert_eq!(
275 serde_json::Value::from(Value::Obj(out)),
276 serde_json::json!({"a": 1, "d": [2, 3]})
277 );
278 let out = argu
279 .validate(
280 vec![Value::from(1)],
281 "",
282 [
283 ("a", Spec::from(Token::Number)),
284 (
285 "d",
286 Spec::from(rest(Token::Any, crate::spec::arr::<Spec>([]))),
287 ),
288 ],
289 )
290 .unwrap();
291 assert_eq!(
292 serde_json::Value::from(Value::Obj(out)),
293 serde_json::json!({"a": 1, "d": []})
294 );
295 assert!(argu
296 .validate(
297 vec![Value::from(1), Value::from("x")],
298 "",
299 [
300 ("a", Spec::from(Token::Number)),
301 (
302 "d",
303 Spec::from(rest(Token::Number, crate::spec::arr::<Spec>([])))
304 )
305 ],
306 )
307 .is_err());
308 assert_eq!(
309 argu.signature("f", Vec::<(String, Spec)>::new())
310 .unwrap_err()
311 .0,
312 "mylib: empty argument spec"
313 );
314 assert_eq!(format!("{}", ArguError("e".into())), "e");
315 }
316}