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)]
21pub enum OutputValue {
22 Account(Account),
23 Blob(Vec<u8>),
24 Bool(bool),
25 Date(Date),
26 Decimal(Decimal),
27 Duration(Duration),
28 Enum(OutputValueEnum),
29 Float32(Float32),
30 Float64(Float64),
31 Int(i64),
32 Int128(Int128),
33 IntBig(Int),
34 List(Vec<Self>),
35 Map(Vec<(Self, Self)>),
36 Null,
37 Principal(Principal),
38 Subaccount(Subaccount),
39 Text(String),
40 Timestamp(Timestamp),
41 Uint(u64),
42 Uint128(Nat128),
43 UintBig(Nat),
44 Ulid(Ulid),
45 Unit,
46}
47
48#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
58pub struct OutputValueEnum {
59 variant: String,
60 path: Option<String>,
61 payload: Option<Box<OutputValue>>,
62}
63
64impl OutputValueEnum {
65 #[must_use]
66 pub const fn variant(&self) -> &str {
67 self.variant.as_str()
68 }
69
70 #[must_use]
71 pub fn path(&self) -> Option<&str> {
72 self.path.as_deref()
73 }
74
75 #[must_use]
76 pub fn payload(&self) -> Option<&OutputValue> {
77 self.payload.as_deref()
78 }
79}
80
81impl From<Value> for OutputValue {
82 fn from(value: Value) -> Self {
83 Self::from(&value)
84 }
85}
86
87impl From<&Value> for OutputValue {
88 fn from(value: &Value) -> Self {
89 match value {
90 Value::Account(value) => Self::Account(*value),
91 Value::Blob(value) => Self::Blob(value.clone()),
92 Value::Bool(value) => Self::Bool(*value),
93 Value::Date(value) => Self::Date(*value),
94 Value::Decimal(value) => Self::Decimal(*value),
95 Value::Duration(value) => Self::Duration(*value),
96 Value::Enum(value) => Self::Enum(OutputValueEnum::from(value)),
97 Value::Float32(value) => Self::Float32(*value),
98 Value::Float64(value) => Self::Float64(*value),
99 Value::Int(value) => Self::Int(*value),
100 Value::Int128(value) => Self::Int128(*value),
101 Value::IntBig(value) => Self::IntBig(value.clone()),
102 Value::List(items) => Self::List(items.iter().map(Self::from).collect()),
103 Value::Map(entries) => Self::Map(
104 entries
105 .iter()
106 .map(|(key, value)| (Self::from(key), Self::from(value)))
107 .collect(),
108 ),
109 Value::Null => Self::Null,
110 Value::Principal(value) => Self::Principal(*value),
111 Value::Subaccount(value) => Self::Subaccount(*value),
112 Value::Text(value) => Self::Text(value.clone()),
113 Value::Timestamp(value) => Self::Timestamp(*value),
114 Value::Uint(value) => Self::Uint(*value),
115 Value::Uint128(value) => Self::Uint128(*value),
116 Value::UintBig(value) => Self::UintBig(value.clone()),
117 Value::Ulid(value) => Self::Ulid(*value),
118 Value::Unit => Self::Unit,
119 }
120 }
121}
122
123impl From<ValueEnum> for OutputValueEnum {
124 fn from(value: ValueEnum) -> Self {
125 Self::from(&value)
126 }
127}
128
129impl From<&ValueEnum> for OutputValueEnum {
130 fn from(value: &ValueEnum) -> Self {
131 Self {
132 variant: value.variant().to_string(),
133 path: value.path().map(ToString::to_string),
134 payload: value
135 .payload()
136 .map(|payload| Box::new(OutputValue::from(payload))),
137 }
138 }
139}
140
141#[cfg(test)]
146mod tests {
147 use crate::value::{OutputValue, OutputValueEnum, Value, ValueEnum};
148
149 #[test]
150 fn output_value_from_runtime_value_keeps_recursive_collection_shape() {
151 let runtime = Value::List(vec![
152 Value::Uint(7),
153 Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
154 ]);
155
156 assert_eq!(
157 OutputValue::from(runtime),
158 OutputValue::List(vec![
159 OutputValue::Uint(7),
160 OutputValue::Map(vec![(
161 OutputValue::Text("x".to_string()),
162 OutputValue::Bool(true),
163 )]),
164 ]),
165 );
166 }
167
168 #[test]
169 fn output_value_enum_from_runtime_enum_keeps_payload() {
170 let runtime =
171 ValueEnum::new("Example", Some("test::OutputEnum")).with_payload(Value::Uint(9));
172
173 assert_eq!(
174 OutputValueEnum::from(runtime),
175 OutputValueEnum {
176 variant: "Example".to_string(),
177 path: Some("test::OutputEnum".to_string()),
178 payload: Some(Box::new(OutputValue::Uint(9))),
179 },
180 );
181 }
182}