Skip to main content

paginate_core/
value.rs

1//! The plain, language-agnostic value model that crosses the FFI boundary.
2//!
3//! [`Value`] is intentionally small and JSON-shaped. The binding layer converts
4//! host objects (Python `dict`/`datetime`/`Decimal`/..., or a JS object) into
5//! `Value`, the core does its work, and the binding converts back. Keeping this
6//! the *only* shared vocabulary is what makes the core reusable across runtimes.
7
8use std::collections::BTreeMap;
9
10/// A single language-agnostic value.
11///
12/// The first variants map 1:1 onto JSON. The trailing typed-scalar variants
13/// (`DateTime`, `Date`, `Decimal`, `Uuid`) exist so the cursor codec can
14/// round-trip ordering keys with full fidelity — they carry the value's
15/// canonical string form and the binding layer rebuilds the rich host type.
16///
17/// `#[non_exhaustive]`: this crate is published independently (`core-v*`) and
18/// `Value` is its central vocabulary type, so a future variant (e.g. a native
19/// big-integer) must not be a breaking change for downstream crates — like the
20/// sibling public enums (`CoreError`, `FilterOp`, ...), it requires a `_` arm.
21#[derive(Debug, Clone, PartialEq)]
22#[non_exhaustive]
23pub enum Value {
24    /// Absence of a value (`None` / `null`).
25    Null,
26    /// Boolean.
27    Bool(bool),
28    /// 64-bit signed integer.
29    Int(i64),
30    /// 64-bit float.
31    Float(f64),
32    /// UTF-8 string.
33    Str(String),
34    /// Opaque bytes.
35    Bytes(Vec<u8>),
36    /// Ordered list of values.
37    List(Vec<Value>),
38    /// String-keyed map (an item / record).
39    Map(BTreeMap<String, Value>),
40    /// ISO-8601 datetime (carried as its `isoformat()` string).
41    DateTime(String),
42    /// ISO-8601 date.
43    Date(String),
44    /// Arbitrary-precision decimal (carried as its canonical string).
45    Decimal(String),
46    /// UUID (carried as its canonical hyphenated string).
47    Uuid(String),
48}
49
50impl Value {
51    /// True when this value represents the absence of data.
52    #[must_use]
53    pub fn is_null(&self) -> bool {
54        matches!(self, Value::Null)
55    }
56
57    /// Borrow the inner string for the textual variants, else `None`.
58    #[must_use]
59    pub fn as_str(&self) -> Option<&str> {
60        match self {
61            Value::Str(s)
62            | Value::DateTime(s)
63            | Value::Date(s)
64            | Value::Decimal(s)
65            | Value::Uuid(s) => Some(s),
66            _ => None,
67        }
68    }
69}