1use std::str::FromStr;
2
3use candid::CandidType;
4use serde::{Deserialize, Serialize};
5
6use super::types;
7
8#[derive(
10 Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, CandidType, Serialize, Deserialize,
11)]
12pub enum Value {
13 Blob(types::Blob),
14 Boolean(types::Boolean),
15 Date(types::Date),
16 DateTime(types::DateTime),
17 Decimal(types::Decimal),
18 Int8(types::Int8),
19 Int16(types::Int16),
20 Int32(types::Int32),
21 Int64(types::Int64),
22 Json(types::Json),
23 Null,
24 Principal(types::Principal),
25 Text(types::Text),
26 Uint8(types::Uint8),
27 Uint16(types::Uint16),
28 Uint32(types::Uint32),
29 Uint64(types::Uint64),
30 Uuid(types::Uuid),
31}
32
33impl FromStr for Value {
34 type Err = ();
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 Ok(Self::Text(s.into()))
38 }
39}
40
41macro_rules! impl_conv_for_value {
43 ($variant:ident, $ty:ty, $name:ident, $test_name:ident) => {
44 impl From<$ty> for Value {
45 fn from(value: $ty) -> Self {
46 Value::$variant(value)
47 }
48 }
49
50 impl Value {
51 pub fn $name(&self) -> Option<&$ty> {
53 if let Value::$variant(v) = self {
54 Some(v)
55 } else {
56 None
57 }
58 }
59 }
60
61 #[cfg(test)]
62 mod $test_name {
63 use super::*;
64
65 #[test]
66 fn test_value_conversion() {
67 let value_instance: $ty = Default::default();
68 let value: Value = value_instance.clone().into();
69 assert_eq!(value.$name(), Some(&value_instance));
70 }
71 }
72 };
73}
74
75macro_rules! value_from_primitive {
76 ($variant:ident, $primitive:ty, $test_name:ident) => {
77 value_from_primitive!($variant, $primitive, $test_name, Default::default());
78 };
79
80 ($variant:ident, $primitive:ty, $test_name:ident, $default_value:expr) => {
81 impl From<$primitive> for Value {
82 fn from(value: $primitive) -> Self {
83 Value::$variant($crate::prelude::$variant(value.into()))
84 }
85 }
86
87 #[cfg(test)]
88 mod $test_name {
89 use super::*;
90
91 #[test]
92 fn test_value_from_primitive() {
93 let primitive_value: $primitive = $default_value;
94 if let Value::$variant(inner_value) = Value::from(primitive_value.clone()) {
95 assert_eq!(inner_value.0, primitive_value);
96 } else {
97 panic!("Value variant does not match");
98 }
99 }
100 }
101 };
102}
103
104impl_conv_for_value!(Blob, types::Blob, as_blob, tests_blob);
106impl_conv_for_value!(Boolean, types::Boolean, as_boolean, tests_boolean);
107impl_conv_for_value!(Date, types::Date, as_date, tests_date);
108impl_conv_for_value!(DateTime, types::DateTime, as_datetime, tests_datetime);
109impl_conv_for_value!(Decimal, types::Decimal, as_decimal, tests_decimal);
110impl_conv_for_value!(Int8, types::Int8, as_int8, tests_int8);
111impl_conv_for_value!(Int16, types::Int16, as_int16, tests_int16);
112impl_conv_for_value!(Int32, types::Int32, as_int32, tests_int32);
113impl_conv_for_value!(Int64, types::Int64, as_int64, tests_int64);
114impl_conv_for_value!(Json, types::Json, as_json, tests_json);
115impl_conv_for_value!(Principal, types::Principal, as_principal, tests_principal);
116impl_conv_for_value!(Text, types::Text, as_text, tests_text);
117impl_conv_for_value!(Uint8, types::Uint8, as_uint8, tests_uint8);
118impl_conv_for_value!(Uint16, types::Uint16, as_uint16, tests_uint16);
119impl_conv_for_value!(Uint32, types::Uint32, as_uint32, tests_uint32);
120impl_conv_for_value!(Uint64, types::Uint64, as_uint64, tests_uint64);
121impl_conv_for_value!(Uuid, types::Uuid, as_uuid, tests_uuid);
122
123value_from_primitive!(Blob, &[u8], tests_blob_primitive_slice);
125value_from_primitive!(Blob, Vec<u8>, tests_blob_primitive);
126value_from_primitive!(Boolean, bool, tests_boolean_primitive);
127value_from_primitive!(Decimal, rust_decimal::Decimal, tests_decimal_primitive);
128value_from_primitive!(Int8, i8, tests_int8_primitive);
129value_from_primitive!(Int16, i16, tests_int16_primitive);
130value_from_primitive!(Int32, i32, tests_int32_primitive);
131value_from_primitive!(Int64, i64, tests_int64_primitive);
132value_from_primitive!(Uint8, u8, tests_uint8_primitive);
133value_from_primitive!(Uint16, u16, tests_uint16_primitive);
134value_from_primitive!(Uint32, u32, tests_uint32_primitive);
135value_from_primitive!(Uint64, u64, tests_uint64_primitive);
136value_from_primitive!(
137 Principal,
138 candid::Principal,
139 tests_principal_primitive,
140 candid::Principal::anonymous()
141);
142value_from_primitive!(Text, String, tests_text_primitive_string);
143value_from_primitive!(Text, &str, tests_text_primitive_str);
144value_from_primitive!(Uuid, uuid::Uuid, tests_uuid_primitive);
145
146impl Value {
147 pub fn is_null(&self) -> bool {
149 matches!(self, Value::Null)
150 }
151
152 pub fn type_name(&self) -> &'static str {
154 match self {
155 Value::Blob(_) => "Blob",
156 Value::Boolean(_) => "Boolean",
157 Value::Date(_) => "Date",
158 Value::DateTime(_) => "DateTime",
159 Value::Decimal(_) => "Decimal",
160 Value::Int8(_) => "Int8",
161 Value::Int16(_) => "Int16",
162 Value::Int32(_) => "Int32",
163 Value::Int64(_) => "Int64",
164 Value::Json(_) => "Json",
165 Value::Null => "Null",
166 Value::Principal(_) => "Principal",
167 Value::Text(_) => "Text",
168 Value::Uint8(_) => "Uint8",
169 Value::Uint16(_) => "Uint16",
170 Value::Uint32(_) => "Uint32",
171 Value::Uint64(_) => "Uint64",
172 Value::Uuid(_) => "Uuid",
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179
180 use uuid::Uuid;
181
182 use super::*;
183
184 #[test]
185 fn test_null() {
186 let int_value: Value = types::Int32(42).into();
187 assert!(!int_value.is_null());
188
189 let null_value = Value::Null;
190 assert!(null_value.is_null());
191 }
192
193 #[test]
194 fn test_value_conversion_blob() {
195 let blob = types::Blob(vec![1, 2, 3]);
196 let value: Value = blob.clone().into();
197 assert_eq!(value.as_blob(), Some(&blob));
198 }
199
200 #[test]
201 fn test_value_conversion_boolean() {
202 let boolean = types::Boolean(true);
203 let value: Value = boolean.into();
204 assert_eq!(value.as_boolean(), Some(&boolean));
205 }
206
207 #[test]
208 fn test_value_conversion_date() {
209 let date = types::Date {
210 year: 2023,
211 month: 3,
212 day: 15,
213 }; let value: Value = date.into();
215 assert_eq!(value.as_date(), Some(&date));
216 }
217
218 #[test]
219 fn test_value_conversion_datetime() {
220 let datetime = types::DateTime {
221 year: 2023,
222 month: 3,
223 day: 15,
224 hour: 12,
225 minute: 30,
226 second: 45,
227 microsecond: 123456,
228 timezone_offset_minutes: 0,
229 }; let value: Value = datetime.into();
231 assert_eq!(value.as_datetime(), Some(&datetime));
232 }
233
234 #[test]
235 fn test_value_conversion_decimal() {
236 let decimal = types::Decimal(rust_decimal::Decimal::new(12345, 2)); let value: Value = decimal.into();
238 assert_eq!(value.as_decimal(), Some(&decimal));
239 }
240
241 #[test]
242 fn test_value_conversion_int32() {
243 let int32 = types::Int32(1234567890);
244 let value: Value = int32.into();
245 assert_eq!(value.as_int32(), Some(&int32));
246 }
247
248 #[test]
249 fn test_value_conversion_int64() {
250 let int64 = types::Int64(1234567890);
251 let value: Value = int64.into();
252 assert_eq!(value.as_int64(), Some(&int64));
253 }
254
255 #[test]
256 fn test_value_conversion_principal() {
257 let principal = types::Principal(candid::Principal::from_text("aaaaa-aa").unwrap());
258 let value: Value = principal.clone().into();
259 assert_eq!(value.as_principal(), Some(&principal));
260 }
261
262 #[test]
263 fn test_value_conversion_text() {
264 let text = types::Text("Hello, World!".to_string());
265 let value: Value = text.clone().into();
266 assert_eq!(value.as_text(), Some(&text));
267 }
268
269 #[test]
270 fn test_value_conversion_uint32() {
271 let uint32 = types::Uint32(123456);
272 let value: Value = uint32.into();
273 assert_eq!(value.as_uint32(), Some(&uint32));
274 }
275
276 #[test]
277 fn test_value_conversion_uint64() {
278 let uint64 = types::Uint64(12345678901234);
279 let value: Value = uint64.into();
280 assert_eq!(value.as_uint64(), Some(&uint64));
281 }
282
283 #[test]
284 fn test_value_conversion_uuid() {
285 let uuid = types::Uuid(
286 Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("failed to parse uuid"),
287 );
288 let value: Value = uuid.clone().into();
289 assert_eq!(value.as_uuid(), Some(&uuid));
290 }
291
292 #[test]
293 fn test_value_type_name() {
294 let int_value: Value = types::Int32(42).into();
295 assert_eq!(int_value.type_name(), "Int32");
296
297 let text_value: Value = types::Text("Hello".to_string()).into();
298 assert_eq!(text_value.type_name(), "Text");
299
300 let null_value = Value::Null;
301 assert_eq!(null_value.type_name(), "Null");
302 }
303
304 #[test]
305 fn test_value_from_str() {
306 let str_value = "Hello, DBMS!";
307
308 let value = Value::from_str(str_value).unwrap();
309 assert_eq!(value.as_text().unwrap().0, str_value);
310 }
311}