adam_lang/cell_value.rs
1//! Private canonical cell storage and construction-time frozen Adam/CEL adapters.
2//!
3//! Primitive cells remain concrete Rust values. Compound access passes through
4//! `ParsedSheet`'s checked representation-witness boundary.
5use std::any::{Any, TypeId};
6
7use cel_runtime::{DynSegment, OwnedValue, ValueType, prepared_segment::PreparedRawSegment};
8
9use crate::{TypeRegistry, type_registry::TypeShape};
10
11/// Formats a canonical compound cell at the checked host boundary.
12///
13/// This also supports hosts that retain the underlying `adam_rs::Sheet`.
14/// # Errors
15/// Rejects an unknown cell, concrete leaf storage, or absent debug capability.
16/// - Complexity: O(v + s) for v contained values and s output bytes.
17///
18/// # Examples
19/// ```
20/// let parsed = adam_lang::AdamParser::new(adam_lang::TypeRegistry::new(), cel_parser::OpLookup::new())
21/// .parse_str("sheet s { cell point: (i32, i32) = (3, 4); }").unwrap();
22/// assert_eq!(adam_lang::debug_compound_cell(&parsed.sheet, parsed.cell_names["point"].0).unwrap(), "(3, 4)");
23/// ```
24pub fn debug_compound_cell(sheet: &adam_rs::Sheet, id: adam_rs::CellId) -> anyhow::Result<String> {
25 sheet.read::<CellValue>(id)?.0.try_debug()
26}
27
28/// Prepares canonical compound-cell formatting from the cell owner's debug capability.
29///
30/// The returned closure reads the current value, checks its complete shape, and
31/// formats through the retained capability. It does not consume or clone values.
32///
33/// # Errors
34/// Rejects an unknown cell, noncompound storage, or unavailable debug capability.
35/// The closure also rejects a different shape or missing cell in its input sheet.
36/// - Complexity: Preparation O(1); each call O(n + v + s) for n type nodes,
37/// v contained values and s output bytes, excluding leaf formatter costs.
38///
39/// # Examples
40/// ```
41/// let parsed = adam_lang::AdamParser::new(adam_lang::TypeRegistry::new(), cel_parser::OpLookup::new())
42/// .parse_str("sheet s { cell xs: [i32]; }").unwrap();
43/// let format = adam_lang::prepare_compound_cell_formatter(&parsed, parsed.cell_names["xs"].0).unwrap();
44/// assert_eq!(format(&parsed).unwrap(), "[]");
45/// ```
46pub fn prepare_compound_cell_formatter(
47 sheet: &adam_rs::Sheet,
48 id: adam_rs::CellId,
49) -> anyhow::Result<impl Fn(&adam_rs::Sheet) -> anyhow::Result<String> + use<>> {
50 let formatter = sheet.read::<CellValue>(id)?.0.prepare_debug()?;
51 Ok(move |sheet: &adam_rs::Sheet| formatter.format(&sheet.read::<CellValue>(id)?.0))
52}
53
54/// Owns a compound value whose cloning and equality capabilities are bound.
55///
56/// Only checked host writes and frozen output adapters construct this private type.
57pub(crate) struct CellValue(pub(crate) OwnedValue);
58
59impl CellValue {
60 /// Rejects missing cell capabilities before sheet insertion.
61 ///
62 /// # Errors
63 /// Returns a diagnostic for missing cloning or equality.
64 pub(crate) fn validate(ty: &ValueType) -> anyhow::Result<()> {
65 anyhow::ensure!(
66 ty.can_clone_for_argument(),
67 "Adam cell clone capability unavailable for `{}`",
68 ty.type_name()
69 );
70 anyhow::ensure!(
71 ty.can_compare(),
72 "Adam cell equality capability unavailable for `{}`",
73 ty.type_name()
74 );
75 Ok(())
76 }
77}
78
79impl Clone for CellValue {
80 /// Clones through the previously validated frozen ownership plan.
81 ///
82 /// - Complexity: O(v) for v contained values, excluding leaf clone costs.
83 fn clone(&self) -> Self {
84 Self(self.0.try_clone().expect("validated cell clone capability"))
85 }
86}
87
88impl PartialEq for CellValue {
89 /// Compares values at a shape-validated cell boundary without repeating type checks.
90 ///
91 /// - Complexity: O(v) for v contained values, excluding leaf equality costs.
92 fn eq(&self, other: &Self) -> bool {
93 // SAFETY: This type is private; host writes and all prepared outputs bind
94 // the complete cell shape before entering Adam's type-erased storage.
95 unsafe { self.0.eq_bound(&other.0) }
96 }
97}
98
99impl std::fmt::Debug for CellValue {
100 /// Formats a value through its optional canonical debug capability.
101 ///
102 /// - Complexity: O(v + s) for v contained values and s formatted bytes.
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self.0.try_debug() {
105 Ok(value) => f.write_str(&value),
106 Err(_) => f.write_str("<compound value>"),
107 }
108 }
109}
110
111/// Describes a construction-time validated move from canonical result to Adam storage.
112enum Output {
113 /// A registered primitive or foreign concrete Rust leaf.
114 Leaf(unsafe fn(OwnedValue) -> Box<dyn Any>),
115 /// Private whole-value ownership of a recursive compound.
116 Compound,
117}
118
119impl Output {
120 /// Moves one already-bound canonical result into its Adam storage carrier.
121 fn store(&self, value: OwnedValue) -> Box<dyn Any> {
122 match self {
123 // SAFETY: The result identity was bound during construction.
124 Self::Leaf(box_value) => unsafe { box_value(value) },
125 Self::Compound => Box::new(CellValue(value)),
126 }
127 }
128}
129
130/// Executes a frozen canonical raw frame behind Adam's existing host callback boundary.
131pub(crate) struct Frame {
132 body: RawFrame,
133 outputs: Vec<Output>,
134 split: bool,
135}
136
137/// Binds Adam storage addresses to the canonical runtime's one raw execution engine.
138pub(crate) struct RawFrame {
139 body: PreparedRawSegment,
140 inputs: Vec<Input>,
141 pointers: Vec<*const u8>,
142}
143
144/// Retains the complete storage identity required at an erased host boundary.
145enum Input {
146 /// A concrete registered Rust leaf.
147 Leaf(TypeId),
148 /// A private carrier with an exact recursive canonical shape.
149 Compound(ValueType),
150}
151
152impl Input {
153 /// Checks storage identity and complete shape without binding or reading raw pointers.
154 ///
155 /// # Errors
156 /// Rejects a different concrete type, compound carrier, or recursive shape.
157 /// - Complexity: O(n) for n compound descriptor nodes; O(1) for leaves.
158 fn validate(&self, input: &dyn Any) -> anyhow::Result<()> {
159 let matches = match self {
160 Self::Leaf(id) => input.type_id() == *id,
161 Self::Compound(expected) => input
162 .downcast_ref::<CellValue>()
163 .is_some_and(|value| value.0.value_type().same_shape(expected)),
164 };
165 anyhow::ensure!(matches, "Adam argument storage or shape mismatch");
166 Ok(())
167 }
168}
169
170impl Frame {
171 /// Validates complete reads, outputs, and cell capabilities before execution.
172 ///
173 /// # Errors
174 /// Rejects mismatched recursive representations or missing required capabilities.
175 /// - Complexity: O(n) in signature nodes and emitted reads.
176 pub(crate) fn new(
177 body: DynSegment,
178 inputs: &[TypeShape],
179 outputs: &[TypeShape],
180 split: bool,
181 types: &TypeRegistry,
182 ) -> anyhow::Result<Self> {
183 let result = if split {
184 types.value_type(&TypeShape::Tuple(outputs.to_vec()))
185 } else {
186 types.value_type(&outputs[0])
187 };
188 let mut adapters = Vec::with_capacity(outputs.len());
189 for shape in outputs {
190 match shape {
191 TypeShape::Named(id) => adapters.push(Output::Leaf(
192 types
193 .entry_by_type_id(*id)
194 .expect("resolved leaf")
195 .box_owned,
196 )),
197 _ => {
198 CellValue::validate(&types.value_type(shape))?;
199 adapters.push(Output::Compound);
200 }
201 }
202 }
203 Ok(Self {
204 body: RawFrame::new(body, inputs, &result, types)?,
205 outputs: adapters,
206 split,
207 })
208 }
209
210 /// Executes raw borrowed inputs already type-checked by Adam's callback boundary.
211 ///
212 /// - Precondition: Input storage matches the exact shapes supplied to construction.
213 /// # Errors
214 /// Propagates CEL operation failures after canonical cleanup.
215 /// - Complexity: O(a) pointer binding plus body costs and output moves.
216 pub(crate) fn call(&mut self, inputs: &[&dyn Any]) -> anyhow::Result<Vec<Box<dyn Any>>> {
217 let result = self.body.call(inputs)?;
218 self.store(result)
219 }
220
221 /// Executes a filter frame without allocating a merged host argument list.
222 ///
223 /// - Precondition: The candidate and dependencies match the prepared storage types.
224 /// # Errors
225 /// Propagates operation failures.
226 /// - Complexity: O(a) pointer binding plus body costs and output moves.
227 pub(crate) fn call_with_prefix(
228 &mut self,
229 value: &dyn Any,
230 inputs: &[&dyn Any],
231 ) -> anyhow::Result<Vec<Box<dyn Any>>> {
232 let result = self.body.call_with_prefix(value, inputs)?;
233 self.store(result)
234 }
235
236 /// Moves a whole result or its bound fields into the requested output cells.
237 ///
238 /// # Errors
239 /// Returns an error if a destructuring result is not a tuple.
240 /// - Complexity: O(n + s) for n output fields and s representation bytes.
241 fn store(&self, result: OwnedValue) -> anyhow::Result<Vec<Box<dyn Any>>> {
242 if self.split {
243 Ok(result
244 .into_fields()?
245 .into_iter()
246 .zip(&self.outputs)
247 .map(|(value, output)| output.store(value))
248 .collect())
249 } else {
250 Ok(vec![self.outputs[0].store(result)])
251 }
252 }
253}
254
255impl RawFrame {
256 /// Validates every positional read and the result once before execution.
257 ///
258 /// # Errors
259 /// Rejects a mismatched signature or missing borrowed cloning.
260 /// - Complexity: O(n) in descriptor nodes and emitted reads.
261 pub(crate) fn new(
262 body: DynSegment,
263 inputs: &[TypeShape],
264 result: &ValueType,
265 types: &TypeRegistry,
266 ) -> anyhow::Result<Self> {
267 let input_types: Vec<_> = inputs.iter().map(|shape| types.value_type(shape)).collect();
268 Ok(Self {
269 body: PreparedRawSegment::new(body, &input_types, result)?,
270 inputs: inputs
271 .iter()
272 .zip(input_types)
273 .map(|(shape, value_type)| match shape {
274 TypeShape::Named(id) => Input::Leaf(*id),
275 _ => Input::Compound(value_type),
276 })
277 .collect(),
278 pointers: vec![std::ptr::null(); inputs.len()],
279 })
280 }
281
282 /// Executes an Adam-validated host frame without recursive checks or conversions.
283 ///
284 /// - Precondition: Every input matches its preparation-time storage adapter.
285 /// # Errors
286 /// Propagates operation failures after cleanup.
287 /// - Complexity: O(a) pointer binding plus body costs.
288 pub(crate) fn call(&mut self, inputs: &[&dyn Any]) -> anyhow::Result<OwnedValue> {
289 self.call_arguments(inputs.iter().copied(), inputs.len())
290 }
291
292 /// Prepends a filter candidate without allocating a merged host argument list.
293 ///
294 /// - Precondition: The candidate and dependencies match their prepared storage types.
295 /// # Errors
296 /// Propagates operation failures.
297 /// - Complexity: O(a) pointer binding plus body costs.
298 pub(crate) fn call_with_prefix(
299 &mut self,
300 value: &dyn Any,
301 inputs: &[&dyn Any],
302 ) -> anyhow::Result<OwnedValue> {
303 self.call_arguments(
304 std::iter::once(value).chain(inputs.iter().copied()),
305 inputs.len() + 1,
306 )
307 }
308
309 /// Checks every erased input before binding any address or executing the prefixed frame.
310 ///
311 /// # Errors
312 /// Rejects missing or extra inputs, wrong scalar identities, compound carriers, or
313 /// recursive shapes on every invocation; propagates operation failures after cleanup.
314 /// Rejection leaves previously bound pointers unused and permits later valid calls.
315 /// - Complexity: O(a + n) validation for a inputs and n compound descriptor nodes,
316 /// plus pointer binding and body costs.
317 pub(crate) fn call_checked_with_prefix(
318 &mut self,
319 value: &dyn Any,
320 inputs: &[&dyn Any],
321 ) -> anyhow::Result<OwnedValue> {
322 anyhow::ensure!(
323 inputs.len().checked_add(1) == Some(self.inputs.len()),
324 "Adam argument count mismatch"
325 );
326 for (input, expected) in std::iter::once(value)
327 .chain(inputs.iter().copied())
328 .zip(&self.inputs)
329 {
330 expected.validate(input)?;
331 }
332 self.call_with_prefix(value, inputs)
333 }
334
335 /// Binds borrowed host addresses into the reusable canonical pointer slots.
336 ///
337 /// - Precondition: Input count and types match preparation.
338 /// # Errors
339 /// Propagates operation failures after raw frame cleanup.
340 /// - Complexity: O(a) pointer binding plus body costs.
341 fn call_arguments<'a>(
342 &mut self,
343 inputs: impl Iterator<Item = &'a dyn Any>,
344 arity: usize,
345 ) -> anyhow::Result<OwnedValue> {
346 debug_assert_eq!(arity, self.pointers.len());
347 for ((input, adapter), pointer) in inputs.zip(&self.inputs).zip(&mut self.pointers) {
348 let data = std::ptr::from_ref(input).cast::<()>();
349 *pointer = match adapter {
350 Input::Compound(_) => {
351 // SAFETY: Adam's internal boundary or the checked public
352 // adapter verifies the private carrier and complete shape.
353 unsafe { &*data.cast::<CellValue>() }.0.as_ptr()
354 }
355 Input::Leaf(_) => data.cast(),
356 };
357 }
358 // SAFETY: Construction validates every read. The internal callback boundary
359 // or checked entry point guarantees storage types and complete compound shapes.
360 unsafe { self.body.call(&self.pointers) }
361 }
362}