Skip to main content

icydb_core/value/
input.rs

1use crate::{
2    types::{
3        Account, Date, Decimal, Duration, Float32, Float64, Int, Int128, Nat, Nat128, Principal,
4        Subaccount, Timestamp, Ulid,
5    },
6    value::{Value, ValueEnum},
7};
8use candid::CandidType;
9use serde::Deserialize;
10
11//
12// InputValue
13//
14// Public input-side value boundary used by literal-taking API surfaces.
15// This stays separate from runtime `Value` so public write/query inputs can
16// move off the internal execution representation incrementally.
17//
18
19#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
20pub enum InputValue {
21    Account(Account),
22    Blob(Vec<u8>),
23    Bool(bool),
24    Date(Date),
25    Decimal(Decimal),
26    Duration(Duration),
27    Enum(InputValueEnum),
28    Float32(Float32),
29    Float64(Float64),
30    Int(i64),
31    Int128(Int128),
32    IntBig(Int),
33    List(Vec<Self>),
34    Map(Vec<(Self, Self)>),
35    Null,
36    Principal(Principal),
37    Subaccount(Subaccount),
38    Text(String),
39    Timestamp(Timestamp),
40    Uint(u64),
41    Uint128(Nat128),
42    UintBig(Nat),
43    Ulid(Ulid),
44    Unit,
45}
46
47//
48// InputValueEnum
49//
50// Input-side enum payload contract paired with `InputValue`.
51// Payload stays recursive through `InputValue` so explicit enum-valued public
52// inputs can cross the boundary without using runtime `Value` directly.
53//
54
55#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
56pub struct InputValueEnum {
57    variant: String,
58    path: Option<String>,
59    payload: Option<Box<InputValue>>,
60}
61
62impl InputValueEnum {
63    #[must_use]
64    pub const fn variant(&self) -> &str {
65        self.variant.as_str()
66    }
67
68    #[must_use]
69    pub fn path(&self) -> Option<&str> {
70        self.path.as_deref()
71    }
72
73    #[must_use]
74    pub fn payload(&self) -> Option<&InputValue> {
75        self.payload.as_deref()
76    }
77}
78
79impl From<Value> for InputValue {
80    fn from(value: Value) -> Self {
81        Self::from(&value)
82    }
83}
84
85impl From<&Value> for InputValue {
86    fn from(value: &Value) -> Self {
87        match value {
88            Value::Account(value) => Self::Account(*value),
89            Value::Blob(value) => Self::Blob(value.clone()),
90            Value::Bool(value) => Self::Bool(*value),
91            Value::Date(value) => Self::Date(*value),
92            Value::Decimal(value) => Self::Decimal(*value),
93            Value::Duration(value) => Self::Duration(*value),
94            Value::Enum(value) => Self::Enum(InputValueEnum::from(value)),
95            Value::Float32(value) => Self::Float32(*value),
96            Value::Float64(value) => Self::Float64(*value),
97            Value::Int(value) => Self::Int(*value),
98            Value::Int128(value) => Self::Int128(*value),
99            Value::IntBig(value) => Self::IntBig(value.clone()),
100            Value::List(values) => Self::List(values.iter().map(Self::from).collect()),
101            Value::Map(entries) => Self::Map(
102                entries
103                    .iter()
104                    .map(|(key, value)| (Self::from(key), Self::from(value)))
105                    .collect(),
106            ),
107            Value::Null => Self::Null,
108            Value::Principal(value) => Self::Principal(*value),
109            Value::Subaccount(value) => Self::Subaccount(*value),
110            Value::Text(value) => Self::Text(value.clone()),
111            Value::Timestamp(value) => Self::Timestamp(*value),
112            Value::Uint(value) => Self::Uint(*value),
113            Value::Uint128(value) => Self::Uint128(*value),
114            Value::UintBig(value) => Self::UintBig(value.clone()),
115            Value::Ulid(value) => Self::Ulid(*value),
116            Value::Unit => Self::Unit,
117        }
118    }
119}
120
121impl From<InputValue> for Value {
122    fn from(value: InputValue) -> Self {
123        Self::from(&value)
124    }
125}
126
127impl From<&InputValue> for Value {
128    fn from(value: &InputValue) -> Self {
129        match value {
130            InputValue::Account(value) => Self::Account(*value),
131            InputValue::Blob(value) => Self::Blob(value.clone()),
132            InputValue::Bool(value) => Self::Bool(*value),
133            InputValue::Date(value) => Self::Date(*value),
134            InputValue::Decimal(value) => Self::Decimal(*value),
135            InputValue::Duration(value) => Self::Duration(*value),
136            InputValue::Enum(value) => Self::Enum(ValueEnum::from(value)),
137            InputValue::Float32(value) => Self::Float32(*value),
138            InputValue::Float64(value) => Self::Float64(*value),
139            InputValue::Int(value) => Self::Int(*value),
140            InputValue::Int128(value) => Self::Int128(*value),
141            InputValue::IntBig(value) => Self::IntBig(value.clone()),
142            InputValue::List(values) => Self::List(values.iter().map(Self::from).collect()),
143            InputValue::Map(entries) => Self::Map(
144                entries
145                    .iter()
146                    .map(|(key, value)| (Self::from(key), Self::from(value)))
147                    .collect(),
148            ),
149            InputValue::Null => Self::Null,
150            InputValue::Principal(value) => Self::Principal(*value),
151            InputValue::Subaccount(value) => Self::Subaccount(*value),
152            InputValue::Text(value) => Self::Text(value.clone()),
153            InputValue::Timestamp(value) => Self::Timestamp(*value),
154            InputValue::Uint(value) => Self::Uint(*value),
155            InputValue::Uint128(value) => Self::Uint128(*value),
156            InputValue::UintBig(value) => Self::UintBig(value.clone()),
157            InputValue::Ulid(value) => Self::Ulid(*value),
158            InputValue::Unit => Self::Unit,
159        }
160    }
161}
162
163impl From<ValueEnum> for InputValueEnum {
164    fn from(value: ValueEnum) -> Self {
165        Self::from(&value)
166    }
167}
168
169impl From<&ValueEnum> for InputValueEnum {
170    fn from(value: &ValueEnum) -> Self {
171        Self {
172            variant: value.variant().to_string(),
173            path: value.path().map(ToString::to_string),
174            payload: value
175                .payload()
176                .map(|payload| Box::new(InputValue::from(payload))),
177        }
178    }
179}
180
181impl From<InputValueEnum> for ValueEnum {
182    fn from(value: InputValueEnum) -> Self {
183        Self::from(&value)
184    }
185}
186
187impl From<&InputValueEnum> for ValueEnum {
188    fn from(value: &InputValueEnum) -> Self {
189        let mut runtime = Self::new(value.variant(), value.path());
190        if let Some(payload) = value.payload() {
191            runtime = runtime.with_payload(Value::from(payload));
192        }
193
194        runtime
195    }
196}
197
198///
199/// TESTS
200///
201
202#[cfg(test)]
203mod tests {
204    use crate::value::{InputValue, InputValueEnum, Value, ValueEnum};
205
206    #[test]
207    fn input_value_round_trip_keeps_recursive_collection_shape() {
208        let runtime = Value::List(vec![
209            Value::Uint(7),
210            Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
211        ]);
212
213        assert_eq!(Value::from(InputValue::from(runtime.clone())), runtime);
214    }
215
216    #[test]
217    fn input_value_enum_round_trip_keeps_payload() {
218        let runtime =
219            ValueEnum::new("Example", Some("test::InputEnum")).with_payload(Value::Uint(9));
220
221        assert_eq!(
222            InputValueEnum::from(runtime.clone()),
223            InputValueEnum {
224                variant: "Example".to_string(),
225                path: Some("test::InputEnum".to_string()),
226                payload: Some(Box::new(InputValue::Uint(9))),
227            },
228        );
229        assert_eq!(
230            ValueEnum::from(InputValueEnum::from(runtime.clone())),
231            runtime
232        );
233    }
234}