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 mut w = Walk {
104 ctx: &mut ctx,
105 is_match,
106 path: if key.is_empty() {
107 Vec::new()
108 } else {
109 vec![crate::context::PathPart::Key(key.into())]
110 },
111 paths: true,
112 };
113 let mut val = val;
114 let kept = validate_node(n, Cur::Mut(&mut val), key, false, &mut w, &mut verr);
115 if !kept {
116 val = Value::Undefined;
117 }
118 (val, verr)
119}
120
121impl Signature {
122 pub fn apply(&self, args: Vec<Value>) -> Result<Map, ArguError> {
124 let mut out = Map::new();
125 let mut idx = 0usize;
126 let require = |n: &Node, v: Value, key: &str| -> Result<Value, ArguError> {
127 let (val, verr) = run(n, v, key, false);
128 if verr.has_any() {
129 return Err(ArguError(format!("{}: {}", self.prefix, verr)));
130 }
131 Ok(val)
132 };
133 for (key, n) in self.keys.iter().zip(self.nodes.iter()) {
134 if is_rest(n) {
135 let rem: Vec<Value> = args[idx.min(args.len())..].to_vec();
139 let child = n.arr_rest.as_deref().unwrap();
140 let mut validated = Vec::with_capacity(rem.len());
141 for v in rem {
142 validated.push(require(child, v, key)?);
143 }
144 out.insert(key.clone(), Value::Arr(validated));
145 idx = args.len();
146 } else if n.skippable {
147 if idx >= args.len() {
148 out.insert(key.clone(), Value::Undefined);
149 continue;
150 }
151 let mut tester = n.clone();
154 tester.skippable = false;
155 tester.silent = false;
156 let (_, probe) = run(&tester, args[idx].clone(), "", true);
157 if !probe.has_any() {
158 out.insert(key.clone(), require(&tester, args[idx].clone(), key)?);
159 idx += 1;
160 } else {
161 out.insert(key.clone(), Value::Undefined);
162 }
163 } else {
164 let v = args.get(idx).cloned().unwrap_or(Value::Undefined);
165 out.insert(key.clone(), require(n, v, key)?);
166 if idx < args.len() {
167 idx += 1;
168 }
169 }
170 }
171 if idx < args.len() {
172 let expected = self.nodes.iter().filter(|n| !is_rest(n)).count();
173 return Err(ArguError(format!(
174 "{}: Too many arguments for type signature (was {}, expected {})",
175 self.prefix,
176 args.len(),
177 expected
178 )));
179 }
180 Ok(out)
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::builders::{rest, skip};
188 use crate::node::Token;
189
190 #[test]
191 fn matches_positional_arguments() {
192 let argu = Argu::new("mylib");
193 let sig = argu
194 .signature(
195 "foo",
196 [
197 ("a", Spec::from(Token::Number)),
198 ("b", Spec::from(Token::String)),
199 ],
200 )
201 .unwrap();
202 let out = sig.apply(vec![Value::from(2), Value::from("x")]).unwrap();
203 assert_eq!(
204 serde_json::Value::from(Value::Obj(out)),
205 serde_json::json!({"a": 2, "b": "x"})
206 );
207 assert_eq!(
208 sig.apply(vec![Value::from(2)]).unwrap_err().0,
209 "mylib (foo): Validation failed for property \"b\" because the property is missing."
210 );
211 assert_eq!(
212 sig.apply(vec![Value::from("z"), Value::from("x")]).unwrap_err().0,
213 "mylib (foo): Validation failed for property \"a\" with string \"z\" because the string is not of type number."
214 );
215 assert_eq!(
216 sig.apply(vec![Value::from(1), Value::from("x"), Value::from(3)])
217 .unwrap_err()
218 .0,
219 "mylib (foo): Too many arguments for type signature (was 3, expected 2)"
220 );
221 let out = argu
223 .validate(
224 vec![Value::from("x")],
225 "",
226 [
227 ("a", Spec::from(skip(Token::Number))),
228 ("b", Spec::from(Token::String)),
229 ],
230 )
231 .unwrap();
232 assert_eq!(
233 serde_json::Value::from(Value::Obj(out)),
234 serde_json::json!({"b": "x"})
235 );
236 let out = argu
237 .validate(
238 vec![Value::from(1), Value::from("x")],
239 "",
240 [
241 ("a", Spec::from(skip(Token::Number))),
242 ("b", Spec::from(Token::String)),
243 ],
244 )
245 .unwrap();
246 assert_eq!(
247 serde_json::Value::from(Value::Obj(out)),
248 serde_json::json!({"a": 1, "b": "x"})
249 );
250 let out = argu
251 .validate(vec![], "", [("a", Spec::from(skip(Token::Number)))])
252 .unwrap();
253 assert_eq!(
254 serde_json::Value::from(Value::Obj(out)),
255 serde_json::json!({})
256 );
257 let out = argu
259 .validate(
260 vec![Value::from(1), Value::from(2), Value::from(3)],
261 "",
262 [
263 ("a", Spec::from(Token::Number)),
264 (
265 "d",
266 Spec::from(rest(Token::Number, crate::spec::arr::<Spec>([]))),
267 ),
268 ],
269 )
270 .unwrap();
271 assert_eq!(
272 serde_json::Value::from(Value::Obj(out)),
273 serde_json::json!({"a": 1, "d": [2, 3]})
274 );
275 let out = argu
276 .validate(
277 vec![Value::from(1)],
278 "",
279 [
280 ("a", Spec::from(Token::Number)),
281 (
282 "d",
283 Spec::from(rest(Token::Any, crate::spec::arr::<Spec>([]))),
284 ),
285 ],
286 )
287 .unwrap();
288 assert_eq!(
289 serde_json::Value::from(Value::Obj(out)),
290 serde_json::json!({"a": 1, "d": []})
291 );
292 assert!(argu
293 .validate(
294 vec![Value::from(1), Value::from("x")],
295 "",
296 [
297 ("a", Spec::from(Token::Number)),
298 (
299 "d",
300 Spec::from(rest(Token::Number, crate::spec::arr::<Spec>([])))
301 )
302 ],
303 )
304 .is_err());
305 assert_eq!(
306 argu.signature("f", Vec::<(String, Spec)>::new())
307 .unwrap_err()
308 .0,
309 "mylib: empty argument spec"
310 );
311 assert_eq!(format!("{}", ArguError("e".into())), "e");
312 }
313}