1use serde_json::Value;
2use wick_interface_types::{OperationSignature, Type};
3use wick_packet::Packet;
4
5use crate::Error;
6
7pub fn parse_args(args: &[String], sig: &OperationSignature) -> Result<Vec<Packet>, Error> {
9 let mut packets = Vec::new();
10 let mut iter = args.iter();
11 while let Some(next) = iter.next() {
12 if !next.starts_with("--") {
13 return Err(Error::InvalidArgument(next.clone()));
14 }
15 let next = next.trim_start_matches("--");
16 let (name, value) = split_arg(next);
17 let value = match value {
18 Some(value) => value,
19 None => {
20 let value = iter.next();
21 if value.is_none() {
22 return Err(Error::MissingArgumentValue(name.to_owned()));
23 }
24 value.unwrap()
25 }
26 };
27 let input = sig.inputs.iter().find(|i| i.name == name);
28 if input.is_none() {
29 return Err(Error::InvalidInput(name.to_owned()));
30 }
31 let input = input.unwrap();
32
33 let value = if value.starts_with('@') {
34 let path = value.trim_start_matches('@');
35
36 match input.ty() {
37 Type::String => Value::String(std::fs::read_to_string(path)?),
38 Type::Bytes => {
39 let bytes: wick_packet::Base64Bytes = std::fs::read(path)?.into();
40 serde_json::to_value(bytes).unwrap()
41 }
42 _ => encode(&std::fs::read_to_string(path)?),
43 }
44 } else {
45 match input.ty() {
46 Type::Datetime | Type::String => {
49 if is_valid(value) {
50 coerce_string(name, value, input.ty())?
51 } else {
52 value.into()
54 }
55 }
56 _ => encode(value),
58 }
59 };
60
61 let payload = Packet::encode(name, value);
65
66 packets.push(payload);
67 }
68
69 Ok(packets)
70}
71
72fn encode(value: &str) -> Value {
73 serde_json::from_str::<Value>(value).unwrap_or_else(|_| Value::String(value.to_owned()))
74}
75
76fn coerce_string(name: &str, value: &str, ty: &Type) -> Result<Value, Error> {
77 let val = serde_json::from_str::<Value>(value).unwrap_or_else(|_| Value::String(value.to_owned()));
78
79 Ok(match val {
80 serde_json::Value::Null => Value::String("null".to_owned()),
81 serde_json::Value::Bool(v) => Value::String(v.to_string()),
82 serde_json::Value::Number(v) => Value::String(v.to_string()),
83 serde_json::Value::String(v) => Value::String(v),
84 serde_json::Value::Array(_v) => return Err(Error::encoding(name, value, ty.clone())),
85 serde_json::Value::Object(_v) => return Err(Error::encoding(name, value, ty.clone())),
86 })
87}
88
89#[must_use]
90fn split_arg(arg: &str) -> (&str, Option<&str>) {
91 let mut parts = arg.split('=');
92 (parts.next().unwrap(), parts.next())
93}
94
95fn is_valid(string: &str) -> bool {
96 let parsed: Result<Value, _> = serde_json::from_str(string);
97 parsed.is_ok()
98}
99
100#[cfg(test)]
101mod tests {
102 use anyhow::Result;
103 use wick_interface_types::Field;
104 use wick_packet::DateTime;
105
106 use super::*;
107
108 fn to_vec(list: &[&str]) -> Vec<String> {
109 list.iter().map(|s| (*s).to_owned()).collect()
110 }
111
112 fn sig(fields: &[(&str, Type)]) -> OperationSignature {
113 OperationSignature::new(
114 "test".to_owned(),
115 fields
116 .iter()
117 .map(|(n, t)| Field::new((*n).to_owned(), t.clone()))
118 .collect(),
119 Default::default(),
120 Default::default(),
121 )
122 }
123
124 #[test_logger::test]
125 fn parse_separate_args() -> Result<()> {
126 let args = to_vec(&["--input-a", "value-a"]);
127 let packets = parse_args(&args, &sig(&[("input-a", Type::String)]))?;
128 assert_eq!(packets[0], Packet::encode("input-a", "value-a"));
129 Ok(())
130 }
131
132 #[test_logger::test]
133 fn parse_numbers() -> Result<()> {
134 let args = to_vec(&["--input-a", "123"]);
135 let packets = parse_args(&args, &sig(&[("input-a", Type::U64)]))?;
136 assert_eq!(packets[0], Packet::encode("input-a", 123));
137 assert_eq!(packets[0].clone().decode::<i32>().unwrap(), 123);
138 Ok(())
139 }
140
141 #[test_logger::test]
142 #[ignore = "This is broken and should be fixed."]
143 fn parse_date_millis() -> Result<()> {
144 let date = wick_packet::parse_date("2021-04-12T22:10:57+02:00")?;
145 let args = to_vec(&["--input-a", &date.timestamp_millis().to_string()]);
146 println!("args: {:?}", args);
147 let packets = parse_args(&args, &sig(&[("input-a", Type::Datetime)]))?;
148 assert_eq!(packets[0].clone().decode::<DateTime>().unwrap(), date);
149 Ok(())
150 }
151
152 #[test_logger::test]
153 fn parse_date_str() -> Result<()> {
154 let date = wick_packet::parse_date("2021-04-12T22:10:57+02:00")?;
155 let args = to_vec(&["--input-a", "2021-04-12T22:10:57+02:00"]);
156 println!("args: {:?}", args);
157 let packets = parse_args(&args, &sig(&[("input-a", Type::Datetime)]))?;
158 assert_eq!(packets[0].clone().decode::<DateTime>().unwrap(), date);
159 Ok(())
160 }
161
162 #[test_logger::test]
163 fn parse_combined_args() -> Result<()> {
164 let args = to_vec(&["--input-a=value-a"]);
165 let packets = parse_args(&args, &sig(&[("input-a", Type::String)]))?;
166 assert_eq!(packets[0], Packet::encode("input-a", "value-a"));
167 Ok(())
168 }
169
170 #[test_logger::test]
171 fn parse_mixed_args() -> Result<()> {
172 let args = to_vec(&["--input-a", "value-a", "--input-b=value-b"]);
173 let packets = parse_args(&args, &sig(&[("input-a", Type::String), ("input-b", Type::String)]))?;
174 assert_eq!(packets[0], Packet::encode("input-a", "value-a"));
175 assert_eq!(packets[1], Packet::encode("input-b", "value-b"));
176 Ok(())
177 }
178
179 #[test_logger::test]
180 fn parse_err_invalid() -> Result<()> {
181 let args = to_vec(&["input-a", "value-a", "--input-b=value-b"]);
182 let result = parse_args(&args, &sig(&[("input-a", Type::String), ("input-b", Type::String)]));
183 assert!(result.is_err());
184 Ok(())
185 }
186
187 #[test_logger::test]
188 fn parse_err_dangling() -> Result<()> {
189 let args = to_vec(&["--input-a", "value-a", "--input-b"]);
190 let result = parse_args(&args, &sig(&[("input-a", Type::String), ("input-b", Type::String)]));
191 assert!(result.is_err());
192 Ok(())
193 }
194
195 #[test_logger::test]
196 fn parse_arg_numeric() -> Result<()> {
197 let args = to_vec(&["--num", "2000"]);
198 let packets = parse_args(&args, &sig(&[("num", Type::U32)]))?;
199 assert_eq!(packets[0], Packet::encode("num", 2000));
200 Ok(())
201 }
202
203 #[test_logger::test]
204 fn test_is_valid() -> Result<()> {
205 let int = "1234567890";
206 assert!(is_valid(int));
207 let float = "12345.67890";
208 assert!(is_valid(float));
209 let obj = "{}";
210 assert!(is_valid(obj));
211 let array = "[]";
212 assert!(is_valid(array));
213 let naked_string = "hello world";
214 assert!(!is_valid(naked_string));
215 let string = "\"hello world\"";
216 assert!(is_valid(string));
217 Ok(())
218 }
219}