1pub mod help;
4
5use std::{fmt::Debug, mem::size_of, str::FromStr};
6
7use num_traits::Num;
8
9use casper_types::{
10 account::AccountHash,
11 bytesrepr::{Bytes, ToBytes, OPTION_NONE_TAG, OPTION_SOME_TAG},
12 AsymmetricType, CLType, CLTyped, CLValue, Key, PublicKey, RuntimeArgs, URef, U128, U256, U512,
13};
14
15use super::CliError;
16
17type Parser = fn(&str, OptionalStatus, &str) -> Result<CLValue, CliError>;
18
19const PREFIX_FOR_OPTION: &str = "opt_";
20const BYTE_ARRAY_PREFIX: &str = "byte_array_";
21const SUPPORTED_TYPES: [(&str, Parser); 17] = [
22 ("bool", parse_bool),
23 ("i32", parse_int::<i32>),
24 ("i64", parse_int::<i64>),
25 ("u8", parse_int::<u8>),
26 ("u32", parse_int::<u32>),
27 ("u64", parse_int::<u64>),
28 ("u128", parse_int::<U128>),
29 ("u256", parse_int::<U256>),
30 ("u512", parse_int::<U512>),
31 ("unit", parse_unit),
32 ("string", parse_string),
33 ("key", parse_key),
34 ("account_hash", parse_account_hash),
35 ("uref", parse_uref),
36 ("public_key", parse_public_key),
37 ("byte_list", parse_byte_list),
38 ("byte_array_<NUM>", parse_byte_array),
39];
40
41#[derive(Debug, PartialEq, Eq)]
42enum OptionalStatus {
43 Some,
44 None,
45 NotOptional,
46}
47
48fn parse_cl_value<T, F>(optional_status: OptionalStatus, parse: F) -> Result<CLValue, CliError>
51where
52 T: CLTyped + ToBytes,
53 F: FnOnce() -> Result<T, CliError>,
54{
55 match optional_status {
56 OptionalStatus::Some => CLValue::from_t(Some(parse()?)),
57 OptionalStatus::None => CLValue::from_t::<Option<T>>(None),
58 OptionalStatus::NotOptional => CLValue::from_t(parse()?),
59 }
60 .map_err(|error| {
61 CliError::InvalidCLValue(format!(
62 "unable to parse cl value {:?} with optional_status {:?}",
63 error, optional_status
64 ))
65 })
66}
67
68fn parse_bool(
69 _simple_type: &str,
70 optional_status: OptionalStatus,
71 value: &str,
72) -> Result<CLValue, CliError> {
73 let parse = || match value.to_lowercase().as_str() {
74 "true" | "t" => Ok(true),
75 "false" | "f" => Ok(false),
76 invalid => Err(CliError::InvalidCLValue(format!(
77 "can't parse '{}' as a bool (should be 'true' or 'false')",
78 invalid
79 ))),
80 };
81 parse_cl_value(optional_status, parse)
82}
83
84fn parse_int<T: CLTyped + ToBytes + Debug + Num>(
85 _simple_type: &str,
86 optional_status: OptionalStatus,
87 value: &str,
88) -> Result<CLValue, CliError> {
89 let parse = || {
90 let bit_width = size_of::<T>() * 8;
91 if value.is_empty() {
92 return Err(CliError::InvalidCLValue(format!(
93 "can't parse '' as u{}",
94 bit_width,
95 )));
96 }
97 T::from_str_radix(value, 10).map_err(|_| {
98 CliError::InvalidCLValue(format!("can't parse '{}' as u{}", value, bit_width,))
99 })
100 };
101 parse_cl_value(optional_status, parse)
102}
103
104fn parse_unit(
105 _simple_type: &str,
106 optional_status: OptionalStatus,
107 value: &str,
108) -> Result<CLValue, CliError> {
109 let parse = || {
110 if !value.is_empty() {
111 return Err(CliError::InvalidCLValue(format!(
112 "can't parse '{}' as unit (should be '')",
113 value
114 )));
115 }
116 Ok(())
117 };
118 parse_cl_value(optional_status, parse)
119}
120
121fn parse_string(
122 _simple_type: &str,
123 optional_status: OptionalStatus,
124 value: &str,
125) -> Result<CLValue, CliError> {
126 let parse = || Ok(value.to_string());
127 parse_cl_value(optional_status, parse)
128}
129
130fn parse_key(
131 _simple_type: &str,
132 optional_status: OptionalStatus,
133 value: &str,
134) -> Result<CLValue, CliError> {
135 let parse = || {
136 Key::from_formatted_str(value).map_err(|error| {
137 CliError::InvalidCLValue(format!("can't parse '{}' as Key: {}", value, error))
138 })
139 };
140 parse_cl_value(optional_status, parse)
141}
142
143fn parse_account_hash(
144 _simple_type: &str,
145 optional_status: OptionalStatus,
146 value: &str,
147) -> Result<CLValue, CliError> {
148 let parse = || {
149 AccountHash::from_formatted_str(value).map_err(|error| {
150 CliError::InvalidCLValue(format!("can't parse '{}' as AccountHash: {}", value, error))
151 })
152 };
153 parse_cl_value(optional_status, parse)
154}
155
156fn parse_uref(
157 _simple_type: &str,
158 optional_status: OptionalStatus,
159 value: &str,
160) -> Result<CLValue, CliError> {
161 let parse = || {
162 URef::from_formatted_str(value).map_err(|error| {
163 CliError::InvalidCLValue(format!("can't parse '{}' as URef: {}", value, error))
164 })
165 };
166 parse_cl_value(optional_status, parse)
167}
168
169fn parse_public_key(
170 _simple_type: &str,
171 optional_status: OptionalStatus,
172 value: &str,
173) -> Result<CLValue, CliError> {
174 let parse = || {
175 let pub_key = PublicKey::from_hex(value).map_err(|error| {
176 CliError::InvalidCLValue(format!("can't parse '{}' as PublicKey: {}", value, error))
177 })?;
178 Ok(pub_key)
179 };
180 parse_cl_value(optional_status, parse)
181}
182
183fn parse_byte_list(
184 _simple_type: &str,
185 optional_status: OptionalStatus,
186 value: &str,
187) -> Result<CLValue, CliError> {
188 let parse = || {
189 base16::decode(value).map(Bytes::from).map_err(|error| {
190 CliError::InvalidCLValue(format!("can't parse '{}' as a byte_list: {}", value, error))
191 })
192 };
193 parse_cl_value(optional_status, parse)
194}
195
196fn parse_byte_array(
197 simple_type: &str,
198 optional_status: OptionalStatus,
199 value: &str,
200) -> Result<CLValue, CliError> {
201 let array_len_str = simple_type
203 .strip_prefix("byte_array_")
204 .expect("should have 'byte_array_' prefix");
205 let array_len = u32::from_str(array_len_str).map_err(|_| {
206 CliError::InvalidCLValue(format!(
207 "can't parse '{}' of '{}' as an integer",
208 array_len_str, simple_type,
209 ))
210 })?;
211
212 let is_some = match optional_status {
213 OptionalStatus::Some => true,
214 OptionalStatus::None => {
215 return Ok(CLValue::from_components(
216 CLType::Option(Box::new(CLType::ByteArray(array_len))),
217 vec![OPTION_NONE_TAG],
218 ))
219 }
220 OptionalStatus::NotOptional => false,
221 };
222
223 let mut bytes = base16::decode(value).map_err(|error| {
224 CliError::InvalidCLValue(format!(
225 "can't parse '{}' as a byte_array: {}",
226 value, error
227 ))
228 })?;
229 if bytes.len() != array_len as usize {
230 return Err(CliError::InvalidCLValue(format!(
231 "provided {} bytes but specified a byte_array of {} bytes",
232 bytes.len(),
233 array_len
234 )));
235 }
236
237 let cl_value = if is_some {
238 let mut all_bytes = vec![OPTION_SOME_TAG];
239 all_bytes.append(&mut bytes);
240 CLValue::from_components(
241 CLType::Option(Box::new(CLType::ByteArray(array_len))),
242 all_bytes,
243 )
244 } else {
245 CLValue::from_components(CLType::ByteArray(array_len), bytes)
246 };
247 Ok(cl_value)
248}
249
250fn get_simple_type_and_optional_status(
256 maybe_opt_type: &str,
257 value: &str,
258) -> Result<(String, OptionalStatus, String), CliError> {
259 let maybe_opt_type = maybe_opt_type.to_lowercase();
260 let (simple_type, optional_status, trimmed_value) =
261 match maybe_opt_type.strip_prefix(PREFIX_FOR_OPTION) {
262 Some(simple_type) => {
263 if value.to_lowercase() == "null" {
264 (simple_type.to_string(), OptionalStatus::None, String::new())
265 } else {
266 (
267 simple_type.to_string(),
268 OptionalStatus::Some,
269 value.trim_matches('\'').to_string(),
270 )
271 }
272 }
273 None => (
274 maybe_opt_type,
275 OptionalStatus::NotOptional,
276 value.trim_matches('\'').to_string(),
277 ),
278 };
279
280 if value == trimmed_value {
281 return Err(CliError::InvalidCLValue(format!(
282 "value in simple arg should be surrounded by single quotes unless it's a null \
283 optional value (value passed: {})",
284 value
285 )));
286 }
287
288 Ok((simple_type, optional_status, trimmed_value))
289}
290
291fn parts_to_cl_value(
293 simple_type: &str,
294 optional_status: OptionalStatus,
295 trimmed_value: &str,
296) -> Result<CLValue, CliError> {
297 let parser = match simple_type {
298 t if t == SUPPORTED_TYPES[0].0 => SUPPORTED_TYPES[0].1,
299 t if t == SUPPORTED_TYPES[1].0 => SUPPORTED_TYPES[1].1,
300 t if t == SUPPORTED_TYPES[2].0 => SUPPORTED_TYPES[2].1,
301 t if t == SUPPORTED_TYPES[3].0 => SUPPORTED_TYPES[3].1,
302 t if t == SUPPORTED_TYPES[4].0 => SUPPORTED_TYPES[4].1,
303 t if t == SUPPORTED_TYPES[5].0 => SUPPORTED_TYPES[5].1,
304 t if t == SUPPORTED_TYPES[6].0 => SUPPORTED_TYPES[6].1,
305 t if t == SUPPORTED_TYPES[7].0 => SUPPORTED_TYPES[7].1,
306 t if t == SUPPORTED_TYPES[8].0 => SUPPORTED_TYPES[8].1,
307 t if t == SUPPORTED_TYPES[9].0 => SUPPORTED_TYPES[9].1,
308 t if t == SUPPORTED_TYPES[10].0 => SUPPORTED_TYPES[10].1,
309 t if t == SUPPORTED_TYPES[11].0 => SUPPORTED_TYPES[11].1,
310 t if t == SUPPORTED_TYPES[12].0 => SUPPORTED_TYPES[12].1,
311 t if t == SUPPORTED_TYPES[13].0 => SUPPORTED_TYPES[13].1,
312 t if t == SUPPORTED_TYPES[14].0 => SUPPORTED_TYPES[14].1,
313 t if t == SUPPORTED_TYPES[15].0 => SUPPORTED_TYPES[15].1,
314 t if t.starts_with(BYTE_ARRAY_PREFIX) => SUPPORTED_TYPES[16].1,
315 _ => {
316 let original_type = match optional_status {
317 OptionalStatus::Some | OptionalStatus::None => {
318 PREFIX_FOR_OPTION.to_string() + simple_type
319 }
320 OptionalStatus::NotOptional => simple_type.to_string(),
321 };
322 return Err(CliError::InvalidCLValue(format!(
323 "unknown variant {}, expected one of {}",
324 original_type,
325 help::supported_cl_type_list()
326 )));
327 }
328 };
329 parser(simple_type, optional_status, trimmed_value)
330}
331
332fn split_arg(arg: &str) -> Result<(&str, &str, &str), CliError> {
334 const ARG_VALUE_NAME: &str = r#""NAME:TYPE='VALUE'" OR "NAME:TYPE=null""#;
335
336 let parts: Vec<_> = arg.splitn(3, &[':', '='][..]).collect();
337 if parts.len() != 3 {
338 return Err(CliError::InvalidCLValue(format!(
339 "arg {} should be formatted as {}",
340 arg, ARG_VALUE_NAME
341 )));
342 }
343 Ok((parts[0], parts[1], parts[2]))
344}
345
346pub fn insert_arg(arg: &str, runtime_args: &mut RuntimeArgs) -> Result<(), CliError> {
348 let (name, initial_type, value) = split_arg(arg)?;
349 let (simple_type, optional_status, trimmed_value) =
350 get_simple_type_and_optional_status(initial_type, value)?;
351 let cl_value = parts_to_cl_value(&simple_type, optional_status, &trimmed_value)?;
352 runtime_args.insert_cl_value(name, cl_value);
353 Ok(())
354}
355
356#[cfg(test)]
357mod tests {
358 use casper_types::{
359 account::AccountHash, bytesrepr::ToBytes, AccessRights, CLTyped, CLValue, NamedArg,
360 PublicKey, RuntimeArgs, URef, U128, U256, U512,
361 };
362
363 use super::*;
364
365 fn check_insert_valid_arg<T: CLTyped + ToBytes>(cli_string: &str, expected: T) {
366 let expected = RuntimeArgs::from(vec![NamedArg::new(
367 "x".to_string(),
368 CLValue::from_t(expected).unwrap(),
369 )]);
370
371 let mut actual = RuntimeArgs::new();
372 insert_arg(cli_string, &mut actual).expect("should parse");
373 assert_eq!(actual, expected);
374 }
375
376 fn check_insert_invalid_arg(cli_string: &str) {
377 let mut runtime_args = RuntimeArgs::new();
378 let result = insert_arg(cli_string, &mut runtime_args);
379 assert!(result.is_err(), "{} should be an error", cli_string);
380 }
381
382 #[test]
383 fn should_insert_valid_bool_arg() {
384 check_insert_valid_arg("x:bool='f'", false);
385 check_insert_valid_arg("x:bool='F'", false);
386 check_insert_valid_arg("x:bool='false'", false);
387 check_insert_valid_arg("x:Bool='False'", false);
388 check_insert_valid_arg("x:BOOL='False'", false);
389 check_insert_valid_arg("x:bool='t'", true);
390 check_insert_valid_arg("x:bool='T'", true);
391 check_insert_valid_arg("x:bool='true'", true);
392 check_insert_valid_arg("x:Bool='True'", true);
393 check_insert_valid_arg("x:BOOL='TRUE'", true);
394 check_insert_valid_arg("x:opt_bool='f'", Some(false));
395 check_insert_valid_arg("x:Opt_Bool='t'", Some(true));
396 check_insert_valid_arg::<Option<bool>>("x:OPT_BOOL=null", None);
397 }
398
399 #[test]
400 fn should_fail_to_insert_invalid_bool_arg() {
401 check_insert_invalid_arg("x:bool='fa'");
402 check_insert_invalid_arg("x:opt_bool=''");
403 }
404
405 #[test]
406 fn should_insert_valid_i32_arg() {
407 check_insert_valid_arg("x:i32='2147483647'", i32::MAX);
408 check_insert_valid_arg("x:I32='0'", 0_i32);
409 check_insert_valid_arg("x:i32='-2147483648'", i32::MIN);
410 check_insert_valid_arg("x:opt_i32='-1'", Some(-1_i32));
411 check_insert_valid_arg::<Option<i32>>("x:OPT_I32=Null", None);
412 }
413
414 #[test]
415 fn should_fail_to_insert_invalid_i32_arg() {
416 check_insert_invalid_arg("x:i32='f'");
417 check_insert_invalid_arg("x:opt_i32=''");
418 }
419
420 #[test]
421 fn should_insert_valid_i64_arg() {
422 check_insert_valid_arg("x:i64='9223372036854775807'", i64::MAX);
423 check_insert_valid_arg("x:I64='0'", 0_i64);
424 check_insert_valid_arg("x:i64='-9223372036854775808'", i64::MIN);
425 check_insert_valid_arg("x:opt_i64='-1'", Some(-1_i64));
426 check_insert_valid_arg::<Option<i64>>("x:OPT_I64=NULL", None);
427 }
428
429 #[test]
430 fn should_fail_to_insert_invalid_i64_arg() {
431 check_insert_invalid_arg("x:i64='f'");
432 check_insert_invalid_arg("x:opt_i64=''");
433 }
434
435 #[test]
436 fn should_insert_valid_u8_arg() {
437 check_insert_valid_arg("x:u8='0'", 0_u8);
438 check_insert_valid_arg("x:U8='255'", u8::MAX);
439 check_insert_valid_arg("x:opt_u8='1'", Some(1_u8));
440 check_insert_valid_arg::<Option<u8>>("x:OPT_U8=null", None);
441 }
442
443 #[test]
444 fn should_fail_to_insert_invalid_u8_arg() {
445 check_insert_invalid_arg("x:u8='f'");
446 check_insert_invalid_arg("x:opt_u8=''");
447 }
448
449 #[test]
450 fn should_insert_valid_u32_arg() {
451 check_insert_valid_arg("x:u32='0'", 0_u32);
452 check_insert_valid_arg("x:U32='4294967295'", u32::MAX);
453 check_insert_valid_arg("x:opt_u32='1'", Some(1_u32));
454 check_insert_valid_arg::<Option<u32>>("x:OPT_U32=null", None);
455 }
456
457 #[test]
458 fn should_fail_to_insert_invalid_u32_arg() {
459 check_insert_invalid_arg("x:u32='f'");
460 check_insert_invalid_arg("x:opt_u32=''");
461 }
462
463 #[test]
464 fn should_insert_valid_u64_arg() {
465 check_insert_valid_arg("x:u64='0'", 0_u64);
466 check_insert_valid_arg("x:U64='18446744073709551615'", u64::MAX);
467 check_insert_valid_arg("x:opt_u64='1'", Some(1_u64));
468 check_insert_valid_arg::<Option<u64>>("x:OPT_U64=null", None);
469 }
470
471 #[test]
472 fn should_fail_to_insert_invalid_u64_arg() {
473 check_insert_invalid_arg("x:u64='f'");
474 check_insert_invalid_arg("x:opt_u64=''");
475 }
476
477 #[test]
478 fn should_insert_valid_u128_arg() {
479 check_insert_valid_arg("x:u128='0'", U128::zero());
480 check_insert_valid_arg(
481 "x:U128='340282366920938463463374607431768211455'",
482 U128::max_value(),
483 );
484 check_insert_valid_arg("x:opt_u128='1'", Some(U128::from(1)));
485 check_insert_valid_arg::<Option<U128>>("x:OPT_U128=null", None);
486 }
487
488 #[test]
489 fn should_fail_to_insert_invalid_u128_arg() {
490 check_insert_invalid_arg("x:u128='f'");
491 check_insert_invalid_arg("x:opt_u128=''");
492 }
493
494 #[test]
495 fn should_insert_valid_u256_arg() {
496 check_insert_valid_arg("x:u256='0'", U256::zero());
497 check_insert_valid_arg(
498 "x:U256='115792089237316195423570985008687907853269984665640564039457584007913129639935'",
499 U256::max_value(),
500 );
501 check_insert_valid_arg("x:opt_u256='1'", Some(U256::from(1)));
502 check_insert_valid_arg::<Option<U256>>("x:OPT_U256=null", None);
503 }
504
505 #[test]
506 fn should_fail_to_insert_invalid_u256_arg() {
507 check_insert_invalid_arg("x:u256='f'");
508 check_insert_invalid_arg("x:opt_u256=''");
509 }
510
511 #[test]
512 fn should_insert_valid_u512_arg() {
513 check_insert_valid_arg("x:u512='0'", U512::zero());
514 check_insert_valid_arg(
515 "x:U512='134078079299425970995740249982058461274793658205923933777235614437217640300735\
516 46976801874298166903427690031858186486050853753882811946569946433649006084095'",
517 U512::max_value(),
518 );
519 check_insert_valid_arg("x:opt_u512='1'", Some(U512::from(1)));
520 check_insert_valid_arg::<Option<U512>>("x:OPT_U512=null", None);
521 }
522
523 #[test]
524 fn should_fail_to_insert_invalid_u512_arg() {
525 check_insert_invalid_arg("x:u512='f'");
526 check_insert_invalid_arg("x:opt_u512=''");
527 }
528
529 #[test]
530 fn should_insert_valid_unit_arg() {
531 check_insert_valid_arg("x:unit=''", ());
532 check_insert_valid_arg("x:opt_unit=''", Some(()));
533 check_insert_valid_arg::<Option<()>>("x:OPT_UNIT=null", None);
534 }
535
536 #[test]
537 fn should_fail_to_insert_invalid_unit_arg() {
538 check_insert_invalid_arg("x:unit='f'");
539 check_insert_invalid_arg("x:opt_unit='1'");
540 }
541
542 #[test]
543 fn should_insert_valid_string_arg() {
544 let value = String::from("test \"string");
545 check_insert_valid_arg(&format!("x:string='{}'", value), value.clone());
546 check_insert_valid_arg(&format!("x:opt_string='{}'", value), Some(value));
547 check_insert_valid_arg::<Option<String>>("x:OPT_STRING=null", None);
548 }
549
550 #[test]
551 fn should_insert_valid_key_arg() {
552 let bytes = (1..33).collect::<Vec<_>>();
553 let array = <[u8; 32]>::try_from(bytes.as_ref()).unwrap();
554
555 let key_account = Key::Account(AccountHash::new(array));
556 let key_hash = Key::Hash(array);
557 let key_uref = Key::URef(URef::new(array, AccessRights::NONE));
558
559 for key in &[key_account, key_hash, key_uref] {
560 check_insert_valid_arg(&format!("x:key='{}'", key.to_formatted_string()), *key);
561 check_insert_valid_arg(
562 &format!("x:opt_key='{}'", key.to_formatted_string()),
563 Some(*key),
564 );
565 check_insert_valid_arg::<Option<Key>>("x:OPT_KEY=null", None);
566 }
567 }
568
569 #[test]
570 fn should_fail_to_insert_invalid_key_arg() {
571 check_insert_invalid_arg("x:key='f'");
572 check_insert_invalid_arg("x:opt_key=''");
573 }
574
575 #[test]
576 fn should_insert_valid_account_hash_arg() {
577 let bytes = (1..33).collect::<Vec<_>>();
578 let array = <[u8; 32]>::try_from(bytes.as_ref()).unwrap();
579 let value = AccountHash::new(array);
580 check_insert_valid_arg(
581 &format!("x:account_hash='{}'", value.to_formatted_string()),
582 value,
583 );
584 check_insert_valid_arg(
585 &format!("x:opt_account_hash='{}'", value.to_formatted_string()),
586 Some(value),
587 );
588 check_insert_valid_arg::<Option<AccountHash>>("x:OPT_ACCOUNT_HASH=null", None);
589 }
590
591 #[test]
592 fn should_fail_to_insert_invalid_account_hash_arg() {
593 check_insert_invalid_arg("x:account_hash='f'");
594 check_insert_invalid_arg("x:account_hash='account-hash-f'");
595 check_insert_invalid_arg("x:opt_account_hash=''");
596 }
597
598 #[test]
599 fn should_insert_valid_uref_arg() {
600 let bytes = (1..33).collect::<Vec<_>>();
601 let array = <[u8; 32]>::try_from(bytes.as_ref()).unwrap();
602 let value = URef::new(array, AccessRights::READ_ADD_WRITE);
603 check_insert_valid_arg(&format!("x:uref='{}'", value.to_formatted_string()), value);
604 check_insert_valid_arg(
605 &format!("x:opt_uref='{}'", value.to_formatted_string()),
606 Some(value),
607 );
608 check_insert_valid_arg::<Option<URef>>("x:OPT_UREF=null", None);
609 }
610
611 #[test]
612 fn should_fail_to_insert_invalid_uref_arg() {
613 check_insert_invalid_arg("x:uref='f'");
614 check_insert_invalid_arg("x:uref='uref-f'");
615 check_insert_invalid_arg("x:opt_uref=''");
616 }
617
618 #[test]
619 fn should_insert_valid_public_key_arg() {
620 let hex_value = "0119bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1";
621 let value = PublicKey::from_hex(hex_value).unwrap();
622 check_insert_valid_arg(&format!("x:public_key='{}'", hex_value), value.clone());
623 check_insert_valid_arg(&format!("x:opt_public_key='{}'", hex_value), Some(value));
624 check_insert_valid_arg::<Option<PublicKey>>("x:OPT_PUBLIC_KEY=null", None);
625 }
626
627 #[test]
628 fn should_fail_to_insert_invalid_public_key_arg() {
629 check_insert_invalid_arg("x:public_key='f'");
630 check_insert_invalid_arg("x:opt_public_key=''");
631 }
632
633 #[test]
634 fn should_insert_valid_byte_list_arg() {
635 check_insert_valid_arg("x:byte_list=''", Bytes::new());
636 check_insert_valid_arg("x:opt_byte_list=''", Some(Bytes::new()));
637 check_insert_valid_arg::<Option<Bytes>>("x:opt_byte_list=null", None);
638
639 let value = Bytes::from(vec![0_u8, 1, 2, 3, 4, 5]);
640 let hex_value = base16::encode_upper(&value);
641
642 check_insert_valid_arg(&format!("x:Byte_List='{}'", hex_value), value.clone());
643 check_insert_valid_arg(&format!("x:opt_byte_list='{}'", hex_value), Some(value));
644 check_insert_valid_arg::<Option<Bytes>>("x:OPT_BYTE_LIST=null", None);
645 }
646
647 #[test]
648 fn should_fail_to_insert_invalid_byte_list_arg() {
649 check_insert_invalid_arg("x:byte_list='fz'");
650 }
651
652 #[test]
653 fn should_insert_valid_byte_array_arg() {
654 check_insert_valid_arg("x:byte_array_0=''", []);
655 check_insert_valid_arg("x:opt_byte_array_0=''", Some([]));
656 check_insert_valid_arg::<Option<[u8; 0]>>("x:opt_byte_array_0=null", None);
657
658 let value = [0_u8, 1, 2, 3, 4, 5];
659 let hex_value = base16::encode_upper(&value);
660
661 check_insert_valid_arg(&format!("x:Byte_Array_6='{}'", hex_value), value);
662 check_insert_valid_arg(&format!("x:opt_byte_array_6='{}'", hex_value), Some(value));
663 check_insert_valid_arg::<Option<[u8; 6]>>("x:OPT_BYTE_ARRAY_6=null", None);
664 }
665
666 #[test]
667 fn should_fail_to_insert_invalid_byte_array_arg() {
668 check_insert_invalid_arg("x:byte_array_1='fz'");
669 check_insert_invalid_arg("x:byte_array_1=''");
670 check_insert_invalid_arg("x:byte_array_1='0102'");
671 }
672
673 #[test]
674 fn should_fail_to_insert_malformed_args() {
675 const ARG_BAD_TYPE: &str = "name:wat='false'";
676 const ARG_GIBBERISH: &str = "asdf|1234(..)";
677 const ARG_UNQUOTED: &str = "name:u32=0"; const EMPTY: &str = "";
679 const LARGE_2K_INPUT: &str = r"
680eJy2irIizK6zT0XOklyBAY1KVUsAbyF6eJUYBmRPHqX2rONbaEieJt4Ci1eZYjBdHdEq46oMBH0LeiQO8RIJb95
681SJGEp83RxakDj7trunJVvMbj2KZFnpJOyEauFa35dlaVG9Ki7hjFy4BLlDyA0Wgwk20RXFkbgKQIQVvR16RPffR
682WO86WqZ3gMuOh447svZRYfhbRF3NVBaWRz7SJ9Zm3w8djisvS0Y3GSnpzKnSEQirApqomfQTHTrU9ww2SMgdGuu
683EllGLsj3ze8WzIbXLlJvXdnJFz7UfsgX4xowG4d6xSiUVWCY4sVItNXlqs8adfZZHH7AjqLjlRRvWwjNCiWsiqx
684ICe9jlkdEVeRAO0BqF6FhjSxPt9X3y6WXAomB0YTIFQGyto4jMBOhWb96ny3DG3WISUSdaKWf8KaRuAQD4ao3ML
685jJZSXkTlovZTYQmYlkYo4s3635YLthuh0hSorRs0ju7ffeY3tu7VRvttgvbBLVjFJjYrwW1YAEOaxDdLnhiTIQn
686H0zRLWnCQ4Czk5BWsRLDdupJbKRWRZcQ7pehSgfc5qtXpJRFVtL2L82hxfBdiXqzXl3KdQ21CnGxTzcgEv0ptrs
687XGJwNgd04YiZzHrZL7iF3xFann6DJVyEZ0eEifTfY8rtxPCMDutjr68iFjnjy40c7SfhvsZLODuEjS4VQkIwfJc
688QP5fH3cQ2K4A4whpzTVc3yqig468Cjbxfobw4Z7YquZnuFw1TXSrM35ZBXpI4WKo9QLxmE2HkgMI1Uac2dWyG0U
689iCAxpHxC4uTIFEq2MUuGd7ZgYs8zoYpODvtAcZ8nUqKssdugQUGfXw9Cs1pcDZgEppYVVw1nYoHXKCjK3oItexs
690uIaZ0m1o91L9Js5lhaDybyDoye9zPFOnEIwKdcH0dO9cZmv6UyvVZS2oVKJm7nHQAJDARjVfC7GYAT2AQhFZxIQ
691DP9jjHCqxMJz6p499G5lk8cYAhnlUm7GCr4AwvjsEU7sEsJcZLDCLG6FaFMdLHJS5v2yPYzpuWebjcNCXbk4yER
692F9NsvlDBrLhoDt1GDgJPlRF8B5h5BSzPHsCjNVa9h2YWx1GVl6Yrrk04FSMSj0nRO8OoxkyU0ugtBQlUv3rQ833
693Vcs7jCGetaazcvaI45dRDGe6LyEPwojlC4IaB8PtljKo2zn0u91lQGJY7rj1qLUtFBRDCKERs7W1j9A2eGJ3ORY
694Db7Q3K7BY9XbANGoYiwtLoytopYCQs5RYHepkoQ19f1E9IcqCFQg9h0rWK494xb88GfSGKBpPHddrQYXFrr715u
695NkAj885V8Mnam5kSzsOmrg504QhPSOaqpkY36xyXUP13yWK4fEf39tJ2PN2DlAsxFAWJUec4CiS47rgrU87oESt
696KZJni3Jhccczlq1CaRKaYYV38joEzPL0UNKr5RiCodTWJmdN07JI5txtQqgc8kvHOrxgOASPQOPSbAUz33vZx3b
697eNsTYUD0Dxa4IkMUNHSy6mpaSOElO7wgUvWJEajnVWZJ5gWehyE4yqo6PkL3VBj51Jg2uozPa8xnbSfymlVVLFl
698EIfMyPwUj1J9ngQw0J3bn33IIOB3bkNfB50f1MkKkhyn1TMZJcnZ7IS16PXBH6DD7Sht1PVKhER2E3QS7z8YQ6B
699q27ktZZ33IcCnayahxHnyf2Wzab9ic5eSJLzsVi0VWP7DePt2GnCbz5D2tcAxgVVFmdIsEakytjmeEGyMu9k2R7
700Q8d1wPtqKgayVtgdIaMbvsnXMkRqITkf3o8Qh495pm1wkKArTGFGODXc1cCKheFUEtJWdK92DHH7OuRENHAb5KS
701PKzSUg2k18wyf9XCy1pQKv31wii3rWrWMCbxOWmhuzw1N9tqO8U97NsThRSoPAjpd05G2roia4m4CaPWTAUmVky
702RfiWoA7bglAh4Aoz2LN2ezFleTNJjjLw3n9bYPg5BdRL8n8wimhXDo9SW46A5YS62C08ZOVtvfn82YRaYkuKKz7
7033NJ25PnQG6diMm4Lm3wi22yR7lY7oYYJjLNcaLYOI6HOvaJ
704";
705
706 check_insert_invalid_arg(ARG_BAD_TYPE);
707 check_insert_invalid_arg(ARG_GIBBERISH);
708 check_insert_invalid_arg(ARG_UNQUOTED);
709 check_insert_invalid_arg(EMPTY);
710 check_insert_invalid_arg(LARGE_2K_INPUT);
711 }
712}