ic_dbms_api/dbms/
value.rs

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