Skip to main content

icydb_core/value/
output.rs

1use crate::{
2    types::{
3        Account, Date, Decimal, Duration, Float32, Float64, Principal, Subaccount, Timestamp, U256,
4        Ulid,
5    },
6    value::PublicValue,
7};
8
9use serde::{Deserialize, Deserializer};
10
11#[cfg(test)]
12use crate::value::Value;
13
14/// Public output-side root value boundary.
15///
16/// Recursive data is owned by `PublicValue`; this sealed wrapper keeps
17/// accepted output distinct from caller-authored `InputValue`.
18#[repr(transparent)]
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct OutputValue(PublicValue);
21
22impl OutputValue {
23    impl_public_value_scalar_constructors!("output");
24
25    /// Borrow the canonical recursive public value.
26    #[must_use]
27    pub const fn as_public(&self) -> &PublicValue {
28        &self.0
29    }
30
31    /// Consume this boundary wrapper without conversion.
32    #[must_use]
33    pub fn into_public(self) -> PublicValue {
34        self.0
35    }
36
37    pub(crate) const fn from_public(value: PublicValue) -> Self {
38        Self(value)
39    }
40
41    /// Build a recursive list output value.
42    #[must_use]
43    pub const fn list(values: Vec<PublicValue>) -> Self {
44        Self(PublicValue::List(values))
45    }
46
47    /// Build a recursive map output value.
48    #[must_use]
49    pub const fn map(entries: Vec<(PublicValue, PublicValue)>) -> Self {
50        Self(PublicValue::Map(entries))
51    }
52}
53
54impl<'de> Deserialize<'de> for OutputValue {
55    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56    where
57        D: Deserializer<'de>,
58    {
59        PublicValue::deserialize(deserializer).map(Self)
60    }
61}
62
63#[cfg(test)]
64impl From<Value> for OutputValue {
65    fn from(value: Value) -> Self {
66        Self(
67            PublicValue::try_from_runtime_non_enum(&value)
68                .expect("test output conversion requires accepted enum catalog"),
69        )
70    }
71}
72
73/// Render one accepted output value into the stable row-projection text form.
74#[must_use]
75pub fn render_output_value_text(value: &OutputValue) -> String {
76    value.as_public().render_text()
77}
78
79impl serde::Serialize for OutputValue {
80    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
81        serde::Serialize::serialize(&self.0, serializer)
82    }
83}
84
85// OutputValue delegates both Serde directions to the canonical recursive value.
86#[cfg(not(target_family = "wasm"))]
87impl ic_wire::typescript::Wire for OutputValue {
88    fn native_schema(
89        context: &mut ic_wire::typescript::SchemaContext,
90    ) -> Result<serde_json::Value, ic_wire::typescript::GenerationError> {
91        context.describe::<PublicValue>()
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use crate::value::{OutputValue, PublicValue, Value};
98
99    #[test]
100    fn output_value_from_runtime_value_keeps_recursive_collection_shape() {
101        let runtime = Value::List(vec![
102            Value::Nat64(7),
103            Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
104        ]);
105
106        assert_eq!(
107            OutputValue::from(runtime),
108            OutputValue::list(vec![
109                PublicValue::Nat64(7),
110                PublicValue::Map(vec![(
111                    PublicValue::Text("x".to_string()),
112                    PublicValue::Bool(true),
113                )]),
114            ]),
115        );
116    }
117}