ic_dbms_api/dbms/
value.rs

1use candid::CandidType;
2use serde::{Deserialize, Serialize};
3
4use super::types;
5
6/// A generic wrapper enum to hold any DBMS value.
7#[derive(
8    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, CandidType, Serialize, Deserialize,
9)]
10pub enum Value {
11    Blob(types::Blob),
12    Boolean(types::Boolean),
13    Date(types::Date),
14    DateTime(types::DateTime),
15    Decimal(types::Decimal),
16    Int32(types::Int32),
17    Int64(types::Int64),
18    Null,
19    Principal(types::Principal),
20    Text(types::Text),
21    Uint32(types::Uint32),
22    Uint64(types::Uint64),
23    Uuid(types::Uuid),
24}
25
26// macro rules for implementing From trait for Value enum variants
27macro_rules! impl_conv_for_value {
28    ($variant:ident, $ty:ty, $name:ident) => {
29        impl From<$ty> for Value {
30            fn from(value: $ty) -> Self {
31                Value::$variant(value)
32            }
33        }
34
35        impl Value {
36            /// Attempts to extract a reference to the inner value if it matches the variant.
37            pub fn $name(&self) -> Option<&$ty> {
38                if let Value::$variant(v) = self {
39                    Some(v)
40                } else {
41                    None
42                }
43            }
44        }
45    };
46}
47
48impl_conv_for_value!(Blob, types::Blob, as_blob);
49impl_conv_for_value!(Boolean, types::Boolean, as_boolean);
50impl_conv_for_value!(Date, types::Date, as_date);
51impl_conv_for_value!(DateTime, types::DateTime, as_datetime);
52impl_conv_for_value!(Decimal, types::Decimal, as_decimal);
53impl_conv_for_value!(Int32, types::Int32, as_int32);
54impl_conv_for_value!(Int64, types::Int64, as_int64);
55impl_conv_for_value!(Principal, types::Principal, as_principal);
56impl_conv_for_value!(Text, types::Text, as_text);
57impl_conv_for_value!(Uint32, types::Uint32, as_uint32);
58impl_conv_for_value!(Uint64, types::Uint64, as_uint64);
59impl_conv_for_value!(Uuid, types::Uuid, as_uuid);
60
61impl Value {
62    /// Checks if the value is [`Value::Null`].
63    pub fn is_null(&self) -> bool {
64        matches!(self, Value::Null)
65    }
66
67    /// Returns the type name of the value as a string.
68    pub fn type_name(&self) -> &'static str {
69        match self {
70            Value::Blob(_) => "Blob",
71            Value::Boolean(_) => "Boolean",
72            Value::Date(_) => "Date",
73            Value::DateTime(_) => "DateTime",
74            Value::Decimal(_) => "Decimal",
75            Value::Int32(_) => "Int32",
76            Value::Int64(_) => "Int64",
77            Value::Null => "Null",
78            Value::Principal(_) => "Principal",
79            Value::Text(_) => "Text",
80            Value::Uint32(_) => "Uint32",
81            Value::Uint64(_) => "Uint64",
82            Value::Uuid(_) => "Uuid",
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89
90    use uuid::Uuid;
91
92    use super::*;
93
94    #[test]
95    fn test_null() {
96        let int_value: Value = types::Int32(42).into();
97        assert!(!int_value.is_null());
98
99        let null_value = Value::Null;
100        assert!(null_value.is_null());
101    }
102
103    #[test]
104    fn test_value_conversion_blob() {
105        let blob = types::Blob(vec![1, 2, 3]);
106        let value: Value = blob.clone().into();
107        assert_eq!(value.as_blob(), Some(&blob));
108    }
109
110    #[test]
111    fn test_value_conversion_boolean() {
112        let boolean = types::Boolean(true);
113        let value: Value = boolean.into();
114        assert_eq!(value.as_boolean(), Some(&boolean));
115    }
116
117    #[test]
118    fn test_value_conversion_date() {
119        let date = types::Date {
120            year: 2023,
121            month: 3,
122            day: 15,
123        }; // Example date
124        let value: Value = date.into();
125        assert_eq!(value.as_date(), Some(&date));
126    }
127
128    #[test]
129    fn test_value_conversion_datetime() {
130        let datetime = types::DateTime {
131            year: 2023,
132            month: 3,
133            day: 15,
134            hour: 12,
135            minute: 30,
136            second: 45,
137            microsecond: 123456,
138            timezone_offset_minutes: 0,
139        }; // Example datetime
140        let value: Value = datetime.into();
141        assert_eq!(value.as_datetime(), Some(&datetime));
142    }
143
144    #[test]
145    fn test_value_conversion_decimal() {
146        let decimal = types::Decimal(rust_decimal::Decimal::new(12345, 2)); // 123.45
147        let value: Value = decimal.into();
148        assert_eq!(value.as_decimal(), Some(&decimal));
149    }
150
151    #[test]
152    fn test_value_conversion_int32() {
153        let int32 = types::Int32(1234567890);
154        let value: Value = int32.into();
155        assert_eq!(value.as_int32(), Some(&int32));
156    }
157
158    #[test]
159    fn test_value_conversion_int64() {
160        let int64 = types::Int64(1234567890);
161        let value: Value = int64.into();
162        assert_eq!(value.as_int64(), Some(&int64));
163    }
164
165    #[test]
166    fn test_value_conversion_principal() {
167        let principal = types::Principal(candid::Principal::from_text("aaaaa-aa").unwrap());
168        let value: Value = principal.clone().into();
169        assert_eq!(value.as_principal(), Some(&principal));
170    }
171
172    #[test]
173    fn test_value_conversion_text() {
174        let text = types::Text("Hello, World!".to_string());
175        let value: Value = text.clone().into();
176        assert_eq!(value.as_text(), Some(&text));
177    }
178
179    #[test]
180    fn test_value_conversion_uint32() {
181        let uint32 = types::Uint32(123456);
182        let value: Value = uint32.into();
183        assert_eq!(value.as_uint32(), Some(&uint32));
184    }
185
186    #[test]
187    fn test_value_conversion_uint64() {
188        let uint64 = types::Uint64(12345678901234);
189        let value: Value = uint64.into();
190        assert_eq!(value.as_uint64(), Some(&uint64));
191    }
192
193    #[test]
194    fn test_value_conversion_uuid() {
195        let uuid = types::Uuid(
196            Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("failed to parse uuid"),
197        );
198        let value: Value = uuid.clone().into();
199        assert_eq!(value.as_uuid(), Some(&uuid));
200    }
201
202    #[test]
203    fn test_value_type_name() {
204        let int_value: Value = types::Int32(42).into();
205        assert_eq!(int_value.type_name(), "Int32");
206
207        let text_value: Value = types::Text("Hello".to_string()).into();
208        assert_eq!(text_value.type_name(), "Text");
209
210        let null_value = Value::Null;
211        assert_eq!(null_value.type_name(), "Null");
212    }
213}