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