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