odra_cli/cmd/
args.rs

1use std::any::Any;
2
3use crate::parser::{CLTypedParser, CsprTokenAmountParser, GasParser};
4use clap::{builder::PathBufValueParser, ArgAction, ArgMatches};
5use odra::schema::casper_contract_schema::NamedCLType;
6
7pub const ARG_ATTACHED_VALUE: &str = "attached_value";
8pub const ARG_GAS: &str = "gas";
9pub const ARG_CONTRACTS: &str = "contracts-toml";
10pub const ARG_PRINT_EVENTS: &str = "print-events";
11pub const ARG_NUMBER: &str = "number";
12
13#[derive(Debug, thiserror::Error)]
14pub enum ArgsError {
15    #[error("Invalid arg value: {0}")]
16    TypesError(#[from] crate::types::Error),
17    #[error("Decoding error: {0}")]
18    DecodingError(String),
19    #[error("Arg not found: {0}")]
20    ArgNotFound(String),
21    #[error("Arg type not found: {0}")]
22    ArgTypeNotFound(String)
23}
24
25/// A typed command argument.
26#[derive(Debug, PartialEq)]
27pub struct CommandArg {
28    pub name: String,
29    pub required: bool,
30    pub description: String,
31    pub ty: NamedCLType,
32    pub is_list_element: bool
33}
34
35impl CommandArg {
36    pub fn new(name: &str, description: &str, ty: NamedCLType) -> Self {
37        Self {
38            name: name.to_string(),
39            description: description.to_string(),
40            ty,
41            required: false,
42            is_list_element: false
43        }
44    }
45
46    pub fn required(self) -> Self {
47        Self {
48            required: true,
49            ..self
50        }
51    }
52
53    pub fn list(self) -> Self {
54        Self {
55            is_list_element: true,
56            ..self
57        }
58    }
59
60    pub(crate) fn split_name(&self) -> Vec<String> {
61        self.name
62            .split('.')
63            .map(|s| s.to_string())
64            .collect::<Vec<_>>()
65    }
66}
67
68impl From<CommandArg> for clap::Arg {
69    fn from(arg: CommandArg) -> Self {
70        let result = clap::Arg::new(&arg.name)
71            .long(arg.name)
72            .value_name(format!("{:?}", arg.ty))
73            .required(arg.required)
74            .value_parser(CLTypedParser::new(arg.ty))
75            .help(arg.description);
76
77        match arg.is_list_element {
78            true => result.action(ArgAction::Append),
79            false => result.action(ArgAction::Set)
80        }
81    }
82}
83
84pub enum Arg {
85    AttachedValue,
86    Gas,
87    Contracts,
88    EventsNumber,
89    PrintEvents
90}
91
92impl Arg {
93    pub fn name(&self) -> &str {
94        match self {
95            Arg::AttachedValue => ARG_ATTACHED_VALUE,
96            Arg::Gas => ARG_GAS,
97            Arg::Contracts => ARG_CONTRACTS,
98            Arg::EventsNumber => ARG_NUMBER,
99            Arg::PrintEvents => ARG_PRINT_EVENTS
100        }
101    }
102}
103
104impl From<Arg> for clap::Arg {
105    fn from(arg: Arg) -> Self {
106        match arg {
107            Arg::AttachedValue => arg_attached_value(),
108            Arg::Gas => arg_gas(),
109            Arg::Contracts => arg_contracts(),
110            Arg::EventsNumber => arg_number("Number of events to print"),
111            Arg::PrintEvents => arg_print_events()
112        }
113    }
114}
115
116fn arg_attached_value() -> clap::Arg {
117    clap::Arg::new(ARG_ATTACHED_VALUE)
118        .help("The amount of CSPRs attached to the call")
119        .long(ARG_ATTACHED_VALUE)
120        .required(false)
121        .value_name("CSPR")
122        .value_parser(CsprTokenAmountParser)
123        .action(ArgAction::Set)
124}
125
126fn arg_gas() -> clap::Arg {
127    clap::Arg::new(ARG_GAS)
128        .help("The amount of gas to attach to the call")
129        .long(ARG_GAS)
130        .required(true)
131        .value_name("CSPR")
132        .value_parser(GasParser)
133        .action(ArgAction::Set)
134}
135
136fn arg_contracts() -> clap::Arg {
137    clap::Arg::new(ARG_CONTRACTS)
138        .help("The path to the file with the deployed contracts. Relative to the project root.")
139        .long(ARG_CONTRACTS)
140        .short('c')
141        .required(false)
142        .value_name("PathBuf")
143        .value_parser(PathBufValueParser::new())
144        .action(ArgAction::Set)
145}
146
147fn arg_number(description: &'static str) -> clap::Arg {
148    clap::Arg::new(ARG_NUMBER)
149        .short('n')
150        .long(ARG_NUMBER)
151        .value_name("N")
152        .default_value("10")
153        .value_parser(clap::value_parser!(u32).range(1..50))
154        .help(description)
155}
156
157fn arg_print_events() -> clap::Arg {
158    clap::Arg::new(ARG_PRINT_EVENTS)
159        .long(ARG_PRINT_EVENTS)
160        .short('p')
161        .help("Print events emitted by the contract")
162        .action(ArgAction::SetTrue)
163}
164
165pub fn read_arg<T: ToOwned<Owned = T> + Any + Clone + Send + Sync + 'static>(
166    matches: &ArgMatches,
167    arg: Arg
168) -> Option<T> {
169    matches.get_one::<T>(arg.name()).map(ToOwned::to_owned)
170}