Skip to main content

hawdb_core/
value.rs

1// Copyright 2026 Nowledge
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::cmp::Ordering;
16use std::collections::BTreeMap;
17use std::fmt::{Display, Formatter};
18use std::hash::{Hash, Hasher};
19use uuid::Uuid;
20
21use crate::{LogicalType, Vector, VectorRef};
22
23#[derive(Debug, Clone)]
24pub enum Value {
25    Null,
26    Bool(bool),
27    Int(i64),
28    Float(f64),
29    String(String),
30    Binary(Vec<u8>),
31    Vector(Vector),
32    Uuid(Uuid),
33    List(Vec<Value>),
34    Map(BTreeMap<String, Value>),
35}
36
37/// An allocation-free borrowed view of a [`Value`].
38///
39/// Scalar values are copied, while variable-width payloads retain references
40/// to their owner. Use [`ValueRef::to_owned_value`] only at an ownership
41/// boundary such as final result materialization.
42#[derive(Debug, Clone, Copy)]
43pub enum ValueRef<'a> {
44    Null,
45    Bool(bool),
46    Int(i64),
47    Float(f64),
48    String(&'a str),
49    Binary(&'a [u8]),
50    Vector(VectorRef<'a>),
51    Uuid(Uuid),
52    List(&'a [Value]),
53    Map(&'a BTreeMap<String, Value>),
54}
55
56impl PartialEq for Value {
57    fn eq(&self, other: &Self) -> bool {
58        self.cmp(other) == Ordering::Equal
59    }
60}
61
62impl Eq for Value {}
63
64impl PartialOrd for Value {
65    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
66        Some(self.cmp(other))
67    }
68}
69
70impl Ord for Value {
71    fn cmp(&self, other: &Self) -> Ordering {
72        self.kind_rank()
73            .cmp(&other.kind_rank())
74            .then_with(|| match (self, other) {
75                (Value::Null, Value::Null) => Ordering::Equal,
76                (Value::Bool(left), Value::Bool(right)) => left.cmp(right),
77                (Value::Int(left), Value::Int(right)) => left.cmp(right),
78                (Value::Float(left), Value::Float(right)) => left.total_cmp(right),
79                (Value::String(left), Value::String(right)) => left.cmp(right),
80                (Value::Binary(left), Value::Binary(right)) => left.cmp(right),
81                (Value::Vector(left), Value::Vector(right)) => left.cmp(right),
82                (Value::Uuid(left), Value::Uuid(right)) => left.cmp(right),
83                (Value::List(left), Value::List(right)) => left.cmp(right),
84                (Value::Map(left), Value::Map(right)) => left.cmp(right),
85                _ => Ordering::Equal,
86            })
87    }
88}
89
90impl Hash for Value {
91    fn hash<H: Hasher>(&self, state: &mut H) {
92        self.kind_rank().hash(state);
93        match self {
94            Value::Null => {}
95            Value::Bool(value) => value.hash(state),
96            Value::Int(value) => value.hash(state),
97            Value::Float(value) => value.to_bits().hash(state),
98            Value::String(value) => value.hash(state),
99            Value::Binary(value) => value.hash(state),
100            Value::Vector(value) => value.hash(state),
101            Value::Uuid(value) => value.hash(state),
102            Value::List(values) => values.hash(state),
103            Value::Map(values) => values.hash(state),
104        }
105    }
106}
107
108impl Value {
109    pub fn as_ref(&self) -> ValueRef<'_> {
110        self.into()
111    }
112
113    pub fn logical_type(&self) -> Option<LogicalType> {
114        self.as_ref().logical_type()
115    }
116
117    fn kind_rank(&self) -> u8 {
118        match self {
119            Value::Null => 0,
120            Value::Bool(_) => 1,
121            Value::Int(_) => 2,
122            Value::Float(_) => 3,
123            Value::String(_) => 4,
124            Value::Binary(_) => 5,
125            Value::Vector(_) => 9,
126            Value::Uuid(_) => 6,
127            Value::List(_) => 7,
128            Value::Map(_) => 8,
129        }
130    }
131}
132
133impl<'a> ValueRef<'a> {
134    pub const fn is_null(self) -> bool {
135        matches!(self, Self::Null)
136    }
137
138    pub const fn logical_type(self) -> Option<LogicalType> {
139        match self {
140            Self::Null => None,
141            Self::Bool(_) => Some(LogicalType::Boolean),
142            Self::Int(_) => Some(LogicalType::Int64),
143            Self::Float(_) => Some(LogicalType::Float64),
144            Self::String(_) => Some(LogicalType::String),
145            Self::Binary(_) => Some(LogicalType::Binary),
146            Self::Vector(value) => Some(LogicalType::Vector(value.dimension())),
147            Self::Uuid(_) => Some(LogicalType::Uuid),
148            Self::List(_) => Some(LogicalType::List),
149            Self::Map(_) => Some(LogicalType::Map),
150        }
151    }
152
153    pub const fn as_bool(self) -> Option<bool> {
154        match self {
155            Self::Bool(value) => Some(value),
156            _ => None,
157        }
158    }
159
160    pub const fn as_i64(self) -> Option<i64> {
161        match self {
162            Self::Int(value) => Some(value),
163            _ => None,
164        }
165    }
166
167    pub const fn as_f64(self) -> Option<f64> {
168        match self {
169            Self::Float(value) => Some(value),
170            _ => None,
171        }
172    }
173
174    pub const fn as_str(self) -> Option<&'a str> {
175        match self {
176            Self::String(value) => Some(value),
177            _ => None,
178        }
179    }
180
181    pub const fn as_binary(self) -> Option<&'a [u8]> {
182        match self {
183            Self::Binary(value) => Some(value),
184            _ => None,
185        }
186    }
187
188    pub const fn as_uuid(self) -> Option<Uuid> {
189        match self {
190            Self::Uuid(value) => Some(value),
191            _ => None,
192        }
193    }
194
195    pub const fn as_list(self) -> Option<&'a [Value]> {
196        match self {
197            Self::List(value) => Some(value),
198            _ => None,
199        }
200    }
201
202    pub const fn as_map(self) -> Option<&'a BTreeMap<String, Value>> {
203        match self {
204            Self::Map(value) => Some(value),
205            _ => None,
206        }
207    }
208
209    pub fn to_owned_value(self) -> Value {
210        match self {
211            Self::Null => Value::Null,
212            Self::Bool(value) => Value::Bool(value),
213            Self::Int(value) => Value::Int(value),
214            Self::Float(value) => Value::Float(value),
215            Self::String(value) => Value::String(value.to_owned()),
216            Self::Binary(value) => Value::Binary(value.to_vec()),
217            Self::Vector(value) => Value::Vector(value.to_owned()),
218            Self::Uuid(value) => Value::Uuid(value),
219            Self::List(value) => Value::List(value.to_vec()),
220            Self::Map(value) => Value::Map(value.clone()),
221        }
222    }
223
224    const fn kind_rank(self) -> u8 {
225        match self {
226            Self::Null => 0,
227            Self::Bool(_) => 1,
228            Self::Int(_) => 2,
229            Self::Float(_) => 3,
230            Self::String(_) => 4,
231            Self::Binary(_) => 5,
232            Self::Vector(_) => 9,
233            Self::Uuid(_) => 6,
234            Self::List(_) => 7,
235            Self::Map(_) => 8,
236        }
237    }
238}
239
240impl<'a> From<&'a Value> for ValueRef<'a> {
241    fn from(value: &'a Value) -> Self {
242        match value {
243            Value::Null => Self::Null,
244            Value::Bool(value) => Self::Bool(*value),
245            Value::Int(value) => Self::Int(*value),
246            Value::Float(value) => Self::Float(*value),
247            Value::String(value) => Self::String(value),
248            Value::Binary(value) => Self::Binary(value),
249            Value::Vector(value) => Self::Vector(value.as_ref()),
250            Value::Uuid(value) => Self::Uuid(*value),
251            Value::List(value) => Self::List(value),
252            Value::Map(value) => Self::Map(value),
253        }
254    }
255}
256
257impl PartialEq for ValueRef<'_> {
258    fn eq(&self, other: &Self) -> bool {
259        self.cmp(other) == Ordering::Equal
260    }
261}
262
263impl Eq for ValueRef<'_> {}
264
265impl PartialOrd for ValueRef<'_> {
266    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
267        Some(self.cmp(other))
268    }
269}
270
271impl Ord for ValueRef<'_> {
272    fn cmp(&self, other: &Self) -> Ordering {
273        self.kind_rank()
274            .cmp(&other.kind_rank())
275            .then_with(|| match (self, other) {
276                (Self::Null, Self::Null) => Ordering::Equal,
277                (Self::Bool(left), Self::Bool(right)) => left.cmp(right),
278                (Self::Int(left), Self::Int(right)) => left.cmp(right),
279                (Self::Float(left), Self::Float(right)) => left.total_cmp(right),
280                (Self::String(left), Self::String(right)) => left.cmp(right),
281                (Self::Binary(left), Self::Binary(right)) => left.cmp(right),
282                (Self::Vector(left), Self::Vector(right)) => left.cmp(right),
283                (Self::Uuid(left), Self::Uuid(right)) => left.cmp(right),
284                (Self::List(left), Self::List(right)) => left.cmp(right),
285                (Self::Map(left), Self::Map(right)) => left.cmp(right),
286                _ => Ordering::Equal,
287            })
288    }
289}
290
291impl Hash for ValueRef<'_> {
292    fn hash<H: Hasher>(&self, state: &mut H) {
293        self.kind_rank().hash(state);
294        match self {
295            Self::Null => {}
296            Self::Bool(value) => value.hash(state),
297            Self::Int(value) => value.hash(state),
298            Self::Float(value) => value.to_bits().hash(state),
299            Self::String(value) => value.hash(state),
300            Self::Binary(value) => value.hash(state),
301            Self::Vector(value) => value.hash(state),
302            Self::Uuid(value) => value.hash(state),
303            Self::List(value) => value.hash(state),
304            Self::Map(value) => value.hash(state),
305        }
306    }
307}
308
309impl PartialEq<Value> for ValueRef<'_> {
310    fn eq(&self, other: &Value) -> bool {
311        *self == other.as_ref()
312    }
313}
314
315impl PartialEq<&Value> for ValueRef<'_> {
316    fn eq(&self, other: &&Value) -> bool {
317        *self == other.as_ref()
318    }
319}
320
321impl PartialEq<ValueRef<'_>> for Value {
322    fn eq(&self, other: &ValueRef<'_>) -> bool {
323        self.as_ref() == *other
324    }
325}
326
327impl PartialEq<ValueRef<'_>> for &Value {
328    fn eq(&self, other: &ValueRef<'_>) -> bool {
329        self.as_ref() == *other
330    }
331}
332
333impl Display for ValueRef<'_> {
334    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
335        match self {
336            Self::Null => write!(f, "null"),
337            Self::Bool(value) => write!(f, "{value}"),
338            Self::Int(value) => write!(f, "{value}"),
339            Self::Float(value) => write!(f, "{value}"),
340            Self::String(value) => write!(f, "{value}"),
341            Self::Binary(value) => write_binary(f, value),
342            Self::Vector(value) => {
343                f.write_str("VECTOR")?;
344                f.debug_list().entries(value.coordinates()).finish()
345            }
346            Self::Uuid(value) => write!(f, "{value}"),
347            Self::List(values) => {
348                let values = values
349                    .iter()
350                    .map(ToString::to_string)
351                    .collect::<Vec<_>>()
352                    .join(", ");
353                write!(f, "[{values}]")
354            }
355            Self::Map(values) => {
356                let values = values
357                    .iter()
358                    .map(|(key, value)| format!("{key}: {value}"))
359                    .collect::<Vec<_>>()
360                    .join(", ");
361                write!(f, "{{{values}}}")
362            }
363        }
364    }
365}
366
367impl Display for Value {
368    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
369        match self {
370            Value::Null => write!(f, "null"),
371            Value::Bool(value) => write!(f, "{value}"),
372            Value::Int(value) => write!(f, "{value}"),
373            Value::Float(value) => write!(f, "{value}"),
374            Value::String(value) => write!(f, "{value}"),
375            Value::Binary(value) => write_binary(f, value),
376            Value::Vector(value) => ValueRef::Vector(value.as_ref()).fmt(f),
377            Value::Uuid(value) => write!(f, "{value}"),
378            Value::List(values) => {
379                let values = values
380                    .iter()
381                    .map(ToString::to_string)
382                    .collect::<Vec<_>>()
383                    .join(", ");
384                write!(f, "[{values}]")
385            }
386            Value::Map(values) => {
387                let values = values
388                    .iter()
389                    .map(|(key, value)| format!("{key}: {value}"))
390                    .collect::<Vec<_>>()
391                    .join(", ");
392                write!(f, "{{{values}}}")
393            }
394        }
395    }
396}
397
398fn write_binary(f: &mut Formatter<'_>, value: &[u8]) -> std::fmt::Result {
399    f.write_str("\\x")?;
400    for byte in value {
401        write!(f, "{byte:02x}")?;
402    }
403    Ok(())
404}
405
406#[cfg(test)]
407mod tests {
408    use super::*;
409
410    #[test]
411    fn value_ref_borrows_variable_width_payloads() {
412        let value = Value::String("borrowed payload".to_string());
413        let reference = value.as_ref();
414
415        assert_eq!(reference.as_str(), Some("borrowed payload"));
416        let Value::String(owned) = &value else {
417            unreachable!();
418        };
419        assert_eq!(reference.as_str().unwrap().as_ptr(), owned.as_ptr());
420        assert_eq!(reference.to_owned_value(), value);
421    }
422
423    #[test]
424    fn value_ref_preserves_owned_value_order_and_hash_semantics() {
425        let values = [
426            Value::Null,
427            Value::Bool(true),
428            Value::Int(1),
429            Value::Float(f64::NAN),
430            Value::String("value".into()),
431            Value::Binary(vec![0, 0xff]),
432            Value::Vector(Vector::new(&[0.0, -0.0]).unwrap()),
433            Value::Vector(Vector::new(&[-0.0, 0.0]).unwrap()),
434            Value::Vector(Vector::new(&[1.0]).unwrap()),
435            Value::List(vec![Value::Int(2)]),
436            Value::Map(BTreeMap::from([("key".into(), Value::Int(3))])),
437        ];
438
439        for left in &values {
440            use std::hash::BuildHasher;
441            let state = std::collections::hash_map::RandomState::new();
442            assert_eq!(state.hash_one(left), state.hash_one(left.as_ref()));
443            for right in &values {
444                assert_eq!(left.cmp(right), left.as_ref().cmp(&right.as_ref()));
445                assert_eq!(left == right, left.as_ref() == *right);
446            }
447        }
448    }
449
450    #[test]
451    fn null_has_no_intrinsic_logical_type() {
452        assert_eq!(Value::Null.logical_type(), None);
453        assert_eq!(Value::Int(42).logical_type(), Some(LogicalType::Int64));
454        assert_eq!(
455            Value::Binary(vec![0, 0xff]).logical_type(),
456            Some(LogicalType::Binary)
457        );
458    }
459
460    #[test]
461    fn binary_value_ref_borrows_and_formats_postgres_hex() {
462        let value = Value::Binary(vec![0, 1, 0xfe, 0xff]);
463        let reference = value.as_ref();
464
465        assert_eq!(reference.as_binary(), Some(&[0, 1, 0xfe, 0xff][..]));
466        let Value::Binary(owned) = &value else {
467            unreachable!();
468        };
469        assert_eq!(reference.as_binary().unwrap().as_ptr(), owned.as_ptr());
470        assert_eq!(reference.to_owned_value(), value);
471        assert_eq!(value.to_string(), "\\x0001feff");
472    }
473}