odra_core/
args.rs

1//! This module provides types and traits for working with entrypoint arguments.
2
3use crate::{contract_def::Argument, prelude::*, ContractEnv};
4use casper_types::{
5    bytesrepr::{FromBytes, ToBytes},
6    CLType, CLTyped, Parameter, RuntimeArgs
7};
8
9/// A type that represents an entrypoint arg that may or may not be present.
10#[derive(Default, Debug, Clone)]
11pub enum Maybe<T> {
12    /// A value is present.
13    Some(T),
14    /// No value is present.
15    #[default]
16    None
17}
18
19impl<T> Maybe<T> {
20    /// Returns `true` if the value is present.
21    pub fn is_some(&self) -> bool {
22        matches!(self, Maybe::Some(_))
23    }
24
25    /// Returns `true` if the value is not present.
26    pub fn is_none(&self) -> bool {
27        matches!(self, Maybe::None)
28    }
29
30    /// Unwraps the value.
31    /// If the value is not present, the contract reverts with an `ExecutionError::UnwrapError`.
32    pub fn unwrap(self, env: &ContractEnv) -> T {
33        match self {
34            Maybe::Some(value) => value,
35            Maybe::None => env.revert(ExecutionError::UnwrapError)
36        }
37    }
38
39    /// Unwraps the value or returns the default value.
40    pub fn unwrap_or(self, default: T) -> T {
41        match self {
42            Maybe::Some(value) => value,
43            Maybe::None => default
44        }
45    }
46}
47
48impl<T: Default> Maybe<T> {
49    /// Unwraps the value or returns the default value.
50    pub fn unwrap_or_default(self) -> T {
51        match self {
52            Maybe::Some(value) => value,
53            Maybe::None => T::default()
54        }
55    }
56}
57
58impl<T: ToBytes> ToBytes for Maybe<T> {
59    fn to_bytes(&self) -> Result<Vec<u8>, casper_types::bytesrepr::Error> {
60        match self {
61            Maybe::Some(value) => value.to_bytes(),
62            Maybe::None => Ok(Vec::new())
63        }
64    }
65
66    fn serialized_length(&self) -> usize {
67        match self {
68            Maybe::Some(value) => value.serialized_length(),
69            Maybe::None => 0
70        }
71    }
72}
73
74impl<T: FromBytes> FromBytes for Maybe<T> {
75    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), casper_types::bytesrepr::Error> {
76        let res = T::from_bytes(bytes);
77        if let Ok((value, rem)) = res {
78            Ok((Maybe::Some(value), rem))
79        } else {
80            Ok((Maybe::None, bytes))
81        }
82    }
83
84    fn from_vec(bytes: Vec<u8>) -> Result<(Self, Vec<u8>), casper_types::bytesrepr::Error> {
85        Self::from_bytes(bytes.as_slice()).map(|(x, remainder)| (x, Vec::from(remainder)))
86    }
87}
88
89/// A trait for types that can be used as entrypoint arguments.
90pub trait EntrypointArgument: Sized {
91    /// Returns `true` if the argument is required.
92    fn is_required() -> bool;
93    /// Returns the CLType of the argument.
94    fn cl_type() -> CLType;
95    /// Inserts the argument into the runtime args.
96    fn insert_runtime_arg(self, name: &str, args: &mut RuntimeArgs);
97    /// Unwraps the argument from an Option.
98    fn unwrap(value: Option<Self>, env: &ContractEnv) -> Self;
99}
100
101impl<T: CLTyped + ToBytes> EntrypointArgument for Maybe<T> {
102    fn is_required() -> bool {
103        false
104    }
105
106    fn cl_type() -> CLType {
107        T::cl_type()
108    }
109
110    fn insert_runtime_arg(self, name: &str, args: &mut RuntimeArgs) {
111        if let Maybe::Some(v) = self {
112            let _ = args.insert(name, v);
113        }
114    }
115
116    fn unwrap(value: Option<Self>, _env: &ContractEnv) -> Self {
117        match value {
118            Some(v) => v,
119            None => Maybe::None
120        }
121    }
122}
123
124impl<T: CLTyped + ToBytes> EntrypointArgument for T {
125    fn is_required() -> bool {
126        true
127    }
128
129    fn cl_type() -> CLType {
130        T::cl_type()
131    }
132
133    fn insert_runtime_arg(self, name: &str, args: &mut RuntimeArgs) {
134        let _ = args.insert(name, self);
135    }
136
137    fn unwrap(value: Option<Self>, env: &ContractEnv) -> Self {
138        match value {
139            Some(v) => v,
140            None => env.revert(ExecutionError::UnwrapError)
141        }
142    }
143}
144
145/// Returns a Casper entrypoint argument representation.
146/// If the parameter is not required, it returns `None`.
147pub fn parameter<T: EntrypointArgument>(name: &str) -> Option<Parameter> {
148    match T::is_required() {
149        true => Some(Parameter::new(name, T::cl_type())),
150        false => None
151    }
152}
153
154/// Returns an Odra's entrypoint argument representation.
155pub fn odra_argument<T: EntrypointArgument>(name: &str) -> Argument {
156    Argument {
157        name: name.to_string(),
158        ty: T::cl_type(),
159        is_ref: false,
160        is_slice: false,
161        is_required: T::is_required()
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use crate::contract_context::MockContractContext;
169    use casper_types::U256;
170
171    #[test]
172    fn test_maybe() {
173        let some = Maybe::Some(1);
174        let none: Maybe<u32> = Maybe::None;
175
176        let ctx = MockContractContext::new();
177        let env = ContractEnv::new(0, Rc::new(RefCell::new(ctx)));
178
179        assert!(some.is_some());
180        assert!(!some.is_none());
181        assert_eq!(some.clone().unwrap(&env), 1);
182        assert_eq!(some.unwrap_or_default(), 1);
183
184        assert!(!none.is_some());
185        assert!(none.is_none());
186        assert_eq!(none.unwrap_or_default(), 0);
187    }
188
189    #[test]
190    #[should_panic(expected = "revert")]
191    fn unwrap_on_none() {
192        let none: Maybe<u32> = Maybe::None;
193        let mut ctx = MockContractContext::new();
194        ctx.expect_revert().returning(|_| panic!("revert"));
195        let env = ContractEnv::new(0, Rc::new(RefCell::new(ctx)));
196
197        none.unwrap(&env);
198    }
199
200    #[test]
201    fn test_into_args() {
202        let args = [
203            odra_argument::<Maybe<u32>>("arg1"),
204            odra_argument::<U256>("arg2"),
205            odra_argument::<Option<String>>("arg3")
206        ];
207
208        assert_eq!(args.len(), 3);
209    }
210
211    #[test]
212    fn test_into_casper_parameters() {
213        let params = [
214            parameter::<Maybe<u32>>("arg1"),
215            parameter::<Option<u32>>("arg2"),
216            parameter::<Maybe<Option<u32>>>("arg3"),
217            parameter::<Address>("arg4")
218        ]
219        .into_iter()
220        .flatten()
221        .collect::<Vec<_>>();
222
223        assert_eq!(params.len(), 2);
224    }
225}