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#[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#[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#[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}