icydb_core/value/
input.rs1use crate::{
2 db::EntityKey,
3 types::{
4 Account, Blob, Date, Decimal, Duration, Float32, Float64, Id, IntBig, NatBig, Principal,
5 Subaccount, Timestamp, U256, Ulid, Unit,
6 },
7 value::{PublicEnumValue, PublicValue, Value},
8};
9use candid::{CandidType, types::Serializer};
10use serde::{Deserialize, Deserializer};
11
12#[repr(transparent)]
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct InputValue(PublicValue);
23
24impl InputValue {
25 #[must_use]
27 pub const fn from_public(value: PublicValue) -> Self {
28 Self(value)
29 }
30
31 #[must_use]
33 pub const fn as_public(&self) -> &PublicValue {
34 &self.0
35 }
36
37 #[must_use]
39 pub fn into_public(self) -> PublicValue {
40 self.0
41 }
42
43 #[must_use]
45 pub const fn account(value: Account) -> Self {
46 Self(PublicValue::Account(value))
47 }
48
49 #[must_use]
51 pub const fn blob(value: Vec<u8>) -> Self {
52 Self(PublicValue::Blob(value))
53 }
54
55 #[must_use]
57 pub const fn boolean(value: bool) -> Self {
58 Self(PublicValue::Bool(value))
59 }
60
61 #[must_use]
63 pub const fn date(value: Date) -> Self {
64 Self(PublicValue::Date(value))
65 }
66
67 #[must_use]
69 pub const fn decimal(value: Decimal) -> Self {
70 Self(PublicValue::Decimal(value))
71 }
72
73 #[must_use]
75 pub const fn duration(value: Duration) -> Self {
76 Self(PublicValue::Duration(value))
77 }
78
79 #[must_use]
81 pub const fn float32(value: Float32) -> Self {
82 Self(PublicValue::Float32(value))
83 }
84
85 #[must_use]
87 pub const fn float64(value: Float64) -> Self {
88 Self(PublicValue::Float64(value))
89 }
90
91 #[must_use]
93 pub const fn int64(value: i64) -> Self {
94 Self(PublicValue::Int64(value))
95 }
96
97 #[must_use]
99 pub const fn int128(value: i128) -> Self {
100 Self(PublicValue::Int128(value))
101 }
102
103 #[must_use]
105 pub const fn int_big(value: IntBig) -> Self {
106 Self(PublicValue::IntBig(value))
107 }
108
109 #[must_use]
114 pub fn list(values: Vec<Self>) -> Self {
115 Self(PublicValue::List(
116 values.into_iter().map(Self::into_public).collect(),
117 ))
118 }
119
120 #[must_use]
125 pub fn map(entries: Vec<(Self, Self)>) -> Self {
126 Self(PublicValue::Map(
127 entries
128 .into_iter()
129 .map(|(key, value)| (key.into_public(), value.into_public()))
130 .collect(),
131 ))
132 }
133
134 #[must_use]
136 pub const fn null() -> Self {
137 Self(PublicValue::Null)
138 }
139
140 #[must_use]
142 pub const fn is_null(&self) -> bool {
143 matches!(self.0, PublicValue::Null)
144 }
145
146 #[must_use]
148 pub const fn principal(value: Principal) -> Self {
149 Self(PublicValue::Principal(value))
150 }
151
152 #[must_use]
154 pub const fn subaccount(value: Subaccount) -> Self {
155 Self(PublicValue::Subaccount(value))
156 }
157
158 #[must_use]
160 pub const fn text(value: String) -> Self {
161 Self(PublicValue::Text(value))
162 }
163
164 #[must_use]
166 pub const fn timestamp(value: Timestamp) -> Self {
167 Self(PublicValue::Timestamp(value))
168 }
169
170 #[must_use]
172 pub const fn nat64(value: u64) -> Self {
173 Self(PublicValue::Nat64(value))
174 }
175
176 #[must_use]
178 pub const fn nat128(value: u128) -> Self {
179 Self(PublicValue::Nat128(value))
180 }
181
182 #[must_use]
184 pub const fn nat_big(value: NatBig) -> Self {
185 Self(PublicValue::NatBig(value))
186 }
187
188 #[must_use]
190 pub const fn ulid(value: Ulid) -> Self {
191 Self(PublicValue::Ulid(value))
192 }
193
194 #[must_use]
196 pub const fn unit() -> Self {
197 Self(PublicValue::Unit)
198 }
199
200 #[must_use]
202 pub const fn u256(value: U256) -> Self {
203 Self(PublicValue::U256(value))
204 }
205
206 #[must_use]
208 pub fn enum_value(variant: &str, path: Option<&str>) -> Self {
209 Self(PublicValue::Enum(PublicEnumValue::new(variant, path)))
210 }
211
212 #[must_use]
214 pub fn loose_enum(variant: impl Into<String>) -> Self {
215 Self(PublicValue::Enum(PublicEnumValue::loose(variant)))
216 }
217
218 #[must_use]
220 pub fn with_enum_payload(self, payload: Self) -> Option<Self> {
221 let PublicValue::Enum(value) = self.0 else {
222 return None;
223 };
224 Some(Self(PublicValue::Enum(value.with_payload(payload.0))))
225 }
226
227 pub(crate) fn try_into_runtime_non_enum(self) -> Option<Value> {
232 self.0.try_into_runtime_non_enum()
233 }
234
235 pub(crate) fn try_from_runtime_non_enum(value: &Value) -> Option<Self> {
237 PublicValue::try_from_runtime_non_enum(value).map(Self)
238 }
239}
240
241impl CandidType for InputValue {
242 fn ty() -> candid::types::Type {
243 PublicValue::ty()
244 }
245
246 fn _ty() -> candid::types::Type {
247 PublicValue::_ty()
248 }
249
250 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
251 where
252 S: Serializer,
253 {
254 self.0.idl_serialize(serializer)
255 }
256}
257
258impl<'de> Deserialize<'de> for InputValue {
259 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
260 where
261 D: Deserializer<'de>,
262 {
263 PublicValue::deserialize(deserializer).map(Self)
264 }
265}
266
267impl From<PublicValue> for InputValue {
268 fn from(value: PublicValue) -> Self {
269 Self(value)
270 }
271}
272
273#[cfg(test)]
274impl From<Value> for InputValue {
275 fn from(value: Value) -> Self {
276 Self::try_from_runtime_non_enum(&value)
277 .expect("test runtime-to-input conversion must not contain canonical enum IDs")
278 }
279}
280
281#[cfg(test)]
282impl From<&Value> for InputValue {
283 fn from(value: &Value) -> Self {
284 Self::try_from_runtime_non_enum(value)
285 .expect("test runtime-to-input conversion must not contain canonical enum IDs")
286 }
287}
288
289impl From<&str> for InputValue {
290 fn from(value: &str) -> Self {
291 Self(PublicValue::Text(value.to_string()))
292 }
293}
294
295impl From<String> for InputValue {
296 fn from(value: String) -> Self {
297 Self(PublicValue::Text(value))
298 }
299}
300
301impl From<Vec<u8>> for InputValue {
302 fn from(value: Vec<u8>) -> Self {
303 Self(PublicValue::Blob(value))
304 }
305}
306
307impl From<Blob> for InputValue {
308 fn from(value: Blob) -> Self {
309 Self(PublicValue::Blob(value.to_vec()))
310 }
311}
312
313impl From<bool> for InputValue {
314 fn from(value: bool) -> Self {
315 Self(PublicValue::Bool(value))
316 }
317}
318
319impl From<Account> for InputValue {
320 fn from(value: Account) -> Self {
321 Self(PublicValue::Account(value))
322 }
323}
324
325impl From<Date> for InputValue {
326 fn from(value: Date) -> Self {
327 Self(PublicValue::Date(value))
328 }
329}
330
331impl From<Decimal> for InputValue {
332 fn from(value: Decimal) -> Self {
333 Self(PublicValue::Decimal(value))
334 }
335}
336
337impl From<Duration> for InputValue {
338 fn from(value: Duration) -> Self {
339 Self(PublicValue::Duration(value))
340 }
341}
342
343impl From<Float32> for InputValue {
344 fn from(value: Float32) -> Self {
345 Self(PublicValue::Float32(value))
346 }
347}
348
349impl From<Float64> for InputValue {
350 fn from(value: Float64) -> Self {
351 Self(PublicValue::Float64(value))
352 }
353}
354
355impl From<IntBig> for InputValue {
356 fn from(value: IntBig) -> Self {
357 Self(PublicValue::IntBig(value))
358 }
359}
360
361impl From<i128> for InputValue {
362 fn from(value: i128) -> Self {
363 Self(PublicValue::Int128(value))
364 }
365}
366
367impl From<NatBig> for InputValue {
368 fn from(value: NatBig) -> Self {
369 Self(PublicValue::NatBig(value))
370 }
371}
372
373impl From<u128> for InputValue {
374 fn from(value: u128) -> Self {
375 Self(PublicValue::Nat128(value))
376 }
377}
378
379impl From<Principal> for InputValue {
380 fn from(value: Principal) -> Self {
381 Self(PublicValue::Principal(value))
382 }
383}
384
385impl From<Subaccount> for InputValue {
386 fn from(value: Subaccount) -> Self {
387 Self(PublicValue::Subaccount(value))
388 }
389}
390
391impl From<Timestamp> for InputValue {
392 fn from(value: Timestamp) -> Self {
393 Self(PublicValue::Timestamp(value))
394 }
395}
396
397impl From<Ulid> for InputValue {
398 fn from(value: Ulid) -> Self {
399 Self(PublicValue::Ulid(value))
400 }
401}
402
403impl From<U256> for InputValue {
404 fn from(value: U256) -> Self {
405 Self(PublicValue::U256(value))
406 }
407}
408
409impl From<()> for InputValue {
410 fn from((): ()) -> Self {
411 Self(PublicValue::Unit)
412 }
413}
414
415impl From<Unit> for InputValue {
416 fn from(_value: Unit) -> Self {
417 Self(PublicValue::Unit)
418 }
419}
420
421impl<T> From<Option<T>> for InputValue
422where
423 T: Into<Self>,
424{
425 fn from(value: Option<T>) -> Self {
426 match value {
427 Some(value) => value.into(),
428 None => Self(PublicValue::Null),
429 }
430 }
431}
432
433impl<T> From<Box<T>> for InputValue
434where
435 T: Into<Self>,
436{
437 fn from(value: Box<T>) -> Self {
438 (*value).into()
439 }
440}
441
442impl<E> From<Id<E>> for InputValue
443where
444 E: EntityKey,
445 E::Key: Into<Self>,
446{
447 fn from(value: Id<E>) -> Self {
448 value.into_key().into()
449 }
450}
451
452impl<E> From<&Id<E>> for InputValue
453where
454 E: EntityKey,
455 E::Key: Into<Self>,
456{
457 fn from(value: &Id<E>) -> Self {
458 value.key().into()
459 }
460}
461
462macro_rules! impl_input_value_int {
463 ($($ty:ty),* $(,)?) => {
464 $(
465 impl From<$ty> for InputValue {
466 fn from(value: $ty) -> Self {
467 Self(PublicValue::Int64(i64::from(value)))
468 }
469 }
470 )*
471 };
472}
473
474macro_rules! impl_input_value_nat {
475 ($($ty:ty),* $(,)?) => {
476 $(
477 impl From<$ty> for InputValue {
478 fn from(value: $ty) -> Self {
479 Self(PublicValue::Nat64(u64::from(value)))
480 }
481 }
482 )*
483 };
484}
485
486impl_input_value_int!(i8, i16, i32, i64);
487impl_input_value_nat!(u8, u16, u32, u64);
488
489#[cfg(test)]
494mod tests {
495 use crate::value::{InputValue, Value};
496
497 #[test]
498 fn runtime_to_input_value_keeps_recursive_collection_shape() {
499 let runtime = Value::List(vec![
500 Value::Nat64(7),
501 Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
502 ]);
503
504 assert_eq!(
505 InputValue::from(runtime),
506 InputValue::list(vec![
507 InputValue::nat64(7),
508 InputValue::map(vec![(InputValue::from("x"), InputValue::boolean(true))]),
509 ]),
510 );
511 }
512
513 #[test]
514 fn unresolved_enum_input_cannot_lower_without_admission() {
515 let direct = InputValue::loose_enum("Active");
516 let nested = InputValue::list(vec![direct.clone()]);
517
518 assert_eq!(direct.try_into_runtime_non_enum(), None);
519 assert_eq!(nested.try_into_runtime_non_enum(), None);
520 }
521}