1use crate::{
2 traits::EntityKey,
3 types::{
4 Account, Date, Decimal, Duration, Float32, Float64, Id, Int, Int128, Nat, Nat128,
5 Principal, Subaccount, Timestamp, Ulid,
6 },
7 value::{Value, ValueEnum},
8};
9use candid::CandidType;
10use serde::Deserialize;
11
12#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
21pub enum InputValue {
22 Account(Account),
23 Blob(Vec<u8>),
24 Bool(bool),
25 Date(Date),
26 Decimal(Decimal),
27 Duration(Duration),
28 Enum(InputValueEnum),
29 Float32(Float32),
30 Float64(Float64),
31 Int(i64),
32 Int128(Int128),
33 IntBig(Int),
34 List(Vec<Self>),
35 Map(Vec<(Self, Self)>),
36 Null,
37 Principal(Principal),
38 Subaccount(Subaccount),
39 Text(String),
40 Timestamp(Timestamp),
41 Uint(u64),
42 Uint128(Nat128),
43 UintBig(Nat),
44 Ulid(Ulid),
45 Unit,
46}
47
48#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
57pub struct InputValueEnum {
58 variant: String,
59 path: Option<String>,
60 payload: Option<Box<InputValue>>,
61}
62
63impl InputValueEnum {
64 #[must_use]
65 pub const fn variant(&self) -> &str {
66 self.variant.as_str()
67 }
68
69 #[must_use]
70 pub fn path(&self) -> Option<&str> {
71 self.path.as_deref()
72 }
73
74 #[must_use]
75 pub fn payload(&self) -> Option<&InputValue> {
76 self.payload.as_deref()
77 }
78}
79
80impl From<Value> for InputValue {
81 fn from(value: Value) -> Self {
82 Self::from(&value)
83 }
84}
85
86impl From<&Value> for InputValue {
87 fn from(value: &Value) -> Self {
88 match value {
89 Value::Account(value) => Self::Account(*value),
90 Value::Blob(value) => Self::Blob(value.clone()),
91 Value::Bool(value) => Self::Bool(*value),
92 Value::Date(value) => Self::Date(*value),
93 Value::Decimal(value) => Self::Decimal(*value),
94 Value::Duration(value) => Self::Duration(*value),
95 Value::Enum(value) => Self::Enum(InputValueEnum::from(value)),
96 Value::Float32(value) => Self::Float32(*value),
97 Value::Float64(value) => Self::Float64(*value),
98 Value::Int(value) => Self::Int(*value),
99 Value::Int128(value) => Self::Int128(*value),
100 Value::IntBig(value) => Self::IntBig(value.clone()),
101 Value::List(values) => Self::List(values.iter().map(Self::from).collect()),
102 Value::Map(entries) => Self::Map(
103 entries
104 .iter()
105 .map(|(key, value)| (Self::from(key), Self::from(value)))
106 .collect(),
107 ),
108 Value::Null => Self::Null,
109 Value::Principal(value) => Self::Principal(*value),
110 Value::Subaccount(value) => Self::Subaccount(*value),
111 Value::Text(value) => Self::Text(value.clone()),
112 Value::Timestamp(value) => Self::Timestamp(*value),
113 Value::Uint(value) => Self::Uint(*value),
114 Value::Uint128(value) => Self::Uint128(*value),
115 Value::UintBig(value) => Self::UintBig(value.clone()),
116 Value::Ulid(value) => Self::Ulid(*value),
117 Value::Unit => Self::Unit,
118 }
119 }
120}
121
122impl From<InputValue> for Value {
123 fn from(value: InputValue) -> Self {
124 Self::from(&value)
125 }
126}
127
128impl From<&InputValue> for Value {
129 fn from(value: &InputValue) -> Self {
130 match value {
131 InputValue::Account(value) => Self::Account(*value),
132 InputValue::Blob(value) => Self::Blob(value.clone()),
133 InputValue::Bool(value) => Self::Bool(*value),
134 InputValue::Date(value) => Self::Date(*value),
135 InputValue::Decimal(value) => Self::Decimal(*value),
136 InputValue::Duration(value) => Self::Duration(*value),
137 InputValue::Enum(value) => Self::Enum(ValueEnum::from(value)),
138 InputValue::Float32(value) => Self::Float32(*value),
139 InputValue::Float64(value) => Self::Float64(*value),
140 InputValue::Int(value) => Self::Int(*value),
141 InputValue::Int128(value) => Self::Int128(*value),
142 InputValue::IntBig(value) => Self::IntBig(value.clone()),
143 InputValue::List(values) => Self::List(values.iter().map(Self::from).collect()),
144 InputValue::Map(entries) => Self::Map(
145 entries
146 .iter()
147 .map(|(key, value)| (Self::from(key), Self::from(value)))
148 .collect(),
149 ),
150 InputValue::Null => Self::Null,
151 InputValue::Principal(value) => Self::Principal(*value),
152 InputValue::Subaccount(value) => Self::Subaccount(*value),
153 InputValue::Text(value) => Self::Text(value.clone()),
154 InputValue::Timestamp(value) => Self::Timestamp(*value),
155 InputValue::Uint(value) => Self::Uint(*value),
156 InputValue::Uint128(value) => Self::Uint128(*value),
157 InputValue::UintBig(value) => Self::UintBig(value.clone()),
158 InputValue::Ulid(value) => Self::Ulid(*value),
159 InputValue::Unit => Self::Unit,
160 }
161 }
162}
163
164impl From<ValueEnum> for InputValueEnum {
165 fn from(value: ValueEnum) -> Self {
166 Self::from(&value)
167 }
168}
169
170impl From<&ValueEnum> for InputValueEnum {
171 fn from(value: &ValueEnum) -> Self {
172 Self {
173 variant: value.variant().to_string(),
174 path: value.path().map(ToString::to_string),
175 payload: value
176 .payload()
177 .map(|payload| Box::new(InputValue::from(payload))),
178 }
179 }
180}
181
182impl From<InputValueEnum> for ValueEnum {
183 fn from(value: InputValueEnum) -> Self {
184 Self::from(&value)
185 }
186}
187
188impl From<&InputValueEnum> for ValueEnum {
189 fn from(value: &InputValueEnum) -> Self {
190 let mut runtime = Self::new(value.variant(), value.path());
191 if let Some(payload) = value.payload() {
192 runtime = runtime.with_payload(Value::from(payload));
193 }
194
195 runtime
196 }
197}
198
199impl From<&str> for InputValue {
200 fn from(value: &str) -> Self {
201 Self::Text(value.to_string())
202 }
203}
204
205impl From<String> for InputValue {
206 fn from(value: String) -> Self {
207 Self::Text(value)
208 }
209}
210
211impl From<Vec<u8>> for InputValue {
212 fn from(value: Vec<u8>) -> Self {
213 Self::Blob(value)
214 }
215}
216
217impl From<bool> for InputValue {
218 fn from(value: bool) -> Self {
219 Self::Bool(value)
220 }
221}
222
223impl From<Account> for InputValue {
224 fn from(value: Account) -> Self {
225 Self::Account(value)
226 }
227}
228
229impl From<Date> for InputValue {
230 fn from(value: Date) -> Self {
231 Self::Date(value)
232 }
233}
234
235impl From<Decimal> for InputValue {
236 fn from(value: Decimal) -> Self {
237 Self::Decimal(value)
238 }
239}
240
241impl From<Duration> for InputValue {
242 fn from(value: Duration) -> Self {
243 Self::Duration(value)
244 }
245}
246
247impl From<Float32> for InputValue {
248 fn from(value: Float32) -> Self {
249 Self::Float32(value)
250 }
251}
252
253impl From<Float64> for InputValue {
254 fn from(value: Float64) -> Self {
255 Self::Float64(value)
256 }
257}
258
259impl From<Int> for InputValue {
260 fn from(value: Int) -> Self {
261 Self::IntBig(value)
262 }
263}
264
265impl From<Int128> for InputValue {
266 fn from(value: Int128) -> Self {
267 Self::Int128(value)
268 }
269}
270
271impl From<Nat> for InputValue {
272 fn from(value: Nat) -> Self {
273 Self::UintBig(value)
274 }
275}
276
277impl From<Nat128> for InputValue {
278 fn from(value: Nat128) -> Self {
279 Self::Uint128(value)
280 }
281}
282
283impl From<Principal> for InputValue {
284 fn from(value: Principal) -> Self {
285 Self::Principal(value)
286 }
287}
288
289impl From<Subaccount> for InputValue {
290 fn from(value: Subaccount) -> Self {
291 Self::Subaccount(value)
292 }
293}
294
295impl From<Timestamp> for InputValue {
296 fn from(value: Timestamp) -> Self {
297 Self::Timestamp(value)
298 }
299}
300
301impl From<Ulid> for InputValue {
302 fn from(value: Ulid) -> Self {
303 Self::Ulid(value)
304 }
305}
306
307impl From<()> for InputValue {
308 fn from((): ()) -> Self {
309 Self::Unit
310 }
311}
312
313impl<T> From<Option<T>> for InputValue
314where
315 T: Into<Self>,
316{
317 fn from(value: Option<T>) -> Self {
318 match value {
319 Some(value) => value.into(),
320 None => Self::Null,
321 }
322 }
323}
324
325impl<E> From<Id<E>> for InputValue
326where
327 E: EntityKey,
328 E::Key: Into<Self>,
329{
330 fn from(value: Id<E>) -> Self {
331 value.into_key().into()
332 }
333}
334
335impl<E> From<&Id<E>> for InputValue
336where
337 E: EntityKey,
338 E::Key: Into<Self>,
339{
340 fn from(value: &Id<E>) -> Self {
341 value.key().into()
342 }
343}
344
345macro_rules! impl_input_value_int {
346 ($($ty:ty),* $(,)?) => {
347 $(
348 impl From<$ty> for InputValue {
349 fn from(value: $ty) -> Self {
350 Self::Int(i64::from(value))
351 }
352 }
353 )*
354 };
355}
356
357macro_rules! impl_input_value_uint {
358 ($($ty:ty),* $(,)?) => {
359 $(
360 impl From<$ty> for InputValue {
361 fn from(value: $ty) -> Self {
362 Self::Uint(u64::from(value))
363 }
364 }
365 )*
366 };
367}
368
369impl_input_value_int!(i8, i16, i32, i64);
370impl_input_value_uint!(u8, u16, u32, u64);
371
372#[cfg(test)]
377mod tests {
378 use crate::value::{InputValue, InputValueEnum, Value, ValueEnum};
379
380 #[test]
381 fn input_value_round_trip_keeps_recursive_collection_shape() {
382 let runtime = Value::List(vec![
383 Value::Uint(7),
384 Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
385 ]);
386
387 assert_eq!(Value::from(InputValue::from(runtime.clone())), runtime);
388 }
389
390 #[test]
391 fn input_value_enum_round_trip_keeps_payload() {
392 let runtime =
393 ValueEnum::new("Example", Some("test::InputEnum")).with_payload(Value::Uint(9));
394
395 assert_eq!(
396 InputValueEnum::from(runtime.clone()),
397 InputValueEnum {
398 variant: "Example".to_string(),
399 path: Some("test::InputEnum".to_string()),
400 payload: Some(Box::new(InputValue::Uint(9))),
401 },
402 );
403 assert_eq!(
404 ValueEnum::from(InputValueEnum::from(runtime.clone())),
405 runtime
406 );
407 }
408}