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}