Skip to main content

icydb_core/value/
output.rs

1use crate::{
2    types::{
3        Account, Date, Decimal, Duration, Float32, Float64, IntBig, NatBig, Principal, Subaccount,
4        Timestamp, U256, Ulid,
5    },
6    value::PublicValue,
7};
8use candid::{CandidType, types::Serializer};
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    /// Borrow the canonical recursive public value.
24    #[must_use]
25    pub const fn as_public(&self) -> &PublicValue {
26        &self.0
27    }
28
29    /// Consume this boundary wrapper without conversion.
30    #[must_use]
31    pub fn into_public(self) -> PublicValue {
32        self.0
33    }
34
35    pub(crate) const fn from_public(value: PublicValue) -> Self {
36        Self(value)
37    }
38
39    /// Build an account output value.
40    #[must_use]
41    pub const fn account(value: Account) -> Self {
42        Self(PublicValue::Account(value))
43    }
44
45    /// Build a blob output value.
46    #[must_use]
47    pub const fn blob(value: Vec<u8>) -> Self {
48        Self(PublicValue::Blob(value))
49    }
50
51    /// Build a boolean output value.
52    #[must_use]
53    pub const fn boolean(value: bool) -> Self {
54        Self(PublicValue::Bool(value))
55    }
56
57    /// Build a date output value.
58    #[must_use]
59    pub const fn date(value: Date) -> Self {
60        Self(PublicValue::Date(value))
61    }
62
63    /// Build a decimal output value.
64    #[must_use]
65    pub const fn decimal(value: Decimal) -> Self {
66        Self(PublicValue::Decimal(value))
67    }
68
69    /// Build a duration output value.
70    #[must_use]
71    pub const fn duration(value: Duration) -> Self {
72        Self(PublicValue::Duration(value))
73    }
74
75    /// Build a finite 32-bit floating-point output value.
76    #[must_use]
77    pub const fn float32(value: Float32) -> Self {
78        Self(PublicValue::Float32(value))
79    }
80
81    /// Build a finite 64-bit floating-point output value.
82    #[must_use]
83    pub const fn float64(value: Float64) -> Self {
84        Self(PublicValue::Float64(value))
85    }
86
87    /// Build a 64-bit signed integer output value.
88    #[must_use]
89    pub const fn int64(value: i64) -> Self {
90        Self(PublicValue::Int64(value))
91    }
92
93    /// Build a 128-bit signed integer output value.
94    #[must_use]
95    pub const fn int128(value: i128) -> Self {
96        Self(PublicValue::Int128(value))
97    }
98
99    /// Build an unbounded signed integer output value.
100    #[must_use]
101    pub const fn int_big(value: IntBig) -> Self {
102        Self(PublicValue::IntBig(value))
103    }
104
105    /// Build a recursive list output value.
106    #[must_use]
107    pub const fn list(values: Vec<PublicValue>) -> Self {
108        Self(PublicValue::List(values))
109    }
110
111    /// Build a recursive map output value.
112    #[must_use]
113    pub const fn map(entries: Vec<(PublicValue, PublicValue)>) -> Self {
114        Self(PublicValue::Map(entries))
115    }
116
117    /// Build a null output value.
118    #[must_use]
119    pub const fn null() -> Self {
120        Self(PublicValue::Null)
121    }
122
123    /// Build a principal output value.
124    #[must_use]
125    pub const fn principal(value: Principal) -> Self {
126        Self(PublicValue::Principal(value))
127    }
128
129    /// Build a fixed subaccount output value.
130    #[must_use]
131    pub const fn subaccount(value: Subaccount) -> Self {
132        Self(PublicValue::Subaccount(value))
133    }
134
135    /// Build a text output value.
136    #[must_use]
137    pub const fn text(value: String) -> Self {
138        Self(PublicValue::Text(value))
139    }
140
141    /// Build a timestamp output value.
142    #[must_use]
143    pub const fn timestamp(value: Timestamp) -> Self {
144        Self(PublicValue::Timestamp(value))
145    }
146
147    /// Build a 64-bit natural output value.
148    #[must_use]
149    pub const fn nat64(value: u64) -> Self {
150        Self(PublicValue::Nat64(value))
151    }
152
153    /// Build a 128-bit natural output value.
154    #[must_use]
155    pub const fn nat128(value: u128) -> Self {
156        Self(PublicValue::Nat128(value))
157    }
158
159    /// Build an unbounded natural output value.
160    #[must_use]
161    pub const fn nat_big(value: NatBig) -> Self {
162        Self(PublicValue::NatBig(value))
163    }
164
165    /// Build a ULID output value.
166    #[must_use]
167    pub const fn ulid(value: Ulid) -> Self {
168        Self(PublicValue::Ulid(value))
169    }
170
171    /// Build a unit output value.
172    #[must_use]
173    pub const fn unit() -> Self {
174        Self(PublicValue::Unit)
175    }
176
177    /// Build a fixed-width unsigned integer output value.
178    #[must_use]
179    pub const fn u256(value: U256) -> Self {
180        Self(PublicValue::U256(value))
181    }
182}
183
184impl CandidType for OutputValue {
185    fn ty() -> candid::types::Type {
186        PublicValue::ty()
187    }
188
189    fn _ty() -> candid::types::Type {
190        PublicValue::_ty()
191    }
192
193    fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
194    where
195        S: Serializer,
196    {
197        self.0.idl_serialize(serializer)
198    }
199}
200
201impl<'de> Deserialize<'de> for OutputValue {
202    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203    where
204        D: Deserializer<'de>,
205    {
206        PublicValue::deserialize(deserializer).map(Self)
207    }
208}
209
210#[cfg(test)]
211impl From<Value> for OutputValue {
212    fn from(value: Value) -> Self {
213        Self(
214            PublicValue::try_from_runtime_non_enum(&value)
215                .expect("test output conversion requires accepted enum catalog"),
216        )
217    }
218}
219
220/// Render one accepted output value into the stable row-projection text form.
221#[must_use]
222pub fn render_output_value_text(value: &OutputValue) -> String {
223    value.as_public().render_text()
224}
225
226#[cfg(test)]
227mod tests {
228    use crate::value::{OutputValue, PublicValue, Value};
229
230    #[test]
231    fn output_value_from_runtime_value_keeps_recursive_collection_shape() {
232        let runtime = Value::List(vec![
233            Value::Nat64(7),
234            Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
235        ]);
236
237        assert_eq!(
238            OutputValue::from(runtime),
239            OutputValue::list(vec![
240                PublicValue::Nat64(7),
241                PublicValue::Map(vec![(
242                    PublicValue::Text("x".to_string()),
243                    PublicValue::Bool(true),
244                )]),
245            ]),
246        );
247    }
248}