1#![allow(clippy::field_reassign_with_default)]
5
6use alloc::{collections::BTreeMap, string::String, vec::Vec};
7
8#[cfg(feature = "datasize")]
9use datasize::DataSize;
10#[cfg(feature = "json-schema")]
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 bytesrepr::{self, Error, FromBytes, ToBytes},
16 CLType, CLTyped, CLValue, CLValueError, U512,
17};
18#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize, Debug)]
20#[cfg_attr(feature = "datasize", derive(DataSize))]
21#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
22pub struct NamedArg(String, CLValue);
23
24impl NamedArg {
25 pub fn new(name: String, value: CLValue) -> Self {
27 NamedArg(name, value)
28 }
29
30 pub fn name(&self) -> &str {
32 &self.0
33 }
34
35 pub fn cl_value(&self) -> &CLValue {
37 &self.1
38 }
39
40 pub fn cl_value_mut(&mut self) -> &mut CLValue {
42 &mut self.1
43 }
44}
45
46impl From<(String, CLValue)> for NamedArg {
47 fn from((name, value): (String, CLValue)) -> NamedArg {
48 NamedArg(name, value)
49 }
50}
51
52impl ToBytes for NamedArg {
53 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
54 let mut result = bytesrepr::allocate_buffer(self)?;
55 result.append(&mut self.0.to_bytes()?);
56 result.append(&mut self.1.to_bytes()?);
57 Ok(result)
58 }
59
60 fn serialized_length(&self) -> usize {
61 self.0.serialized_length() + self.1.serialized_length()
62 }
63}
64
65impl FromBytes for NamedArg {
66 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
67 let (name, remainder) = String::from_bytes(bytes)?;
68 let (cl_value, remainder) = CLValue::from_bytes(remainder)?;
69 Ok((NamedArg(name, cl_value), remainder))
70 }
71}
72
73#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize, Debug, Default)]
75#[cfg_attr(feature = "datasize", derive(DataSize))]
76#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
77pub struct RuntimeArgs(Vec<NamedArg>);
78
79impl RuntimeArgs {
80 pub fn new() -> RuntimeArgs {
82 RuntimeArgs::default()
83 }
84
85 pub fn try_new<F>(func: F) -> Result<RuntimeArgs, CLValueError>
93 where
94 F: FnOnce(&mut RuntimeArgs) -> Result<(), CLValueError>,
95 {
96 let mut runtime_args = RuntimeArgs::new();
97 func(&mut runtime_args)?;
98 Ok(runtime_args)
99 }
100
101 pub fn get(&self, name: &str) -> Option<&CLValue> {
103 self.0.iter().find_map(|NamedArg(named_name, named_value)| {
104 if named_name == name {
105 Some(named_value)
106 } else {
107 None
108 }
109 })
110 }
111
112 pub fn len(&self) -> usize {
114 self.0.len()
115 }
116
117 pub fn is_empty(&self) -> bool {
119 self.0.is_empty()
120 }
121
122 pub fn insert<K, V>(&mut self, key: K, value: V) -> Result<(), CLValueError>
124 where
125 K: Into<String>,
126 V: CLTyped + ToBytes,
127 {
128 let cl_value = CLValue::from_t(value)?;
129 self.0.push(NamedArg(key.into(), cl_value));
130 Ok(())
131 }
132
133 pub fn insert_cl_value<K>(&mut self, key: K, cl_value: CLValue)
135 where
136 K: Into<String>,
137 {
138 self.0.push(NamedArg(key.into(), cl_value));
139 }
140
141 pub fn to_values(&self) -> Vec<&CLValue> {
143 self.0.iter().map(|NamedArg(_name, value)| value).collect()
144 }
145
146 pub fn named_args(&self) -> impl Iterator<Item = &NamedArg> {
148 self.0.iter()
149 }
150
151 pub fn named_args_mut(&mut self) -> impl Iterator<Item = &mut NamedArg> {
153 self.0.iter_mut()
154 }
155
156 pub fn try_get_number(&self, name: &str) -> Result<U512, CLValueError> {
163 let amount_arg = match self.get(name) {
164 None => return Ok(U512::zero()),
165 Some(arg) => arg,
166 };
167 match amount_arg.cl_type() {
168 CLType::U512 => amount_arg.clone().into_t::<U512>(),
169 CLType::U64 => amount_arg.clone().into_t::<u64>().map(U512::from),
170 _ => Ok(U512::zero()),
171 }
172 }
173}
174
175impl From<Vec<NamedArg>> for RuntimeArgs {
176 fn from(values: Vec<NamedArg>) -> Self {
177 RuntimeArgs(values)
178 }
179}
180
181impl From<BTreeMap<String, CLValue>> for RuntimeArgs {
182 fn from(cl_values: BTreeMap<String, CLValue>) -> RuntimeArgs {
183 RuntimeArgs(cl_values.into_iter().map(NamedArg::from).collect())
184 }
185}
186
187impl From<RuntimeArgs> for BTreeMap<String, CLValue> {
188 fn from(args: RuntimeArgs) -> BTreeMap<String, CLValue> {
189 let mut map = BTreeMap::new();
190 for named in args.0 {
191 map.insert(named.0, named.1);
192 }
193 map
194 }
195}
196
197impl ToBytes for RuntimeArgs {
198 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
199 self.0.to_bytes()
200 }
201
202 fn serialized_length(&self) -> usize {
203 self.0.serialized_length()
204 }
205}
206
207impl FromBytes for RuntimeArgs {
208 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
209 let (args, remainder) = Vec::<NamedArg>::from_bytes(bytes)?;
210 Ok((RuntimeArgs(args), remainder))
211 }
212}
213
214#[macro_export]
228macro_rules! runtime_args {
229 () => (RuntimeArgs::new());
230 ( $($key:expr => $value:expr,)+ ) => (runtime_args!($($key => $value),+));
231 ( $($key:expr => $value:expr),* ) => {
232 {
233 let mut named_args = RuntimeArgs::new();
234 $(
235 named_args.insert($key, $value).unwrap();
236 )*
237 named_args
238 }
239 };
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 const ARG_AMOUNT: &str = "amount";
247
248 #[test]
249 fn test_runtime_args() {
250 let arg1 = CLValue::from_t(1).unwrap();
251 let arg2 = CLValue::from_t("Foo").unwrap();
252 let arg3 = CLValue::from_t(Some(1)).unwrap();
253 let args = {
254 let mut map = BTreeMap::new();
255 map.insert("bar".into(), arg2.clone());
256 map.insert("foo".into(), arg1.clone());
257 map.insert("qwer".into(), arg3.clone());
258 map
259 };
260 let runtime_args = RuntimeArgs::from(args);
261 assert_eq!(runtime_args.get("qwer"), Some(&arg3));
262 assert_eq!(runtime_args.get("foo"), Some(&arg1));
263 assert_eq!(runtime_args.get("bar"), Some(&arg2));
264 assert_eq!(runtime_args.get("aaa"), None);
265
266 let runtime_args_2 = runtime_args! {
269 "bar" => "Foo",
270 "foo" => 1i32,
271 "qwer" => Some(1i32),
272 };
273 assert_eq!(runtime_args, runtime_args_2);
274 }
275
276 #[test]
277 fn empty_macro() {
278 assert_eq!(runtime_args! {}, RuntimeArgs::new());
279 }
280
281 #[test]
282 fn btreemap_compat() {
283 let runtime_args_1 = runtime_args! {
285 "bar" => "Foo",
286 "foo" => 1i32,
287 "qwer" => Some(1i32),
288 };
289 let tagless = runtime_args_1.to_bytes().unwrap().to_vec();
290
291 let mut runtime_args_2 = BTreeMap::new();
292 runtime_args_2.insert(String::from("bar"), CLValue::from_t("Foo").unwrap());
293 runtime_args_2.insert(String::from("foo"), CLValue::from_t(1i32).unwrap());
294 runtime_args_2.insert(String::from("qwer"), CLValue::from_t(Some(1i32)).unwrap());
295
296 assert_eq!(tagless, runtime_args_2.to_bytes().unwrap());
297 }
298
299 #[test]
300 fn named_serialization_roundtrip() {
301 let args = runtime_args! {
302 "foo" => 1i32,
303 };
304 bytesrepr::test_serialization_roundtrip(&args);
305 }
306
307 #[test]
308 fn should_create_args_with() {
309 let res = RuntimeArgs::try_new(|runtime_args| {
310 runtime_args.insert(String::from("foo"), 123)?;
311 runtime_args.insert(String::from("bar"), 456)?;
312 Ok(())
313 });
314
315 let expected = runtime_args! {
316 "foo" => 123,
317 "bar" => 456,
318 };
319 assert!(matches!(res, Ok(args) if expected == args));
320 }
321
322 #[test]
323 fn try_get_number_should_work() {
324 let mut args = RuntimeArgs::new();
325 args.insert(ARG_AMOUNT, 0u64).expect("is ok");
326 assert_eq!(args.try_get_number(ARG_AMOUNT).unwrap(), U512::zero());
327
328 let mut args = RuntimeArgs::new();
329 args.insert(ARG_AMOUNT, U512::zero()).expect("is ok");
330 assert_eq!(args.try_get_number(ARG_AMOUNT).unwrap(), U512::zero());
331
332 let args = RuntimeArgs::new();
333 assert_eq!(args.try_get_number(ARG_AMOUNT).unwrap(), U512::zero());
334
335 let hundred = 100u64;
336
337 let mut args = RuntimeArgs::new();
338 let input = U512::from(hundred);
339 args.insert(ARG_AMOUNT, input).expect("is ok");
340 assert_eq!(args.try_get_number(ARG_AMOUNT).unwrap(), input);
341
342 let mut args = RuntimeArgs::new();
343 args.insert(ARG_AMOUNT, hundred).expect("is ok");
344 assert_eq!(
345 args.try_get_number(ARG_AMOUNT).unwrap(),
346 U512::from(hundred)
347 );
348 }
349
350 #[test]
351 fn try_get_number_should_return_zero_for_non_numeric_type() {
352 let mut args = RuntimeArgs::new();
353 args.insert(ARG_AMOUNT, "Non-numeric-string").unwrap();
354 assert_eq!(
355 args.try_get_number(ARG_AMOUNT).expect("should get amount"),
356 U512::zero()
357 );
358 }
359
360 #[test]
361 fn try_get_number_should_return_zero_if_amount_is_missing() {
362 let args = RuntimeArgs::new();
363 assert_eq!(
364 args.try_get_number(ARG_AMOUNT).expect("should get amount"),
365 U512::zero()
366 );
367 }
368}