datalogic_rs/eval_input.rs
1//! Input adapter for [`crate::Engine::evaluate`] (the raw-arena tier) and
2//! [`crate::Session::eval_borrowed`].
3//!
4//! [`EvalInput`] lets the borrowed-result entry points accept any of the
5//! input shapes a caller is likely to have on hand:
6//!
7//! - `&'a DataValue<'a>` — already arena-resident; passed through unchanged.
8//! - `DataValue<'a>` — single bumpalo allocation into the arena.
9//! - `&OwnedDataValue` — deep-borrowed into the arena.
10//! - `&ParsedData` — parse-once handle; passed through unchanged (zero cost).
11//! - `&str` — JSON-parsed via [`datavalue::DataValue::from_str`].
12//! - `&serde_json::Value` (`serde_json`) — deep-converted into the arena.
13//!
14//! `EvalInput` carries the arena lifetime in its trait parameter, so it
15//! is the right adapter when the **caller** supplies the arena. For the
16//! one-shot owned-result methods on [`crate::Engine`] and the module-
17//! level `eval*` helpers (where the arena lives **inside** the call),
18//! the engine instead uses [`OwnedInput`], which doesn't carry an arena
19//! lifetime — see that trait for the supported shapes.
20//!
21//! Conversion is fallible because the `&str` impl can return a parse
22//! error; the borrow / owned-clone impls always succeed and return
23//! [`Ok`] without touching the arena beyond the documented per-impl
24//! cost.
25
26use bumpalo::Bump;
27use datavalue::OwnedDataValue;
28
29use crate::Result;
30use crate::arena::DataValue;
31
32/// Sealed-trait scaffolding — the [`Sealed`] super-bound lives in this
33/// private module so external crates cannot implement [`EvalInput`].
34/// The set of supported input shapes is a closed class defined entirely
35/// in this file.
36mod sealed {
37 pub trait Sealed {}
38}
39
40/// Adapter trait that converts a value into a `&'a DataValue<'a>` borrowed
41/// from the caller-supplied arena. **Sealed** — the supported input
42/// shapes are listed in this file; external crates cannot add new ones.
43pub trait EvalInput<'a>: sealed::Sealed {
44 /// Materialise `self` as a `&'a DataValue<'a>` in `arena`.
45 ///
46 /// Implementations either pass through an existing arena reference (zero
47 /// cost), allocate one node, or deep-convert from an owned tree.
48 fn into_arena_value(self, arena: &'a Bump) -> Result<&'a DataValue<'a>>;
49}
50
51impl<'a> sealed::Sealed for &'a DataValue<'a> {}
52impl<'a> EvalInput<'a> for &'a DataValue<'a> {
53 #[inline]
54 fn into_arena_value(self, _arena: &'a Bump) -> Result<&'a DataValue<'a>> {
55 Ok(self)
56 }
57}
58
59impl<'a> sealed::Sealed for DataValue<'a> {}
60impl<'a> EvalInput<'a> for DataValue<'a> {
61 #[inline]
62 fn into_arena_value(self, arena: &'a Bump) -> Result<&'a DataValue<'a>> {
63 Ok(arena.alloc(self))
64 }
65}
66
67impl sealed::Sealed for &str {}
68impl<'a> EvalInput<'a> for &'a str {
69 #[inline]
70 fn into_arena_value(self, arena: &'a Bump) -> Result<&'a DataValue<'a>> {
71 let av = DataValue::from_str(self, arena)?;
72 Ok(arena.alloc(av))
73 }
74}
75
76// `&String` derefs to `&str`, but trait resolution doesn't autoderef
77// across trait impls — accepting `&String` directly here saves callers
78// from writing `payload.as_str()` at every call site.
79impl sealed::Sealed for &String {}
80impl<'a> EvalInput<'a> for &'a String {
81 #[inline]
82 fn into_arena_value(self, arena: &'a Bump) -> Result<&'a DataValue<'a>> {
83 <&'a str as EvalInput<'a>>::into_arena_value(self.as_str(), arena)
84 }
85}
86
87impl sealed::Sealed for &OwnedDataValue {}
88impl<'a> EvalInput<'a> for &'a OwnedDataValue {
89 #[inline]
90 fn into_arena_value(self, arena: &'a Bump) -> Result<&'a DataValue<'a>> {
91 Ok(arena.alloc(self.to_arena(arena)))
92 }
93}
94
95impl sealed::Sealed for &crate::ParsedData {}
96impl<'a> EvalInput<'a> for &'a crate::ParsedData {
97 #[inline]
98 fn into_arena_value(self, _arena: &'a Bump) -> Result<&'a DataValue<'a>> {
99 Ok(self.value())
100 }
101}
102
103#[cfg(feature = "serde_json")]
104impl sealed::Sealed for &serde_json::Value {}
105#[cfg(feature = "serde_json")]
106impl<'a> EvalInput<'a> for &'a serde_json::Value {
107 #[inline]
108 fn into_arena_value(self, arena: &'a Bump) -> Result<&'a DataValue<'a>> {
109 let av = crate::arena::value_to_data(self, arena);
110 Ok(arena.alloc(av))
111 }
112}
113
114// ============================================================
115// OwnedInput — arena-lifetime-free counterpart for one-shot calls
116// ============================================================
117
118/// Adapter trait for [`crate::Engine::eval`] / [`crate::Engine::eval_str`]
119// `Engine::eval_into` is gated behind `serde_json`. Link it when the
120// feature is on; otherwise reference it as code text to keep the docs
121// resolvable in a default-features build.
122#[cfg_attr(
123 feature = "serde_json",
124 doc = "/ [`crate::Engine::eval_into`] and the module-level `datalogic::eval*`"
125)]
126#[cfg_attr(
127 not(feature = "serde_json"),
128 doc = "(plus `Engine::eval_into` with the `serde_json` feature) and the module-level `datalogic::eval*`"
129)]
130/// helpers, where the engine creates and owns the arena per call.
131///
132/// Unlike [`EvalInput`] (which carries an arena lifetime), `OwnedInput`
133/// produces an [`OwnedDataValue`] without borrowing into a caller arena.
134/// The engine then deep-borrows that owned value into its per-call
135/// bump. Sealed; the supported set is closed:
136///
137/// - `&str` — JSON-parsed.
138/// - `&String` — JSON-parsed.
139/// - `&OwnedDataValue` — cloned.
140/// - `OwnedDataValue` — moved.
141/// - `&serde_json::Value` (`serde_json`) — deep-converted.
142///
143/// For the borrowed-result paths, use [`EvalInput`] instead.
144pub trait OwnedInput: sealed::Sealed {
145 /// Materialise `self` as an owned data value.
146 fn into_owned_input(self) -> Result<OwnedDataValue>;
147}
148
149impl OwnedInput for &str {
150 #[inline]
151 fn into_owned_input(self) -> Result<OwnedDataValue> {
152 Ok(OwnedDataValue::from_json(self)?)
153 }
154}
155
156impl OwnedInput for &String {
157 #[inline]
158 fn into_owned_input(self) -> Result<OwnedDataValue> {
159 Ok(OwnedDataValue::from_json(self.as_str())?)
160 }
161}
162
163impl OwnedInput for &OwnedDataValue {
164 #[inline]
165 fn into_owned_input(self) -> Result<OwnedDataValue> {
166 Ok(self.clone())
167 }
168}
169
170impl sealed::Sealed for OwnedDataValue {}
171impl OwnedInput for OwnedDataValue {
172 #[inline]
173 fn into_owned_input(self) -> Result<OwnedDataValue> {
174 Ok(self)
175 }
176}
177
178#[cfg(feature = "serde_json")]
179impl OwnedInput for &serde_json::Value {
180 #[inline]
181 fn into_owned_input(self) -> Result<OwnedDataValue> {
182 Ok(crate::serde_bridge::owned_from_serde(self))
183 }
184}