casper_client/cli/
simple_args.rs

1//! `CLValue` parsing and validation from "simple args" syntax.
2
3pub 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
48/// Parses to a given `CLValue` taking into account whether the arg represents an optional type or
49/// not.
50fn 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    // Safe to unwrap as we already matched on `t.starts_with(BYTE_ARRAY_PREFIX)` to get here.
202    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
250/// Takes the input type and value and returns a tuple containing:
251///   * the simple type (i.e. the type with any "opt_" prefix removed)
252///   * the `OptionalStatus`: if there was an "opt_" prefix, then `None` or `Some` depending on
253///     whether the value is "null" or not, or `NotOptional` if there was no "opt_" prefix
254///   * the value, trimmed of leading and trailing single quotes
255fn 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
291/// Returns a value built from a single arg which has been split into its constituent parts.
292fn 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
332/// Splits a single arg of the form `NAME:TYPE='VALUE'` into its constituent parts.
333fn 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
346/// Insert a value built from a single arg into `runtime_args`.
347pub 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"; // value needs single quotes to be valid
678        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}