Skip to main content

icydb_core/value/
input.rs

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//
13// InputValue
14//
15// Public input-side value boundary used by literal-taking API surfaces.
16// This stays separate from runtime `Value` so public write/query inputs can
17// move off the internal execution representation incrementally.
18//
19
20#[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//
49// InputValueEnum
50//
51// Input-side enum payload contract paired with `InputValue`.
52// Payload stays recursive through `InputValue` so explicit enum-valued public
53// inputs can cross the boundary without using runtime `Value` directly.
54//
55
56#[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///
373/// TESTS
374///
375
376#[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}