1use crate::{contract_def::Argument, prelude::*, ContractEnv};
4use casper_types::{
5 bytesrepr::{FromBytes, ToBytes},
6 CLType, CLTyped, Parameter, RuntimeArgs
7};
8
9#[derive(Default, Debug, Clone)]
11pub enum Maybe<T> {
12 Some(T),
14 #[default]
16 None
17}
18
19impl<T> Maybe<T> {
20 pub fn is_some(&self) -> bool {
22 matches!(self, Maybe::Some(_))
23 }
24
25 pub fn is_none(&self) -> bool {
27 matches!(self, Maybe::None)
28 }
29
30 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 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 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
89pub trait EntrypointArgument: Sized {
91 fn is_required() -> bool;
93 fn cl_type() -> CLType;
95 fn insert_runtime_arg(self, name: &str, args: &mut RuntimeArgs);
97 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
145pub struct BatchUpgradeArgs<T: Into<casper_types::RuntimeArgs>>(BTreeMap<String, T>);
147
148impl<T: Into<casper_types::RuntimeArgs>> From<BTreeMap<String, T>> for BatchUpgradeArgs<T> {
149 fn from(val: BTreeMap<String, T>) -> Self {
150 BatchUpgradeArgs(val)
151 }
152}
153
154impl<T: Into<casper_types::RuntimeArgs>> EntrypointArgument for BatchUpgradeArgs<T> {
155 fn is_required() -> bool {
156 true
157 }
158
159 fn cl_type() -> CLType {
160 CLType::Map {
161 key: Box::new(CLType::String),
162 value: Box::new(CLType::List(Box::new(CLType::U8)))
163 }
164 }
165
166 fn insert_runtime_arg(self, name: &str, args: &mut RuntimeArgs) {
167 let mut args_map: BTreeMap<String, casper_types::bytesrepr::Bytes> = Default::default();
168 for (contract, v) in self.0 {
169 let rt: RuntimeArgs = v.into();
170 let bytes = ToBytes::to_bytes(&rt).unwrap();
171 args_map.insert(
172 contract.to_string(),
173 casper_types::bytesrepr::Bytes::from(bytes)
174 );
175 }
176 let _ = args.insert(name, args_map);
177 }
178
179 fn unwrap(value: Option<Self>, env: &ContractEnv) -> Self {
180 match value {
181 Some(v) => v,
182 None => env.revert(ExecutionError::UnwrapError)
183 }
184 }
185}
186
187pub fn parameter<T: EntrypointArgument>(name: &str) -> Option<Parameter> {
190 match T::is_required() {
191 true => Some(Parameter::new(name, T::cl_type())),
192 false => None
193 }
194}
195
196pub fn odra_argument<T: EntrypointArgument>(name: &str) -> Argument {
198 Argument {
199 name: name.to_string(),
200 ty: T::cl_type(),
201 is_ref: false,
202 is_slice: false,
203 is_required: T::is_required()
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::contract_context::MockContractContext;
211 use casper_types::U256;
212
213 #[test]
214 fn test_maybe() {
215 let some = Maybe::Some(1);
216 let none: Maybe<u32> = Maybe::None;
217
218 let ctx = MockContractContext::new();
219 let env = ContractEnv::new(0, Rc::new(RefCell::new(ctx)));
220
221 assert!(some.is_some());
222 assert!(!some.is_none());
223 assert_eq!(some.clone().unwrap(&env), 1);
224 assert_eq!(some.unwrap_or_default(), 1);
225
226 assert!(!none.is_some());
227 assert!(none.is_none());
228 assert_eq!(none.unwrap_or_default(), 0);
229 }
230
231 #[test]
232 #[should_panic(expected = "revert")]
233 fn unwrap_on_none() {
234 let none: Maybe<u32> = Maybe::None;
235 let mut ctx = MockContractContext::new();
236 ctx.expect_revert().returning(|_| panic!("revert"));
237 let env = ContractEnv::new(0, Rc::new(RefCell::new(ctx)));
238
239 none.unwrap(&env);
240 }
241
242 #[test]
243 fn test_into_args() {
244 let args = [
245 odra_argument::<Maybe<u32>>("arg1"),
246 odra_argument::<U256>("arg2"),
247 odra_argument::<Option<String>>("arg3")
248 ];
249
250 assert_eq!(args.len(), 3);
251 }
252
253 #[test]
254 fn test_into_casper_parameters() {
255 let params = [
256 parameter::<Maybe<u32>>("arg1"),
257 parameter::<Option<u32>>("arg2"),
258 parameter::<Maybe<Option<u32>>>("arg3"),
259 parameter::<Address>("arg4")
260 ]
261 .into_iter()
262 .flatten()
263 .collect::<Vec<_>>();
264
265 assert_eq!(params.len(), 2);
266 }
267}