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