1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use crate::datetime::{NdbDateTime, NdbDuration};
11use crate::geometry::Geometry;
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub enum Value {
17 Null,
19 Bool(bool),
21 Integer(i64),
23 Float(f64),
25 String(String),
27 Bytes(Vec<u8>),
29 Array(Vec<Value>),
31 Object(HashMap<String, Value>),
33 Uuid(String),
35 Ulid(String),
37 DateTime(NdbDateTime),
39 Duration(NdbDuration),
41 Decimal(rust_decimal::Decimal),
43 Geometry(Geometry),
45 Set(Vec<Value>),
47 Regex(String),
49 Range {
51 start: Option<Box<Value>>,
53 end: Option<Box<Value>>,
55 inclusive: bool,
57 },
58 Record {
60 table: String,
62 id: String,
64 },
65}
66
67impl Value {
68 pub fn is_null(&self) -> bool {
70 matches!(self, Value::Null)
71 }
72
73 pub fn as_str(&self) -> Option<&str> {
75 match self {
76 Value::String(s) => Some(s),
77 _ => None,
78 }
79 }
80
81 pub fn as_i64(&self) -> Option<i64> {
83 match self {
84 Value::Integer(i) => Some(*i),
85 _ => None,
86 }
87 }
88
89 pub fn as_f64(&self) -> Option<f64> {
91 match self {
92 Value::Float(f) => Some(*f),
93 Value::Integer(i) => Some(*i as f64),
94 _ => None,
95 }
96 }
97
98 pub fn as_bool(&self) -> Option<bool> {
100 match self {
101 Value::Bool(b) => Some(*b),
102 _ => None,
103 }
104 }
105
106 pub fn as_bytes(&self) -> Option<&[u8]> {
108 match self {
109 Value::Bytes(b) => Some(b),
110 _ => None,
111 }
112 }
113
114 pub fn as_uuid(&self) -> Option<&str> {
116 match self {
117 Value::Uuid(s) => Some(s),
118 _ => None,
119 }
120 }
121
122 pub fn as_ulid(&self) -> Option<&str> {
124 match self {
125 Value::Ulid(s) => Some(s),
126 _ => None,
127 }
128 }
129
130 pub fn as_datetime(&self) -> Option<&NdbDateTime> {
132 match self {
133 Value::DateTime(dt) => Some(dt),
134 _ => None,
135 }
136 }
137
138 pub fn as_duration(&self) -> Option<&NdbDuration> {
140 match self {
141 Value::Duration(d) => Some(d),
142 _ => None,
143 }
144 }
145
146 pub fn as_decimal(&self) -> Option<&rust_decimal::Decimal> {
148 match self {
149 Value::Decimal(d) => Some(d),
150 _ => None,
151 }
152 }
153
154 pub fn as_geometry(&self) -> Option<&Geometry> {
156 match self {
157 Value::Geometry(g) => Some(g),
158 _ => None,
159 }
160 }
161
162 pub fn as_set(&self) -> Option<&[Value]> {
164 match self {
165 Value::Set(s) => Some(s),
166 _ => None,
167 }
168 }
169
170 pub fn as_regex(&self) -> Option<&str> {
172 match self {
173 Value::Regex(r) => Some(r),
174 _ => None,
175 }
176 }
177
178 pub fn as_record(&self) -> Option<(&str, &str)> {
180 match self {
181 Value::Record { table, id } => Some((table, id)),
182 _ => None,
183 }
184 }
185
186 pub fn type_name(&self) -> &'static str {
188 match self {
189 Value::Null => "null",
190 Value::Bool(_) => "bool",
191 Value::Integer(_) => "int",
192 Value::Float(_) => "float",
193 Value::String(_) => "string",
194 Value::Bytes(_) => "bytes",
195 Value::Array(_) => "array",
196 Value::Object(_) => "object",
197 Value::Uuid(_) => "uuid",
198 Value::Ulid(_) => "ulid",
199 Value::DateTime(_) => "datetime",
200 Value::Duration(_) => "duration",
201 Value::Decimal(_) => "decimal",
202 Value::Geometry(_) => "geometry",
203 Value::Set(_) => "set",
204 Value::Regex(_) => "regex",
205 Value::Range { .. } => "range",
206 Value::Record { .. } => "record",
207 }
208 }
209}
210
211impl From<&str> for Value {
213 fn from(s: &str) -> Self {
214 Value::String(s.to_owned())
215 }
216}
217
218impl From<String> for Value {
219 fn from(s: String) -> Self {
220 Value::String(s)
221 }
222}
223
224impl From<i64> for Value {
225 fn from(i: i64) -> Self {
226 Value::Integer(i)
227 }
228}
229
230impl From<f64> for Value {
231 fn from(f: f64) -> Self {
232 Value::Float(f)
233 }
234}
235
236impl From<bool> for Value {
237 fn from(b: bool) -> Self {
238 Value::Bool(b)
239 }
240}
241
242impl From<Vec<u8>> for Value {
243 fn from(b: Vec<u8>) -> Self {
244 Value::Bytes(b)
245 }
246}
247
248impl From<NdbDateTime> for Value {
249 fn from(dt: NdbDateTime) -> Self {
250 Value::DateTime(dt)
251 }
252}
253
254impl From<NdbDuration> for Value {
255 fn from(d: NdbDuration) -> Self {
256 Value::Duration(d)
257 }
258}
259
260impl From<rust_decimal::Decimal> for Value {
261 fn from(d: rust_decimal::Decimal) -> Self {
262 Value::Decimal(d)
263 }
264}
265
266impl From<Geometry> for Value {
267 fn from(g: Geometry) -> Self {
268 Value::Geometry(g)
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn value_type_checks() {
278 assert!(Value::Null.is_null());
279 assert!(!Value::Bool(true).is_null());
280
281 assert_eq!(Value::String("hi".into()).as_str(), Some("hi"));
282 assert_eq!(Value::Integer(42).as_i64(), Some(42));
283 assert_eq!(Value::Float(2.78).as_f64(), Some(2.78));
284 assert_eq!(Value::Integer(10).as_f64(), Some(10.0));
285 assert_eq!(Value::Bool(true).as_bool(), Some(true));
286 assert_eq!(Value::Bytes(vec![1, 2]).as_bytes(), Some(&[1, 2][..]));
287 }
288
289 #[test]
290 fn from_conversions() {
291 let s: Value = "hello".into();
292 assert_eq!(s.as_str(), Some("hello"));
293
294 let i: Value = 42i64.into();
295 assert_eq!(i.as_i64(), Some(42));
296
297 let f: Value = 2.78f64.into();
298 assert_eq!(f.as_f64(), Some(2.78));
299 }
300
301 #[test]
302 fn nested_value() {
303 let nested = Value::Object({
304 let mut m = HashMap::new();
305 m.insert(
306 "inner".into(),
307 Value::Array(vec![Value::Integer(1), Value::Integer(2)]),
308 );
309 m
310 });
311 assert!(!nested.is_null());
312 }
313}