Skip to main content

lemma/evaluation/
evaluate_data.rs

1use crate::computation::{OperationResult, VetoType};
2use crate::parsing::ast::{ascii_lowercase_logical_name, canonical_logical_name};
3use crate::planning::execution_plan::{validate_value_against_type, ExecutionPlan};
4use crate::planning::instance_list::{
5    default_of, row_end_type, typed_from_bound, InstanceListSource, KeyIndex,
6};
7use crate::planning::semantics::{
8    parse_value_from_string, row_literal, typed_literal_from_parsed,
9    typed_literal_with_binding_unit, BoundValueKind, DataDefinition, DataPath, InstanceKey,
10    KeyField, LemmaType, ListKey, ListValue, LiteralValue, RowBinding, RowEnd, RowOrigin, Source,
11    SpecIdentity, SpecValue, TypeSpecification, TypedLiteral, ValueKind,
12};
13use crate::planning::unit_index::UnitIndex;
14use crate::Error;
15use crate::ResourceLimits;
16use indexmap::IndexMap;
17use std::collections::HashSet;
18use std::sync::Arc;
19
20/// Neutral overlay tree. [`EvaluateData::resolve`] reads each value by declared type.
21///
22/// Host input may nest arbitrarily deep, so `Clone`, `Debug`, `PartialEq` and `Drop` are
23/// iterative. Derived versions recurse on the native stack.
24pub enum EvaluateDataValue {
25    /// Unparsed Lemma literal text, parsed against the declared type by [`parse_value_from_string`].
26    Literal(String),
27    Boolean(bool),
28    Map(IndexMap<String, EvaluateDataValue>),
29    List(Vec<EvaluateDataValue>),
30}
31
32impl EvaluateDataValue {
33    pub fn string(value: impl Into<String>) -> Self {
34        Self::Literal(value.into())
35    }
36
37    pub(crate) fn is_empty(&self) -> bool {
38        match self {
39            Self::Literal(s) => s.trim().is_empty(),
40            Self::Boolean(_) | Self::Map(_) | Self::List(_) => false,
41        }
42    }
43
44    pub(crate) fn into_list(mut self) -> Option<Vec<EvaluateDataValue>> {
45        match &mut self {
46            Self::List(items) => Some(std::mem::take(items)),
47            Self::Literal(_) | Self::Boolean(_) | Self::Map(_) => None,
48        }
49    }
50
51    pub(crate) fn into_map(mut self) -> Option<IndexMap<String, EvaluateDataValue>> {
52        match &mut self {
53            Self::Map(map) => Some(std::mem::take(map)),
54            Self::Literal(_) | Self::Boolean(_) | Self::List(_) => None,
55        }
56    }
57}
58
59impl Drop for EvaluateDataValue {
60    fn drop(&mut self) {
61        let mut pending = Vec::new();
62        take_children(self, &mut pending);
63        while let Some(mut node) = pending.pop() {
64            take_children(&mut node, &mut pending);
65        }
66    }
67}
68
69fn take_children(node: &mut EvaluateDataValue, pending: &mut Vec<EvaluateDataValue>) {
70    match node {
71        EvaluateDataValue::Literal(_) | EvaluateDataValue::Boolean(_) => {}
72        EvaluateDataValue::Map(map) => pending.extend(std::mem::take(map).into_values()),
73        EvaluateDataValue::List(items) => pending.append(items),
74    }
75}
76
77impl Clone for EvaluateDataValue {
78    fn clone(&self) -> Self {
79        let mut frames: Vec<CloneFrame<'_>> = Vec::new();
80        let mut step = CloneStep::Descend(self);
81        loop {
82            step = match step {
83                CloneStep::Descend(value) => match value {
84                    EvaluateDataValue::Literal(s) => {
85                        CloneStep::Deliver(EvaluateDataValue::Literal(s.clone()))
86                    }
87                    EvaluateDataValue::Boolean(b) => {
88                        CloneStep::Deliver(EvaluateDataValue::Boolean(*b))
89                    }
90                    EvaluateDataValue::List(items) => {
91                        frames.push(CloneFrame::List {
92                            out: Vec::with_capacity(items.len()),
93                            children: items.iter(),
94                        });
95                        CloneStep::NextChild
96                    }
97                    EvaluateDataValue::Map(map) => {
98                        frames.push(CloneFrame::Map {
99                            out: IndexMap::with_capacity(map.len()),
100                            pending_key: None,
101                            children: map.iter(),
102                        });
103                        CloneStep::NextChild
104                    }
105                },
106                CloneStep::NextChild => {
107                    let frame = frames
108                        .last_mut()
109                        .expect("BUG: clone frame stack empty while reading a child");
110                    match frame.next_child() {
111                        Some(child) => CloneStep::Descend(child),
112                        None => {
113                            let frame = frames
114                                .pop()
115                                .expect("BUG: clone frame stack empty on completion");
116                            CloneStep::Deliver(frame.finish())
117                        }
118                    }
119                }
120                CloneStep::Deliver(value) => match frames.last_mut() {
121                    None => return value,
122                    Some(frame) => {
123                        frame.push(value);
124                        CloneStep::NextChild
125                    }
126                },
127            };
128        }
129    }
130}
131
132enum CloneStep<'a> {
133    Descend(&'a EvaluateDataValue),
134    NextChild,
135    Deliver(EvaluateDataValue),
136}
137
138enum CloneFrame<'a> {
139    List {
140        out: Vec<EvaluateDataValue>,
141        children: std::slice::Iter<'a, EvaluateDataValue>,
142    },
143    Map {
144        out: IndexMap<String, EvaluateDataValue>,
145        pending_key: Option<String>,
146        children: indexmap::map::Iter<'a, String, EvaluateDataValue>,
147    },
148}
149
150impl<'a> CloneFrame<'a> {
151    fn next_child(&mut self) -> Option<&'a EvaluateDataValue> {
152        match self {
153            CloneFrame::List { children, .. } => children.next(),
154            CloneFrame::Map {
155                pending_key,
156                children,
157                ..
158            } => children.next().map(|(key, child)| {
159                *pending_key = Some(key.clone());
160                child
161            }),
162        }
163    }
164
165    fn push(&mut self, value: EvaluateDataValue) {
166        match self {
167            CloneFrame::List { out, .. } => out.push(value),
168            CloneFrame::Map {
169                out, pending_key, ..
170            } => {
171                let key = pending_key
172                    .take()
173                    .expect("BUG: clone map child delivered without its key");
174                out.insert(key, value);
175            }
176        }
177    }
178
179    fn finish(self) -> EvaluateDataValue {
180        match self {
181            CloneFrame::List { out, .. } => EvaluateDataValue::List(out),
182            CloneFrame::Map { out, .. } => EvaluateDataValue::Map(out),
183        }
184    }
185}
186
187impl PartialEq for EvaluateDataValue {
188    fn eq(&self, other: &Self) -> bool {
189        let mut pending: Vec<(&EvaluateDataValue, &EvaluateDataValue)> = vec![(self, other)];
190        while let Some((left, right)) = pending.pop() {
191            match (left, right) {
192                (EvaluateDataValue::Literal(a), EvaluateDataValue::Literal(b)) => {
193                    if a != b {
194                        return false;
195                    }
196                }
197                (EvaluateDataValue::Boolean(a), EvaluateDataValue::Boolean(b)) => {
198                    if a != b {
199                        return false;
200                    }
201                }
202                (EvaluateDataValue::List(a), EvaluateDataValue::List(b)) => {
203                    if a.len() != b.len() {
204                        return false;
205                    }
206                    pending.extend(a.iter().zip(b.iter()));
207                }
208                (EvaluateDataValue::Map(a), EvaluateDataValue::Map(b)) => {
209                    if a.len() != b.len() {
210                        return false;
211                    }
212                    for (key, value) in a {
213                        match b.get(key) {
214                            Some(other_value) => pending.push((value, other_value)),
215                            None => return false,
216                        }
217                    }
218                }
219                _ => return false,
220            }
221        }
222        true
223    }
224}
225
226impl Eq for EvaluateDataValue {}
227
228enum DebugTask<'a> {
229    Node(&'a EvaluateDataValue, usize),
230    Text(&'static str),
231    Quoted(&'a str),
232    Indent(usize),
233}
234
235impl std::fmt::Debug for EvaluateDataValue {
236    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237        let pretty = f.alternate();
238        let mut tasks = vec![DebugTask::Node(self, 0)];
239        while let Some(task) = tasks.pop() {
240            match task {
241                DebugTask::Node(value, level) => {
242                    let mut seq = Vec::new();
243                    debug_seq(value, level, pretty, &mut seq);
244                    tasks.extend(seq.into_iter().rev());
245                }
246                DebugTask::Text(text) => f.write_str(text)?,
247                DebugTask::Quoted(text) => write!(f, "{text:?}")?,
248                DebugTask::Indent(level) => write!(f, "{:width$}", "", width = 4 * level)?,
249            }
250        }
251        Ok(())
252    }
253}
254
255/// Tokens of `value` at nesting `level`, matching the derived `Debug` layout.
256fn debug_seq<'a>(
257    value: &'a EvaluateDataValue,
258    level: usize,
259    pretty: bool,
260    seq: &mut Vec<DebugTask<'a>>,
261) {
262    match value {
263        EvaluateDataValue::Literal(text) => debug_tuple(seq, level, pretty, "Literal", |out, _| {
264            out.push(DebugTask::Quoted(text))
265        }),
266        EvaluateDataValue::Boolean(b) => debug_tuple(seq, level, pretty, "Boolean", |out, _| {
267            out.push(DebugTask::Text(if *b { "true" } else { "false" }))
268        }),
269        EvaluateDataValue::Map(map) => {
270            debug_tuple(seq, level, pretty, "Map", |out, field_level| {
271                if map.is_empty() {
272                    out.push(DebugTask::Text("{}"));
273                } else if pretty {
274                    out.push(DebugTask::Text("{\n"));
275                    for (key, child) in map {
276                        out.push(DebugTask::Indent(field_level + 1));
277                        out.push(DebugTask::Quoted(key));
278                        out.push(DebugTask::Text(": "));
279                        out.push(DebugTask::Node(child, field_level + 1));
280                        out.push(DebugTask::Text(",\n"));
281                    }
282                    out.push(DebugTask::Indent(field_level));
283                    out.push(DebugTask::Text("}"));
284                } else {
285                    out.push(DebugTask::Text("{"));
286                    for (index, (key, child)) in map.iter().enumerate() {
287                        if index > 0 {
288                            out.push(DebugTask::Text(", "));
289                        }
290                        out.push(DebugTask::Quoted(key));
291                        out.push(DebugTask::Text(": "));
292                        out.push(DebugTask::Node(child, field_level));
293                    }
294                    out.push(DebugTask::Text("}"));
295                }
296            })
297        }
298        EvaluateDataValue::List(items) => {
299            debug_tuple(seq, level, pretty, "List", |out, field_level| {
300                if items.is_empty() {
301                    out.push(DebugTask::Text("[]"));
302                } else if pretty {
303                    out.push(DebugTask::Text("[\n"));
304                    for child in items {
305                        out.push(DebugTask::Indent(field_level + 1));
306                        out.push(DebugTask::Node(child, field_level + 1));
307                        out.push(DebugTask::Text(",\n"));
308                    }
309                    out.push(DebugTask::Indent(field_level));
310                    out.push(DebugTask::Text("]"));
311                } else {
312                    out.push(DebugTask::Text("["));
313                    for (index, child) in items.iter().enumerate() {
314                        if index > 0 {
315                            out.push(DebugTask::Text(", "));
316                        }
317                        out.push(DebugTask::Node(child, field_level));
318                    }
319                    out.push(DebugTask::Text("]"));
320                }
321            })
322        }
323    }
324}
325
326/// `Name(field)` with the field's tokens at `field_level`; pretty mode puts it on its own line.
327fn debug_tuple<'a>(
328    seq: &mut Vec<DebugTask<'a>>,
329    level: usize,
330    pretty: bool,
331    name: &'static str,
332    field: impl FnOnce(&mut Vec<DebugTask<'a>>, usize),
333) {
334    seq.push(DebugTask::Text(name));
335    if pretty {
336        seq.push(DebugTask::Text("(\n"));
337        seq.push(DebugTask::Indent(level + 1));
338        field(seq, level + 1);
339        seq.push(DebugTask::Text(",\n"));
340        seq.push(DebugTask::Indent(level));
341        seq.push(DebugTask::Text(")"));
342    } else {
343        seq.push(DebugTask::Text("("));
344        field(seq, level);
345        seq.push(DebugTask::Text(")"));
346    }
347}
348
349impl From<bool> for EvaluateDataValue {
350    fn from(value: bool) -> Self {
351        Self::Boolean(value)
352    }
353}
354
355impl From<String> for EvaluateDataValue {
356    fn from(value: String) -> Self {
357        Self::Literal(value)
358    }
359}
360
361impl From<&str> for EvaluateDataValue {
362    fn from(value: &str) -> Self {
363        Self::Literal(value.to_string())
364    }
365}
366
367fn reject_case_insensitive_duplicates<'a>(
368    keys: impl IntoIterator<Item = &'a str>,
369    describe: impl Fn(&str) -> String,
370) -> Result<(), Error> {
371    let mut seen = HashSet::new();
372    for key in keys {
373        let canonical = ascii_lowercase_logical_name(key.to_string());
374        if !seen.insert(canonical.clone()) {
375            return Err(Error::request(
376                describe(&canonical),
377                Some("Keys are case-insensitive; remove the duplicate"),
378            ));
379        }
380    }
381    Ok(())
382}
383
384/// Top-level run-data entries from a host walker. Keys differing only in case: request Error.
385pub fn evaluate_data_from_entries(
386    entries: Vec<(String, EvaluateDataValue)>,
387) -> Result<IndexMap<String, EvaluateDataValue>, Error> {
388    reject_case_insensitive_duplicates(entries.iter().map(|(k, _)| k.as_str()), |k| {
389        format!("Duplicate data key '{k}'")
390    })?;
391    Ok(entries.into_iter().collect())
392}
393
394/// Nested object entries from a host walker. Keys differing only in case: request Error.
395pub fn evaluate_data_map_from_entries(
396    entries: Vec<(String, EvaluateDataValue)>,
397) -> Result<EvaluateDataValue, Error> {
398    reject_case_insensitive_duplicates(entries.iter().map(|(k, _)| k.as_str()), |k| {
399        format!("Duplicate object key '{k}'")
400    })?;
401    Ok(EvaluateDataValue::Map(entries.into_iter().collect()))
402}
403
404/// Host list item that is null (JSON `null`, JS `null` / `undefined`, Java `null`, Elixir `nil`).
405/// A list has no unsupplied row, so this is a request Error, never an omitted item.
406/// `position` is 1-based within its list.
407pub fn evaluate_data_null_list_item(position: usize) -> Error {
408    Error::request(
409        format!("list item {position} is null"),
410        Some("remove the item or supply a value"),
411    )
412}
413
414/// Attribute a walker error to its top-level data key unless already attributed.
415pub fn attribute_evaluate_data_error(error: Error, key: &str) -> Error {
416    if error.related_data().is_some() {
417        error
418    } else {
419        error.with_related_data(key)
420    }
421}
422
423pub fn parse_data_value(
424    input: &EvaluateDataValue,
425    lemma_type: &Arc<LemmaType>,
426    source: &Source,
427    unit_index: &UnitIndex,
428    named_types: &IndexMap<String, Arc<LemmaType>>,
429) -> Result<TypedLiteral, Error> {
430    let mut frames: Vec<ListParseFrame<'_>> = Vec::new();
431    let mut step = ParseStep::Parse(input, Arc::clone(lemma_type));
432    loop {
433        step = match step {
434            ParseStep::Parse(value, ty) => {
435                match parse_node(value, &ty, source, unit_index, named_types)? {
436                    ParseNode::Literal(literal) => ParseStep::Deliver(literal),
437                    ParseNode::List {
438                        rows,
439                        lemma_type,
440                        element_type,
441                    } => {
442                        frames.push(ListParseFrame {
443                            rows: rows.iter(),
444                            lemma_type,
445                            element_type,
446                            converted: Vec::with_capacity(rows.len()),
447                        });
448                        ParseStep::NextChild
449                    }
450                }
451            }
452            ParseStep::NextChild => {
453                let frame = frames
454                    .last_mut()
455                    .expect("BUG: list parse frame stack empty while reading a child");
456                match frame.rows.next() {
457                    Some(child) => ParseStep::Parse(child, Arc::clone(&frame.element_type)),
458                    None => {
459                        let frame = frames
460                            .pop()
461                            .expect("BUG: list parse frame stack empty on completion");
462                        ParseStep::Deliver(TypedLiteral {
463                            value: ValueKind::List(frame.converted.into()),
464                            lemma_type: frame.lemma_type,
465                        })
466                    }
467                }
468            }
469            ParseStep::Deliver(literal) => match frames.last_mut() {
470                None => return Ok(literal),
471                Some(frame) => {
472                    frame.converted.push(literal);
473                    ParseStep::NextChild
474                }
475            },
476        };
477    }
478}
479
480enum ParseStep<'a> {
481    Parse(&'a EvaluateDataValue, Arc<LemmaType>),
482    NextChild,
483    Deliver(TypedLiteral),
484}
485
486struct ListParseFrame<'a> {
487    rows: std::slice::Iter<'a, EvaluateDataValue>,
488    lemma_type: Arc<LemmaType>,
489    element_type: Arc<LemmaType>,
490    converted: Vec<TypedLiteral>,
491}
492
493enum ParseNode<'a> {
494    Literal(TypedLiteral),
495    List {
496        rows: &'a [EvaluateDataValue],
497        lemma_type: Arc<LemmaType>,
498        element_type: Arc<LemmaType>,
499    },
500}
501
502/// Shape mismatches are request Errors from `evaluate_data_shape_error`, checked before parsing.
503/// The mismatch panics below are therefore unreachable.
504fn parse_node<'a>(
505    input: &'a EvaluateDataValue,
506    lemma_type: &Arc<LemmaType>,
507    source: &Source,
508    unit_index: &UnitIndex,
509    named_types: &IndexMap<String, Arc<LemmaType>>,
510) -> Result<ParseNode<'a>, Error> {
511    let type_spec = &lemma_type.specifications;
512
513    let (kind, binding_unit) = match (input, type_spec) {
514        (EvaluateDataValue::Literal(s), _) => {
515            return parse_literal(s, lemma_type, source, unit_index, named_types)
516                .map(ParseNode::Literal);
517        }
518        (EvaluateDataValue::Boolean(b), TypeSpecification::Boolean { .. }) => {
519            (ValueKind::Boolean(*b), None)
520        }
521        (EvaluateDataValue::Boolean(_), _) => {
522            panic!("BUG: boolean overlay on non-boolean type is a request shape error, not parse")
523        }
524        (EvaluateDataValue::Map(map), TypeSpecification::Measure { .. })
525        | (EvaluateDataValue::Map(map), TypeSpecification::Ratio { .. }) => {
526            let (magnitude, unit) = single_unit_literal_map(map).expect(
527                "BUG: measure/ratio map overlay is a request shape error unless it is a single-unit Literal",
528            );
529            return parse_literal(
530                &format!("{magnitude} {unit}"),
531                lemma_type,
532                source,
533                unit_index,
534                named_types,
535            )
536            .map(ParseNode::Literal);
537        }
538        (EvaluateDataValue::Map(_), _) => {
539            panic!("BUG: map overlay on this type is a request shape error, not parse")
540        }
541        (EvaluateDataValue::List(rows), TypeSpecification::List { .. }) => {
542            return Ok(ParseNode::List {
543                rows: rows.as_slice(),
544                lemma_type: Arc::clone(lemma_type),
545                element_type: Arc::clone(lemma_type.list_element_lemma_type()),
546            });
547        }
548        (EvaluateDataValue::List(_), _) => {
549            panic!("BUG: list overlay on this type is a request shape error, not parse")
550        }
551    };
552
553    Ok(ParseNode::Literal(typed_literal_with_binding_unit(
554        kind,
555        binding_unit,
556        lemma_type,
557    )))
558}
559
560fn parse_literal(
561    text: &str,
562    lemma_type: &Arc<LemmaType>,
563    source: &Source,
564    unit_index: &UnitIndex,
565    named_types: &IndexMap<String, Arc<LemmaType>>,
566) -> Result<TypedLiteral, Error> {
567    let parsed = parse_value_from_string(text, &lemma_type.specifications, source)?;
568    typed_literal_from_parsed(&parsed, lemma_type, unit_index, named_types)
569        .map_err(|msg| Error::validation(msg, Some(source.clone()), None::<String>))
570}
571
572/// Resolve a unit the caller actually wrote to a bare declared name.
573///
574/// Qualified paths go through [`UnitIndex::resolve_with_named_types`]. Bare names
575/// accepted only when this data type (or range element) declares that exact name.
576/// Never invents a unit the caller did not pass.
577fn single_unit_literal_map(map: &IndexMap<String, EvaluateDataValue>) -> Option<(&str, &str)> {
578    if map.len() != 1 {
579        return None;
580    }
581    let (unit, value) = map.iter().next()?;
582    match value {
583        EvaluateDataValue::Literal(magnitude) => Some((magnitude.as_str(), unit.as_str())),
584        _ => None,
585    }
586}
587
588enum ShapeCheck<'a> {
589    Accepts,
590    Mismatch(String),
591    Rows {
592        rows: &'a [EvaluateDataValue],
593        element: &'a LemmaType,
594    },
595}
596
597fn shape_check<'a>(input: &'a EvaluateDataValue, lemma_type: &'a LemmaType) -> ShapeCheck<'a> {
598    let type_spec = &lemma_type.specifications;
599    match (input, type_spec) {
600        (EvaluateDataValue::Literal(_), TypeSpecification::List { .. }) => ShapeCheck::Mismatch(
601            format!("literal overlay is not valid for list data ({type_spec})"),
602        ),
603        (EvaluateDataValue::Literal(_), _) => ShapeCheck::Accepts,
604        (EvaluateDataValue::Boolean(_), TypeSpecification::Boolean { .. }) => ShapeCheck::Accepts,
605        (EvaluateDataValue::Boolean(_), _) => ShapeCheck::Mismatch(format!(
606            "boolean input is only valid for boolean data, not {type_spec}"
607        )),
608        (
609            EvaluateDataValue::Map(map),
610            TypeSpecification::Measure { .. } | TypeSpecification::Ratio { .. },
611        ) => {
612            if single_unit_literal_map(map).is_some() {
613                ShapeCheck::Accepts
614            } else {
615                ShapeCheck::Mismatch(
616                    "measure and ratio data must be a convenience string or a single-unit map"
617                        .to_string(),
618                )
619            }
620        }
621        (EvaluateDataValue::Map(_), _) => ShapeCheck::Mismatch(format!(
622            "object overlay is only valid for measure or ratio data, not {type_spec}"
623        )),
624        (EvaluateDataValue::List(rows), TypeSpecification::List { .. }) => ShapeCheck::Rows {
625            rows: rows.as_slice(),
626            element: lemma_type.list_element_lemma_type().as_ref(),
627        },
628        (EvaluateDataValue::List(_), _) => ShapeCheck::Mismatch(format!(
629            "array overlay is not valid for scalar data ({type_spec})"
630        )),
631    }
632}
633
634struct ShapeFrame<'a> {
635    rows: std::slice::Iter<'a, EvaluateDataValue>,
636    element: &'a LemmaType,
637    position: usize,
638}
639
640/// First shape mismatch in `input` against `lemma_type`, at any list depth. `None` if every
641/// value matches the type its parse node will use. List items are reported by 1-based position.
642fn evaluate_data_shape_error(input: &EvaluateDataValue, lemma_type: &LemmaType) -> Option<String> {
643    let mut frames: Vec<ShapeFrame<'_>> = Vec::new();
644    let mut pending = Some((input, lemma_type));
645    loop {
646        if let Some((value, ty)) = pending.take() {
647            match shape_check(value, ty) {
648                ShapeCheck::Accepts => {}
649                ShapeCheck::Mismatch(message) => return Some(item_path_message(&frames, message)),
650                ShapeCheck::Rows { rows, element } => frames.push(ShapeFrame {
651                    rows: rows.iter(),
652                    element,
653                    position: 0,
654                }),
655            }
656        }
657        let frame = frames.last_mut()?;
658        match frame.rows.next() {
659            Some(child) => {
660                frame.position += 1;
661                pending = Some((child, frame.element));
662            }
663            None => {
664                frames.pop();
665            }
666        }
667    }
668}
669
670fn item_path_message(frames: &[ShapeFrame<'_>], message: String) -> String {
671    if frames.is_empty() {
672        return message;
673    }
674    let positions: Vec<String> = frames.iter().map(|f| f.position.to_string()).collect();
675    format!("list item {}: {message}", positions.join(", item "))
676}
677
678/// Run-data values for fields rows leave unset, by the row as evaluation reads it and
679/// its template slot. Fields a row seals are rejected at resolve.
680#[derive(Debug, Clone, Default)]
681pub(crate) struct RowOverrides {
682    pub(crate) fields: IndexMap<InstanceKey, IndexMap<usize, OperationResult>>,
683    /// Stored lists, by owning row and list path, with a key field set by run data:
684    /// their store index does not hold the run-data keys.
685    pub(crate) keyed_lists: HashSet<(Option<InstanceKey>, DataPath)>,
686}
687
688impl RowOverrides {
689    fn extend(&mut self, other: RowOverrides) {
690        for (key, fields) in other.fields {
691            self.fields.entry(key).or_default().extend(fields);
692        }
693        self.keyed_lists.extend(other.keyed_lists);
694    }
695
696    /// These overrides, read on the rows of a level that is itself the row `owner`.
697    ///
698    /// Keys already under a [`RowOrigin::Numbered`] row stay as-is: nested list
699    /// bindings rebased under an inner Numbered row match how [`ListValue::Values`]
700    /// looks them up (`owner: None` on that Numbered row). Re-owning them under an
701    /// outer Numbered row would miss.
702    fn under(self, owner: &InstanceKey) -> RowOverrides {
703        RowOverrides {
704            fields: self
705                .fields
706                .into_iter()
707                .map(|(key, fields)| {
708                    let key = if instance_key_has_numbered_origin(&key) {
709                        key
710                    } else {
711                        key.under(owner)
712                    };
713                    (key, fields)
714                })
715                .collect(),
716            keyed_lists: self
717                .keyed_lists
718                .into_iter()
719                .map(|(list_owner, path)| {
720                    let list_owner = match list_owner {
721                        Some(key) if instance_key_has_numbered_origin(&key) => key,
722                        Some(key) => key.under(owner),
723                        None => owner.clone(),
724                    };
725                    (Some(list_owner), path)
726                })
727                .collect(),
728        }
729    }
730}
731
732fn instance_key_has_numbered_origin(key: &InstanceKey) -> bool {
733    let mut current = Some(key);
734    while let Some(instance) = current {
735        if matches!(instance.row.origin, RowOrigin::Numbered { .. }) {
736            return true;
737        }
738        current = instance.owner();
739    }
740    false
741}
742
743/// User-provided data values resolved against a plan's type declarations.
744///
745/// Lightweight and cheap to construct — no plan cloning required. The
746/// [`ExecutionPlan`] stays immutable; callers pass `(&ExecutionPlan, &EvaluateData)`
747/// to evaluation and show paths.
748#[derive(Debug, Clone, Default)]
749pub struct EvaluateData {
750    /// Caller bindings by plan data slot, in caller order: successful literals or Veto
751    /// (bad override) per Data.
752    pub bindings: IndexMap<usize, OperationResult>,
753    /// Caller-supplied fields of rows the plan holds: `uses` rows (`i.x`, `i.j.x`) and
754    /// spec-written list rows (`staff.1.salary`, `i.staff.2.x`).
755    pub(crate) row_overrides: RowOverrides,
756    /// Input keys that did not match any plan Data (including Import aliases).
757    pub ignored_unknown: Vec<String>,
758}
759
760impl EvaluateData {
761    /// Parse and validate caller-supplied values against the plan's data declarations.
762    ///
763    /// Unknown keys and Import aliases are ignored (recorded in [`Self::ignored_unknown`]).
764    /// Parse, constraint, options, decimals, and input-oversize failures bind that Data
765    /// as [`OperationResult::Veto`]; evaluation still runs. Duplicate canonical keys Error.
766    pub fn resolve(
767        plan: &ExecutionPlan,
768        raw_values: IndexMap<String, EvaluateDataValue>,
769        limits: &ResourceLimits,
770    ) -> Result<Self, Error> {
771        Self::resolve_level(plan, raw_values, limits, ListLevel::Top)
772    }
773
774    fn resolve_level(
775        plan: &ExecutionPlan,
776        raw_values: IndexMap<String, EvaluateDataValue>,
777        limits: &ResourceLimits,
778        level: ListLevel,
779    ) -> Result<Self, Error> {
780        if level == ListLevel::Top {
781            reject_whole_list_with_row_keys(plan, &raw_values)?;
782        }
783        match run_resolve(ResolveFrame::Level(LevelFrame::new(
784            plan, None, raw_values, limits, level,
785        )))? {
786            ResolveOutput::Level(evaluate_data) => Ok(evaluate_data),
787            ResolveOutput::Binding(..) => {
788                panic!("BUG: root level frame returned a list binding")
789            }
790        }
791    }
792}
793
794enum ResolveOutput {
795    Level(EvaluateData),
796    /// A list value, with the run-data fields its rows' nested rows take.
797    Binding(OperationResult, RowOverrides),
798}
799
800enum ResolveStep<'a> {
801    Call(ResolveFrame<'a>),
802    Return(ResolveOutput),
803}
804
805enum ResolveFrame<'a> {
806    Level(LevelFrame<'a>),
807    List(ListFrame<'a>),
808}
809
810impl<'a> ResolveFrame<'a> {
811    fn resume(&mut self, input: Option<ResolveOutput>) -> Result<ResolveStep<'a>, Error> {
812        match self {
813            ResolveFrame::Level(frame) => frame.resume(input),
814            ResolveFrame::List(frame) => frame.resume(input),
815        }
816    }
817}
818
819fn run_resolve(root: ResolveFrame<'_>) -> Result<ResolveOutput, Error> {
820    let mut frames = vec![root];
821    let mut input = None;
822    loop {
823        let frame = frames.last_mut().expect("BUG: resolve frame stack empty");
824        match frame.resume(input.take())? {
825            ResolveStep::Call(child) => frames.push(child),
826            ResolveStep::Return(output) => {
827                frames.pop();
828                if frames.is_empty() {
829                    return Ok(output);
830                }
831                input = Some(output);
832            }
833        }
834    }
835}
836
837/// One level of run data: its entries in caller order. A list entry suspends the level
838/// until its rows are resolved.
839struct LevelFrame<'a> {
840    plan: &'a ExecutionPlan,
841    /// Evaluation-data key of the run-data row this level writes (`items.1`); `None`
842    /// for the request's plan.
843    row_key: Option<Arc<str>>,
844    limits: &'a ResourceLimits,
845    level: ListLevel,
846    entries: indexmap::map::IntoIter<String, EvaluateDataValue>,
847    seen_canonical: HashSet<String>,
848    evaluate_data: EvaluateData,
849    awaiting: Option<AwaitBinding>,
850}
851
852/// Where a suspended level frame puts the binding a nested list frame returns:
853/// a plan data slot, or one field of a row the plan holds.
854enum AwaitBinding {
855    Data { slot: usize },
856    RowField { key: InstanceKey, slot: usize },
857}
858
859impl<'a> LevelFrame<'a> {
860    fn new(
861        plan: &'a ExecutionPlan,
862        row_key: Option<Arc<str>>,
863        raw_values: IndexMap<String, EvaluateDataValue>,
864        limits: &'a ResourceLimits,
865        level: ListLevel,
866    ) -> Self {
867        let capacity = raw_values.len();
868        Self {
869            plan,
870            row_key,
871            limits,
872            level,
873            entries: raw_values.into_iter(),
874            seen_canonical: HashSet::with_capacity(capacity),
875            evaluate_data: EvaluateData {
876                bindings: IndexMap::with_capacity(capacity),
877                ..EvaluateData::default()
878            },
879            awaiting: None,
880        }
881    }
882
883    fn resume(&mut self, input: Option<ResolveOutput>) -> Result<ResolveStep<'a>, Error> {
884        match (self.awaiting.take(), input) {
885            (None, None) => {}
886            (
887                Some(AwaitBinding::Data { slot }),
888                Some(ResolveOutput::Binding(binding, overrides)),
889            ) => {
890                self.evaluate_data.bindings.insert(slot, binding);
891                self.evaluate_data.row_overrides.extend(overrides);
892            }
893            (
894                Some(AwaitBinding::RowField { key, slot }),
895                Some(ResolveOutput::Binding(binding, overrides)),
896            ) => {
897                self.evaluate_data
898                    .row_overrides
899                    .fields
900                    .entry(key)
901                    .or_default()
902                    .insert(slot, binding);
903                self.evaluate_data.row_overrides.extend(overrides);
904            }
905            _ => panic!("BUG: level frame resumed with an unexpected output"),
906        }
907        self.advance()
908    }
909
910    fn advance(&mut self) -> Result<ResolveStep<'a>, Error> {
911        let plan = self.plan;
912        let limits = self.limits;
913        let level = self.level;
914        let evaluate_data = &mut self.evaluate_data;
915        let seen_canonical = &mut self.seen_canonical;
916
917        for (name, raw_value) in self.entries.by_ref() {
918            let canonical = canonical_logical_name(&name);
919            if seen_canonical.contains(canonical.as_ref()) {
920                let canonical = canonical.into_owned();
921                return Err(Error::request(
922                    format!("Duplicate data key '{canonical}'"),
923                    Some("Data keys are case-insensitive; remove the duplicate"),
924                )
925                .with_related_data(name));
926            }
927            seen_canonical.insert(canonical.to_string());
928
929            let Some((slot, data_path, data_definition)) =
930                resolve_evaluate_data_target(plan, &canonical)
931            else {
932                let row_field = match resolve_row_field_key(plan, &canonical) {
933                    RowKeyLookup::Field(row_field) => row_field,
934                    RowKeyLookup::Unknown => {
935                        evaluate_data.ignored_unknown.push(name);
936                        continue;
937                    }
938                    RowKeyLookup::Invalid(error) => return Err(error.with_related_data(name)),
939                };
940                if matches!(row_field.definition, DataDefinition::Import { .. }) {
941                    if matches!(
942                        raw_value,
943                        EvaluateDataValue::Map(_) | EvaluateDataValue::List(_)
944                    ) {
945                        return Err(Error::request(
946                            format!(
947                                "data '{name}': '{name}' is a uses alias; objects and arrays are not a nesting level"
948                            ),
949                            Some(format!("bind fields with dotted keys, e.g. '{name}.<field>'")),
950                        )
951                        .with_related_data(name));
952                    }
953                    evaluate_data.ignored_unknown.push(name);
954                    continue;
955                }
956                if row_field.sealed {
957                    return Err(Error::request(
958                        format!(
959                            "data '{name}' is sealed by `uses -> with` and cannot be supplied at evaluate"
960                        ),
961                        Some("remove this key, or change the `uses` binding if the value should be caller-owned data"),
962                    )
963                    .with_related_data(name));
964                }
965                if let Some(list) = row_field.keyed_list {
966                    evaluate_data.row_overrides.keyed_lists.insert(list);
967                }
968                if matches!(row_field.definition, DataDefinition::InstanceList { .. }) {
969                    self.awaiting = Some(AwaitBinding::RowField {
970                        key: row_field.key,
971                        slot: row_field.slot,
972                    });
973                    return Ok(ResolveStep::Call(ResolveFrame::List(ListFrame {
974                        plan: row_field.template,
975                        path: row_field.field,
976                        list_key: canonical.into_owned(),
977                        under: self.row_key.clone(),
978                        name,
979                        overrides: RowOverrides::default(),
980                        limits,
981                        level,
982                        raw_value: Some(raw_value),
983                        rows: None,
984                    })));
985                }
986                let type_arc = Arc::clone(
987                    row_field
988                        .definition
989                        .resolved_type_arc()
990                        .expect("BUG: scalar row field is typed template data"),
991                );
992                let binding = resolve_scalar_binding(
993                    plan,
994                    &name,
995                    &canonical,
996                    &raw_value,
997                    &type_arc,
998                    row_field.definition.source(),
999                    limits,
1000                )?;
1001                evaluate_data
1002                    .row_overrides
1003                    .fields
1004                    .entry(row_field.key)
1005                    .or_default()
1006                    .insert(row_field.slot, binding);
1007                continue;
1008            };
1009
1010            if data_definition.is_bound() {
1011                return Err(Error::request(
1012                    format!(
1013                        "data '{name}' is sealed by `uses -> with` and cannot be supplied at evaluate"
1014                    ),
1015                    Some("remove this key, or change the `uses` binding if the value should be caller-owned data"),
1016                )
1017                .with_related_data(name));
1018            }
1019
1020            let data_source = data_definition.source();
1021            let type_arc = match data_definition {
1022                DataDefinition::TypeDeclaration { resolved_type, .. }
1023                | DataDefinition::Value { resolved_type, .. }
1024                | DataDefinition::Reference { resolved_type, .. } => Arc::clone(resolved_type),
1025                DataDefinition::Import { .. } => {
1026                    if matches!(
1027                        raw_value,
1028                        EvaluateDataValue::Map(_) | EvaluateDataValue::List(_)
1029                    ) {
1030                        return Err(Error::request(
1031                            format!(
1032                                "data '{name}': '{name}' is a uses alias; objects and arrays are not a nesting level"
1033                            ),
1034                            Some(format!("bind fields with dotted keys, e.g. '{name}.<field>'")),
1035                        )
1036                        .with_related_data(name));
1037                    }
1038                    evaluate_data.ignored_unknown.push(name);
1039                    continue;
1040                }
1041                DataDefinition::InstanceList { .. } => {
1042                    self.awaiting = Some(AwaitBinding::Data { slot });
1043                    return Ok(ResolveStep::Call(ResolveFrame::List(ListFrame {
1044                        plan,
1045                        path: data_path,
1046                        list_key: data_path.input_key(),
1047                        under: self.row_key.clone(),
1048                        name,
1049                        overrides: RowOverrides::default(),
1050                        limits,
1051                        level,
1052                        raw_value: Some(raw_value),
1053                        rows: None,
1054                    })));
1055                }
1056            };
1057
1058            let binding = resolve_scalar_binding(
1059                plan,
1060                &name,
1061                &canonical,
1062                &raw_value,
1063                &type_arc,
1064                data_source,
1065                limits,
1066            )?;
1067            evaluate_data.bindings.insert(slot, binding);
1068        }
1069
1070        Ok(ResolveStep::Return(ResolveOutput::Level(std::mem::take(
1071            evaluate_data,
1072        ))))
1073    }
1074}
1075
1076/// One caller value against its slot type: parse, size-check and validate it. The
1077/// result to bind — the parsed value or a veto for a bad overlay; structural
1078/// failures (shape, too many rows) are request errors.
1079fn resolve_scalar_binding(
1080    plan: &ExecutionPlan,
1081    name: &str,
1082    input_key: &str,
1083    raw_value: &EvaluateDataValue,
1084    type_arc: &Arc<LemmaType>,
1085    data_source: &Source,
1086    limits: &ResourceLimits,
1087) -> Result<OperationResult, Error> {
1088    if raw_value.is_empty() && type_arc.empty_runtime_input_vetoes() {
1089        return Ok(OperationResult::Veto(VetoType::computation(format!(
1090            "Data {} cannot be empty.",
1091            type_arc.label_for_data_input(input_key)
1092        ))));
1093    }
1094
1095    if let Some(message) = evaluate_data_shape_error(raw_value, type_arc) {
1096        return Err(Error::request(
1097            format!("data '{name}': {message}"),
1098            Some(format!("fix the overlay for '{name}'")),
1099        )
1100        .with_related_data(name));
1101    }
1102
1103    let typed = match parse_data_value(
1104        raw_value,
1105        type_arc,
1106        data_source,
1107        plan.expression_unit_index(),
1108        &plan.resolved_types.resolved,
1109    ) {
1110        Ok(value) => value,
1111        Err(error) => {
1112            return Ok(OperationResult::Veto(VetoType::computation(
1113                type_arc.data_veto_message(input_key, error.message()),
1114            )));
1115        }
1116    };
1117
1118    if let ValueKind::List(rows) = &typed.value {
1119        if rows.len() > limits.max_list_rows {
1120            return Err(Error::request(
1121                format!(
1122                    "data '{name}' has {} rows, over max_list_rows {}",
1123                    rows.len(),
1124                    limits.max_list_rows
1125                ),
1126                Some("reduce the list size or raise max_list_rows".to_string()),
1127            )
1128            .with_related_data(name));
1129        }
1130    }
1131
1132    let literal_value = LiteralValue {
1133        value: typed.value.clone(),
1134    };
1135    let oversize = match &typed.value {
1136        ValueKind::List(items) => items
1137            .iter()
1138            .position(|item| item.byte_size() > limits.max_data_value_bytes)
1139            .map(|index| {
1140                format!(
1141                    "Data {} list item {} exceeds the size limit.",
1142                    type_arc.label_for_data_input(input_key),
1143                    index + 1
1144                )
1145            }),
1146        _ => (literal_value.byte_size() > limits.max_data_value_bytes).then(|| {
1147            format!(
1148                "Data {} exceeds the size limit.",
1149                type_arc.label_for_data_input(input_key)
1150            )
1151        }),
1152    };
1153    if let Some(reason) = oversize {
1154        return Ok(OperationResult::Veto(VetoType::computation(reason)));
1155    }
1156
1157    if let Err(message) = validate_value_against_type(
1158        typed.lemma_type.as_ref(),
1159        &literal_value,
1160        plan.expression_unit_index(),
1161        None,
1162    ) {
1163        return Ok(OperationResult::Veto(VetoType::computation(
1164            type_arc.data_veto_message(input_key, &message),
1165        )));
1166    }
1167
1168    Ok(OperationResult::from_bound(BoundValueKind {
1169        value: typed.value.clone(),
1170        unit: typed.lemma_type.unit().map(std::sync::Arc::from),
1171        instance_factors: None,
1172    }))
1173}
1174
1175/// Plan data slot and path matched by `input_key`: local data or data of a flattened
1176/// import. Fields of spec-written rows (`staff.1.salary`) are not plan data; they
1177/// resolve through [`resolve_row_field_key`].
1178fn resolve_evaluate_data_target<'a>(
1179    plan: &'a ExecutionPlan,
1180    key: &str,
1181) -> Option<(usize, &'a DataPath, &'a DataDefinition)> {
1182    let path = plan.input_key_index.get(key)?;
1183    let (slot, path, definition) = plan
1184        .data
1185        .get_full(path)
1186        .unwrap_or_else(|| panic!("BUG: indexed input '{key}' has no data definition"));
1187    Some((slot, path, definition))
1188}
1189
1190/// A stored list as a row reads it: the row holding it (`None` for the plan) and its path.
1191type ListAddress = (Option<InstanceKey>, DataPath);
1192
1193/// A dotted key naming one field of a row the plan holds: a field of a `uses` row
1194/// (`i.x`, `i.j.x`) or of a spec-written list row (`staff.2.salary`, `i.staff.1.x`).
1195struct RowFieldKey<'a> {
1196    /// The row as evaluation reads it.
1197    key: InstanceKey,
1198    template: &'a ExecutionPlan,
1199    slot: usize,
1200    field: &'a DataPath,
1201    definition: &'a DataDefinition,
1202    /// The row binds the field itself.
1203    sealed: bool,
1204    /// The stored list whose index the field overrides: set when the row is a stored
1205    /// row and the field is one of its list's key fields.
1206    keyed_list: Option<ListAddress>,
1207    /// Every list the key steps through a row of, with its dotted key.
1208    lists: Vec<(ListAddress, String)>,
1209}
1210
1211enum RowKeyLookup<'a> {
1212    Field(RowFieldKey<'a>),
1213    /// No row field of the plan: an ignorable key.
1214    Unknown,
1215    /// A key under a list that names no row or field of it: the caller addressed a row
1216    /// that is not there, or a field those rows do not have.
1217    Invalid(Error),
1218}
1219
1220/// `i.j.x`, `staff.2.salary`, `i.staff.1.x` → the field of the row the key names,
1221/// stepping through `uses` slots and the rows of stored lists from the plan.
1222fn resolve_row_field_key<'a>(plan: &'a ExecutionPlan, key: &str) -> RowKeyLookup<'a> {
1223    let parts: Vec<&str> = key.split('.').collect();
1224    let mut template = plan;
1225    let mut holder: Option<InstanceKey> = None;
1226    let mut keyed: Option<(ListAddress, &'a ListKey)> = None;
1227    let mut lists: Vec<(ListAddress, String)> = Vec::new();
1228    let mut index = 0;
1229    loop {
1230        let Some(path) = template.input_key_index.get(parts[index]) else {
1231            return unknown_under_list(key, &lists);
1232        };
1233        let (slot, _, definition) = template
1234            .data
1235            .get_full(path)
1236            .expect("BUG: indexed input key has a data definition");
1237        let row = holder.as_ref().map(|key| key.row.as_ref());
1238        if index + 1 == parts.len() {
1239            if matches!(definition, DataDefinition::Import { .. }) && !lists.is_empty() {
1240                return unknown_under_list(key, &lists);
1241            }
1242            let Some(key) = holder else {
1243                return RowKeyLookup::Unknown;
1244            };
1245            let keyed_list = keyed.and_then(|(list, list_key)| match list_key {
1246                ListKey::Fields(fields) => fields
1247                    .iter()
1248                    .any(|field| field.path == *path)
1249                    .then_some(list),
1250                ListKey::Numbered => None,
1251            });
1252            return RowKeyLookup::Field(RowFieldKey {
1253                sealed: key.row.bindings[slot].is_some(),
1254                key,
1255                template,
1256                slot,
1257                field: path,
1258                definition,
1259                keyed_list,
1260                lists,
1261            });
1262        }
1263        match definition {
1264            DataDefinition::Import { .. } => {
1265                let import = Arc::clone(template.import_row(row, path, None));
1266                template = template.row_template(&import.identity);
1267                holder = Some(InstanceKey::new(import, holder));
1268                keyed = None;
1269                index += 1;
1270            }
1271            DataDefinition::InstanceList { .. } => {
1272                let list_key = parts[..=index].join(".");
1273                let source = template
1274                    .instance_lists
1275                    .get(path)
1276                    .unwrap_or_else(|| panic!("BUG: instance list '{list_key}' has no source"));
1277                let (rows, owner) = match row.and_then(|row| row.bindings[slot].as_ref()) {
1278                    None => (source.store.rows.as_slice(), holder.clone()),
1279                    Some(RowBinding::Rows { rows, hops, .. }) => {
1280                        let holder = holder.as_ref().expect("BUG: a bound list is a row field");
1281                        (rows.as_slice(), holder.scope(*hops).cloned())
1282                    }
1283                    Some(
1284                        RowBinding::Bound(_)
1285                        | RowBinding::Deferred { .. }
1286                        | RowBinding::Instance(_),
1287                    ) => {
1288                        return RowKeyLookup::Invalid(Error::request(
1289                            format!(
1290                                "data '{key}' is sealed by `uses -> with` and cannot be supplied at evaluate"
1291                            ),
1292                            Some(format!(
1293                                "'{list_key}' is bound by `uses -> with`; its rows take no run data"
1294                            )),
1295                        ));
1296                    }
1297                };
1298                let rest = &parts[index + 1..];
1299                let number = match rest.first().map(|row| row.parse::<usize>()) {
1300                    Some(Ok(number)) if rest.len() > 1 => number,
1301                    _ => {
1302                        return RowKeyLookup::Invalid(no_list_data(
1303                            key,
1304                            &list_key,
1305                            format!("fields of a row of '{list_key}' are keyed '{list_key}.<row>.<field>'"),
1306                        ));
1307                    }
1308                };
1309                if number == 0 || number > rows.len() {
1310                    return RowKeyLookup::Invalid(no_list_data(
1311                        key,
1312                        &list_key,
1313                        format!(
1314                            "'{list_key}' has {} spec-written row(s); rows are numbered from 1",
1315                            rows.len()
1316                        ),
1317                    ));
1318                }
1319                let list = (owner.clone(), path.clone());
1320                lists.push((list.clone(), list_key));
1321                keyed = match row.and_then(|row| row.bindings[slot].as_ref()) {
1322                    None => Some((list, &source.key)),
1323                    Some(_) => None,
1324                };
1325                let listed = Arc::clone(&rows[number - 1]);
1326                template = template.row_template(&listed.identity);
1327                holder = Some(InstanceKey::new(listed, owner));
1328                index += 2;
1329            }
1330            DataDefinition::Value { .. }
1331            | DataDefinition::TypeDeclaration { .. }
1332            | DataDefinition::Reference { .. } => {
1333                return unknown_under_list(key, &lists);
1334            }
1335        }
1336    }
1337}
1338
1339/// Template, definition, instance and field path of a `uses` instance field a dotted
1340/// run-data key names (`i.x`, `i.j.x`); `None` for any other key.
1341pub(crate) fn row_field_definition<'a>(
1342    plan: &'a ExecutionPlan,
1343    key: &str,
1344) -> Option<(
1345    &'a ExecutionPlan,
1346    &'a DataDefinition,
1347    InstanceKey,
1348    &'a DataPath,
1349)> {
1350    match resolve_row_field_key(plan, key) {
1351        RowKeyLookup::Field(row_field) if row_field.lists.is_empty() => Some((
1352            row_field.template,
1353            row_field.definition,
1354            row_field.key,
1355            row_field.field,
1356        )),
1357        RowKeyLookup::Field(_) => None,
1358        RowKeyLookup::Unknown | RowKeyLookup::Invalid(_) => None,
1359    }
1360}
1361
1362/// A key that names nothing past its last list row (`items.1.bonus`): a request error
1363/// under a list, an ignorable key otherwise.
1364fn unknown_under_list<'a>(key: &str, lists: &[(ListAddress, String)]) -> RowKeyLookup<'a> {
1365    match lists.last() {
1366        Some((_, list_key)) => {
1367            let field = key[list_key.len() + 1..]
1368                .split_once('.')
1369                .map_or("", |(_, field)| field);
1370            RowKeyLookup::Invalid(no_list_data(
1371                key,
1372                list_key,
1373                format!("rows of '{list_key}' have no field '{field}'"),
1374            ))
1375        }
1376        None => RowKeyLookup::Unknown,
1377    }
1378}
1379
1380fn no_list_data(key: &str, list_key: &str, hint: String) -> Error {
1381    Error::request(
1382        format!("data '{key}' names no data of '{list_key}'"),
1383        Some(hint),
1384    )
1385}
1386
1387/// A request that replaces a list as a whole and also supplies a field of one of its
1388/// spec-written rows is contradictory: the whole list has no such row.
1389fn reject_whole_list_with_row_keys(
1390    plan: &ExecutionPlan,
1391    raw_values: &IndexMap<String, EvaluateDataValue>,
1392) -> Result<(), Error> {
1393    let mut whole_lists: HashSet<ListAddress> = HashSet::new();
1394    let mut row_fields: Vec<(&String, Vec<(ListAddress, String)>)> = Vec::new();
1395    for name in raw_values.keys() {
1396        let canonical = canonical_logical_name(name);
1397        if let Some((_, path, definition)) = resolve_evaluate_data_target(plan, &canonical) {
1398            if matches!(definition, DataDefinition::InstanceList { .. }) {
1399                whole_lists.insert((None, path.clone()));
1400            }
1401            continue;
1402        }
1403        if let RowKeyLookup::Field(row_field) = resolve_row_field_key(plan, &canonical) {
1404            if matches!(row_field.definition, DataDefinition::InstanceList { .. }) {
1405                whole_lists.insert((Some(row_field.key.clone()), row_field.field.clone()));
1406            }
1407            row_fields.push((name, row_field.lists));
1408        }
1409    }
1410    for (name, lists) in row_fields {
1411        for (list, list_key) in lists {
1412            if whole_lists.contains(&list) {
1413                return Err(Error::request(
1414                    format!(
1415                        "data '{list_key}': cannot supply both the whole list and dotted row keys such as '{name}'"
1416                    ),
1417                    Some(format!(
1418                        "pass either '{list_key}' as an array, or only dotted keys under '{list_key}'"
1419                    )),
1420                )
1421                .with_related_data(list_key));
1422            }
1423        }
1424    }
1425    Ok(())
1426}
1427
1428/// Whether a run-data list value is a top-level one, whose whole row tree is checked
1429/// against the list limits, or rows nested inside one already checked.
1430#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1431enum ListLevel {
1432    Top,
1433    Nested,
1434}
1435
1436/// Rows of one top-level list value across all its levels, `template` being the plan
1437/// its rows read. Over `max_list_nodes` rows in total, or rows nesting deeper than
1438/// `max_list_depth` levels (the spec holding the list is level 1), is a request Error.
1439/// Node count is checked first so a wide tree reports its size, not its depth.
1440fn check_list_tree(
1441    template: &ExecutionPlan,
1442    raw_rows: &[EvaluateDataValue],
1443    limits: &ResourceLimits,
1444) -> Result<(), String> {
1445    let mut stack: Vec<(&ExecutionPlan, &EvaluateDataValue, usize)> =
1446        raw_rows.iter().map(|row| (template, row, 2)).collect();
1447    let mut nodes = 0usize;
1448    let mut deepest = 0usize;
1449    while let Some((row_template, raw_row, depth)) = stack.pop() {
1450        nodes += 1;
1451        if nodes > limits.max_list_nodes {
1452            return Err(format!(
1453                "has more than max_list_nodes {} rows across all levels",
1454                limits.max_list_nodes
1455            ));
1456        }
1457        deepest = deepest.max(depth);
1458        let EvaluateDataValue::Map(fields) = raw_row else {
1459            continue;
1460        };
1461        for (field, value) in fields {
1462            let EvaluateDataValue::List(rows) = value else {
1463                continue;
1464            };
1465            let Some(source) = row_template
1466                .input_key_index
1467                .get(canonical_logical_name(field).as_ref())
1468                .and_then(|path| row_template.instance_lists.get(path))
1469            else {
1470                continue;
1471            };
1472            let nested = row_template.row_template(&source.identity);
1473            stack.extend(rows.iter().map(|row| (nested, row, depth + 1)));
1474        }
1475    }
1476    if deepest > limits.max_list_depth {
1477        return Err(format!(
1478            "rows nest {deepest} levels deep counting the spec itself, over max_list_depth {}",
1479            limits.max_list_depth
1480        ));
1481    }
1482    Ok(())
1483}
1484
1485/// Frame of one instance list entry: validates the list, then resolves each row through
1486/// a level frame of the row template. A vetoed field, duplicate key or overlapping key
1487/// range resolves the list to a veto.
1488struct ListFrame<'a> {
1489    plan: &'a ExecutionPlan,
1490    path: &'a DataPath,
1491    /// Dotted key of the list from the plan of its level, naming its rows.
1492    list_key: String,
1493    /// Evaluation-data key of the run-data row holding the list; `None` for a list of
1494    /// the request's plan.
1495    under: Option<Arc<str>>,
1496    name: String,
1497    /// Run-data fields of the rows' nested rows, keyed under the rows.
1498    overrides: RowOverrides,
1499    limits: &'a ResourceLimits,
1500    level: ListLevel,
1501    raw_value: Option<EvaluateDataValue>,
1502    rows: Option<ListRows<'a>>,
1503}
1504
1505struct ListRows<'a> {
1506    source: &'a InstanceListSource,
1507    /// Row spec identity, shared by the rows.
1508    identity: Arc<SpecIdentity>,
1509    template: &'a ExecutionPlan,
1510    row_type: Arc<LemmaType>,
1511    raw_rows: std::vec::IntoIter<EvaluateDataValue>,
1512    number: usize,
1513    literals: Vec<TypedLiteral>,
1514    /// Template data slot and type of each key field, in key order.
1515    key_slots: Box<[KeySlot<'a>]>,
1516    keyed: Vec<Vec<TypedLiteral>>,
1517}
1518
1519struct KeySlot<'a> {
1520    field: &'a KeyField,
1521    slot: usize,
1522    field_type: Arc<LemmaType>,
1523}
1524
1525fn request_error(name: &str, message: String) -> Error {
1526    Error::request(format!("data '{name}': {message}"), None::<String>).with_related_data(name)
1527}
1528
1529impl<'a> ListFrame<'a> {
1530    fn resume(&mut self, input: Option<ResolveOutput>) -> Result<ResolveStep<'a>, Error> {
1531        match self.rows.take() {
1532            None => {
1533                assert!(
1534                    input.is_none(),
1535                    "BUG: list frame started with a child output"
1536                );
1537                let raw_value = self
1538                    .raw_value
1539                    .take()
1540                    .expect("BUG: list frame started twice");
1541                let rows = self.start(raw_value)?;
1542                self.next_row(rows)
1543            }
1544            Some(mut rows) => {
1545                let Some(ResolveOutput::Level(row_data)) = input else {
1546                    panic!("BUG: list frame resumed without a row result");
1547                };
1548                match self.finish_row(&mut rows, row_data)? {
1549                    Some(veto) => Ok(ResolveStep::Return(ResolveOutput::Binding(
1550                        veto,
1551                        RowOverrides::default(),
1552                    ))),
1553                    None => self.next_row(rows),
1554                }
1555            }
1556        }
1557    }
1558
1559    fn start(&self, raw_value: EvaluateDataValue) -> Result<ListRows<'a>, Error> {
1560        let plan = self.plan;
1561        let source = plan
1562            .instance_lists
1563            .get(self.path)
1564            .expect("BUG: instance list data without a source");
1565        let template = plan.row_template(&source.identity);
1566        let Some(raw_rows) = raw_value.into_list() else {
1567            return Err(request_error(
1568                &self.name,
1569                format!(
1570                    "rows of a `{}` list are an array of objects",
1571                    source.identity.spec
1572                ),
1573            ));
1574        };
1575        if raw_rows.len() > self.limits.max_list_rows {
1576            return Err(request_error(
1577                &self.name,
1578                format!(
1579                    "has {} rows, over max_list_rows {}",
1580                    raw_rows.len(),
1581                    self.limits.max_list_rows
1582                ),
1583            ));
1584        }
1585        if self.level == ListLevel::Top {
1586            check_list_tree(template, &raw_rows, self.limits)
1587                .map_err(|message| request_error(&self.name, message))?;
1588        }
1589        let row_type = Arc::new(LemmaType::primitive(TypeSpecification::Spec {
1590            spec: source.identity.clone(),
1591        }));
1592        let key_slots = match &source.key {
1593            ListKey::Fields(key_fields) => key_fields
1594                .iter()
1595                .map(|field| KeySlot {
1596                    field,
1597                    slot: template
1598                        .data
1599                        .get_index_of(&field.path)
1600                        .expect("BUG: key field is template data"),
1601                    field_type: row_end_type(template, &RowEnd::Data(field.path.clone())),
1602                })
1603                .collect(),
1604            ListKey::Numbered => Box::default(),
1605        };
1606        Ok(ListRows {
1607            source,
1608            identity: Arc::new(source.identity.clone()),
1609            template,
1610            row_type,
1611            literals: Vec::with_capacity(raw_rows.len()),
1612            key_slots,
1613            keyed: Vec::new(),
1614            number: 0,
1615            raw_rows: raw_rows.into_iter(),
1616        })
1617    }
1618
1619    fn next_row(&mut self, mut rows: ListRows<'a>) -> Result<ResolveStep<'a>, Error> {
1620        let Some(raw_row) = rows.raw_rows.next() else {
1621            return Ok(ResolveStep::Return(ResolveOutput::Binding(
1622                self.finish_list(rows),
1623                std::mem::take(&mut self.overrides),
1624            )));
1625        };
1626        rows.number += 1;
1627        let Some(fields) = raw_row.into_map() else {
1628            return Err(request_error(
1629                &self.name,
1630                format!("row {} must be an object", rows.number),
1631            ));
1632        };
1633        let template = rows.template;
1634        let origin = crate::planning::semantics::numbered_row_origin(&self.list_key, rows.number);
1635        let row_key: Arc<str> = match &self.under {
1636            Some(under) => Arc::from(format!("{under}.{origin}")),
1637            None => Arc::from(origin),
1638        };
1639        self.rows = Some(rows);
1640        Ok(ResolveStep::Call(ResolveFrame::Level(LevelFrame::new(
1641            template,
1642            Some(row_key),
1643            fields,
1644            self.limits,
1645            ListLevel::Nested,
1646        ))))
1647    }
1648
1649    /// Adds the resolved row `rows.number`. `Some` is a veto of the whole list.
1650    fn finish_row(
1651        &mut self,
1652        rows: &mut ListRows<'a>,
1653        row_data: EvaluateData,
1654    ) -> Result<Option<OperationResult>, Error> {
1655        let number = rows.number;
1656        let name = self.name.as_str();
1657        let mut bindings: Vec<Option<RowBinding>> = vec![None; rows.template.data.len()];
1658        if let Some(veto) = row_data
1659            .row_overrides
1660            .fields
1661            .values()
1662            .flat_map(IndexMap::values)
1663            .find_map(|binding| match binding {
1664                OperationResult::Veto(veto) => Some(veto),
1665                OperationResult::Value(_) => None,
1666            })
1667        {
1668            return Ok(Some(OperationResult::Veto(VetoType::computation(format!(
1669                "In {name} row {number}: {veto}"
1670            )))));
1671        }
1672        for (slot, binding) in row_data.bindings {
1673            if let OperationResult::Veto(veto) = &binding {
1674                return Ok(Some(OperationResult::Veto(VetoType::computation(format!(
1675                    "In {name} row {number}: {veto}"
1676                )))));
1677            }
1678            bindings[slot] = Some(RowBinding::Bound(binding));
1679        }
1680        if let ListKey::Fields(_) = &rows.source.key {
1681            let mut values = Vec::with_capacity(rows.key_slots.len());
1682            for KeySlot {
1683                field: key_field,
1684                slot,
1685                field_type,
1686            } in &rows.key_slots
1687            {
1688                let key_path = &key_field.path;
1689                let value = match bindings.get(*slot).and_then(Option::as_ref) {
1690                    Some(RowBinding::Bound(OperationResult::Value(bound))) => {
1691                        typed_from_bound(bound, field_type)
1692                    }
1693                    Some(RowBinding::Bound(OperationResult::Veto(_))) => {
1694                        unreachable!("BUG: vetoed row binding returned above")
1695                    }
1696                    Some(
1697                        RowBinding::Deferred { .. }
1698                        | RowBinding::Rows { .. }
1699                        | RowBinding::Instance(_),
1700                    ) => {
1701                        unreachable!("BUG: evaluation data rows bind resolved values")
1702                    }
1703                    None => default_of(&rows.template.data, key_path).ok_or_else(|| {
1704                        request_error(
1705                            name,
1706                            format!(
1707                                "row {number} has no value for key field '{}'",
1708                                key_field.name
1709                            ),
1710                        )
1711                    })?,
1712                };
1713                values.push(value);
1714            }
1715            rows.keyed.push(values);
1716        }
1717        let literal = row_literal(
1718            Arc::clone(&rows.identity),
1719            RowOrigin::Numbered {
1720                list: self.list_key.clone(),
1721                number,
1722                under: self.under.clone(),
1723            },
1724            bindings,
1725            Arc::clone(&rows.row_type),
1726        );
1727        let ValueKind::Spec(SpecValue::Row(key)) = &literal.value else {
1728            unreachable!("BUG: a row literal is a row value");
1729        };
1730        self.overrides.extend(row_data.row_overrides.under(key));
1731        rows.literals.push(literal);
1732        Ok(None)
1733    }
1734
1735    fn finish_list(&self, rows: ListRows<'a>) -> OperationResult {
1736        let literals: Arc<[TypedLiteral]> = rows.literals.into();
1737        let list = match &rows.source.key {
1738            ListKey::Fields(key_fields) => {
1739                let field_names: Vec<String> = key_fields
1740                    .iter()
1741                    .map(|key_field| key_field.name.clone())
1742                    .collect();
1743                match KeyIndex::build_numbered(field_names, &rows.keyed) {
1744                    Ok(index) => ListValue::indexed(literals, Arc::new(index)),
1745                    Err(conflict) => {
1746                        return OperationResult::Veto(VetoType::computation(format!(
1747                            "{}: {conflict}",
1748                            self.name
1749                        )));
1750                    }
1751                }
1752            }
1753            ListKey::Numbered => ListValue::from(literals),
1754        };
1755        OperationResult::from_literal(LiteralValue {
1756            value: ValueKind::List(list),
1757        })
1758    }
1759}
1760
1761#[cfg(test)]
1762mod tests {
1763    use super::*;
1764    use crate::computation::rational::{decimal_to_rational, rational_new, rational_one};
1765    use crate::planning::semantics::{
1766        primitive_number_arc, MeasureUnit, MeasureUnits, RatioUnit, RatioUnits, TypeExtends,
1767    };
1768    use rust_decimal::Decimal;
1769
1770    fn dummy_source() -> Source {
1771        Source::new(
1772            crate::parsing::source::SourceType::Volatile,
1773            crate::parsing::ast::Span {
1774                start: 0,
1775                end: 0,
1776                line: 1,
1777                col: 1,
1778            },
1779        )
1780    }
1781
1782    fn mass_measure_type() -> Arc<LemmaType> {
1783        Arc::new(LemmaType::new(
1784            "Mass".to_string(),
1785            TypeSpecification::Measure {
1786                minimum: None,
1787                maximum: None,
1788                units: MeasureUnits::from(vec![
1789                    MeasureUnit {
1790                        name: "kilogram".to_string(),
1791                        scale: crate::literals::UnitScale::Literal(rational_one()),
1792                        derived_measure_factors: Vec::new(),
1793                        decomposition: crate::literals::BaseMeasureVector::new(),
1794                        minimum: None,
1795                        maximum: None,
1796                        suggestion_magnitude: None,
1797                    },
1798                    MeasureUnit {
1799                        name: "gram".to_string(),
1800                        scale: crate::literals::UnitScale::Literal(
1801                            decimal_to_rational(Decimal::new(1, 3)).expect("factor"),
1802                        ),
1803                        derived_measure_factors: Vec::new(),
1804                        decomposition: crate::literals::BaseMeasureVector::new(),
1805                        minimum: None,
1806                        maximum: None,
1807                        suggestion_magnitude: None,
1808                    },
1809                ]),
1810                traits: Vec::new(),
1811                decomposition: None,
1812                unit: None,
1813                help: String::new(),
1814            },
1815            TypeExtends::Primitive,
1816        ))
1817    }
1818
1819    fn ratio_with_percent_type() -> Arc<LemmaType> {
1820        Arc::new(LemmaType::new(
1821            "Rate".to_string(),
1822            TypeSpecification::Ratio {
1823                minimum: None,
1824                maximum: None,
1825                units: RatioUnits::from(vec![
1826                    RatioUnit {
1827                        name: "percent".to_string(),
1828                        value: decimal_to_rational(Decimal::new(100, 0)).expect("factor"),
1829                        minimum: None,
1830                        maximum: None,
1831                        suggestion_magnitude: None,
1832                    },
1833                    RatioUnit {
1834                        name: "fraction".to_string(),
1835                        value: rational_one(),
1836                        minimum: None,
1837                        maximum: None,
1838                        suggestion_magnitude: None,
1839                    },
1840                ]),
1841                unit: None,
1842                help: String::new(),
1843            },
1844            TypeExtends::Primitive,
1845        ))
1846    }
1847
1848    #[test]
1849    fn string_input_parsed_against_type() {
1850        let ty = primitive_number_arc();
1851        let lit = parse_data_value(
1852            &EvaluateDataValue::Literal("42".to_string()),
1853            ty,
1854            &dummy_source(),
1855            &UnitIndex::new(),
1856            &IndexMap::new(),
1857        )
1858        .unwrap();
1859        assert!(matches!(lit.value, ValueKind::Number(_)));
1860    }
1861
1862    fn unit_map(unit: &str, magnitude: &str) -> EvaluateDataValue {
1863        let mut map = IndexMap::new();
1864        map.insert(
1865            unit.to_string(),
1866            EvaluateDataValue::Literal(magnitude.into()),
1867        );
1868        EvaluateDataValue::Map(map)
1869    }
1870
1871    #[test]
1872    fn single_unit_measure_map_parses() {
1873        let ty = mass_measure_type();
1874        let lit = parse_data_value(
1875            &unit_map("kilogram", "2"),
1876            &ty,
1877            &dummy_source(),
1878            &UnitIndex::new(),
1879            &IndexMap::new(),
1880        )
1881        .unwrap();
1882        let ValueKind::Measure(magnitude) = &lit.value else {
1883            panic!("expected measure");
1884        };
1885        assert_eq!(magnitude, &rational_new(2, 1));
1886    }
1887
1888    #[test]
1889    fn single_unit_ratio_map_parses() {
1890        let ty = ratio_with_percent_type();
1891        let lit = parse_data_value(
1892            &unit_map("percent", "5"),
1893            &ty,
1894            &dummy_source(),
1895            &UnitIndex::new(),
1896            &IndexMap::new(),
1897        )
1898        .unwrap();
1899        let ValueKind::Ratio(canonical) = &lit.value else {
1900            panic!("expected ratio");
1901        };
1902        assert_eq!(
1903            *canonical,
1904            decimal_to_rational(Decimal::new(5, 2)).expect("canonical")
1905        );
1906    }
1907
1908    #[test]
1909    fn multi_key_map_is_shape_error() {
1910        let mut map = IndexMap::new();
1911        map.insert(
1912            "kilogram".to_string(),
1913            EvaluateDataValue::Literal("2".to_string()),
1914        );
1915        map.insert(
1916            "gram".to_string(),
1917            EvaluateDataValue::Literal("2000".into()),
1918        );
1919        let message = evaluate_data_shape_error(&EvaluateDataValue::Map(map), &mass_measure_type())
1920            .expect("shape");
1921        assert!(message.contains("single-unit"), "{message}");
1922    }
1923
1924    #[test]
1925    fn empty_map_is_shape_error() {
1926        let message = evaluate_data_shape_error(
1927            &EvaluateDataValue::Map(IndexMap::new()),
1928            &mass_measure_type(),
1929        )
1930        .expect("shape");
1931        assert!(message.contains("single-unit"), "{message}");
1932    }
1933
1934    #[test]
1935    fn map_on_text_is_shape_error() {
1936        let message = evaluate_data_shape_error(
1937            &unit_map("eur", "84"),
1938            &LemmaType::primitive(TypeSpecification::Text {
1939                length: None,
1940                help: String::new(),
1941            }),
1942        )
1943        .expect("shape");
1944        assert!(message.contains("text"), "{message}");
1945    }
1946
1947    #[test]
1948    fn list_on_scalar_is_shape_error() {
1949        let message = evaluate_data_shape_error(
1950            &EvaluateDataValue::List(vec![EvaluateDataValue::Literal("1".to_string())]),
1951            &LemmaType::primitive(TypeSpecification::Number {
1952                minimum: None,
1953                maximum: None,
1954                decimals: None,
1955                help: String::new(),
1956            }),
1957        )
1958        .expect("shape");
1959        assert!(message.contains("array"), "{message}");
1960    }
1961
1962    #[test]
1963    fn boolean_on_number_is_shape_error() {
1964        let message = evaluate_data_shape_error(
1965            &EvaluateDataValue::Boolean(true),
1966            &LemmaType::primitive(TypeSpecification::Number {
1967                minimum: None,
1968                maximum: None,
1969                decimals: None,
1970                help: String::new(),
1971            }),
1972        )
1973        .expect("shape");
1974        assert!(message.contains("boolean"), "{message}");
1975    }
1976
1977    fn mass_list_type() -> LemmaType {
1978        LemmaType::list_of(mass_measure_type())
1979    }
1980
1981    #[test]
1982    fn nested_boolean_item_is_shape_error() {
1983        let message = evaluate_data_shape_error(
1984            &EvaluateDataValue::List(vec![
1985                EvaluateDataValue::Literal("1 kilogram".to_string()),
1986                EvaluateDataValue::Boolean(true),
1987            ]),
1988            &mass_list_type(),
1989        )
1990        .expect("shape");
1991        assert!(
1992            message.starts_with("list item 2: boolean input is only valid for boolean data"),
1993            "{message}"
1994        );
1995    }
1996
1997    #[test]
1998    fn nested_map_item_is_shape_error() {
1999        let text_list =
2000            LemmaType::list_of(Arc::new(LemmaType::primitive(TypeSpecification::Text {
2001                length: None,
2002                help: String::new(),
2003            })));
2004        let message = evaluate_data_shape_error(
2005            &EvaluateDataValue::List(vec![unit_map("eur", "84")]),
2006            &text_list,
2007        )
2008        .expect("shape");
2009        assert!(
2010            message.starts_with("list item 1: object overlay"),
2011            "{message}"
2012        );
2013    }
2014
2015    #[test]
2016    fn nested_list_item_is_shape_error() {
2017        let message = evaluate_data_shape_error(
2018            &EvaluateDataValue::List(vec![EvaluateDataValue::List(vec![
2019                EvaluateDataValue::Literal("1 eur".to_string()),
2020            ])]),
2021            &mass_list_type(),
2022        )
2023        .expect("shape");
2024        assert!(
2025            message.starts_with("list item 1: array overlay"),
2026            "{message}"
2027        );
2028    }
2029
2030    #[test]
2031    fn deeply_nested_item_reports_full_item_path() {
2032        let nested = LemmaType::list_of(Arc::new(mass_list_type()));
2033        let message = evaluate_data_shape_error(
2034            &EvaluateDataValue::List(vec![
2035                EvaluateDataValue::List(vec![EvaluateDataValue::Literal("1 kilogram".to_string())]),
2036                EvaluateDataValue::List(vec![EvaluateDataValue::Boolean(true)]),
2037            ]),
2038            &nested,
2039        )
2040        .expect("shape");
2041        assert!(
2042            message
2043                .starts_with("list item 2, item 1: boolean input is only valid for boolean data"),
2044            "{message}"
2045        );
2046    }
2047
2048    #[test]
2049    fn nested_literal_under_list_type_is_shape_error() {
2050        let nested = LemmaType::list_of(Arc::new(mass_list_type()));
2051        let message = evaluate_data_shape_error(
2052            &EvaluateDataValue::List(vec![EvaluateDataValue::Literal("1 kilogram".to_string())]),
2053            &nested,
2054        )
2055        .expect("shape");
2056        assert!(
2057            message.starts_with("list item 1: literal overlay"),
2058            "{message}"
2059        );
2060    }
2061
2062    #[test]
2063    fn valid_nested_measure_list_has_no_shape_error() {
2064        let message = evaluate_data_shape_error(
2065            &EvaluateDataValue::List(vec![
2066                unit_map("kilogram", "2"),
2067                EvaluateDataValue::Literal("500 gram".to_string()),
2068            ]),
2069            &mass_list_type(),
2070        );
2071        assert!(message.is_none(), "{message:?}");
2072    }
2073
2074    fn load_engine(code: &str) -> crate::Engine {
2075        let mut engine = crate::Engine::new();
2076        engine
2077            .load([(crate::SourceType::Volatile, code.to_string())])
2078            .expect("load");
2079        engine
2080    }
2081
2082    fn ordered(pairs: &[(&str, EvaluateDataValue)]) -> IndexMap<String, EvaluateDataValue> {
2083        pairs
2084            .iter()
2085            .map(|(k, v)| (k.to_string(), v.clone()))
2086            .collect()
2087    }
2088
2089    fn sample_shallow() -> EvaluateDataValue {
2090        EvaluateDataValue::Map(ordered(&[
2091            (
2092                "price",
2093                EvaluateDataValue::Map(ordered(&[("eur", EvaluateDataValue::from("84"))])),
2094            ),
2095            (
2096                "fares",
2097                EvaluateDataValue::List(vec![
2098                    EvaluateDataValue::from("89 eur"),
2099                    EvaluateDataValue::Boolean(true),
2100                    EvaluateDataValue::Map(IndexMap::new()),
2101                    EvaluateDataValue::List(Vec::new()),
2102                ]),
2103            ),
2104        ]))
2105    }
2106
2107    #[test]
2108    fn debug_matches_derived_layout_compact_and_pretty() {
2109        let value = sample_shallow();
2110        assert_eq!(
2111            format!("{value:?}"),
2112            r#"Map({"price": Map({"eur": Literal("84")}), "fares": List([Literal("89 eur"), Boolean(true), Map({}), List([])])})"#
2113        );
2114        assert_eq!(
2115            format!("{value:#?}"),
2116            concat!(
2117                "Map(\n",
2118                "    {\n",
2119                "        \"price\": Map(\n",
2120                "            {\n",
2121                "                \"eur\": Literal(\n",
2122                "                    \"84\",\n",
2123                "                ),\n",
2124                "            },\n",
2125                "        ),\n",
2126                "        \"fares\": List(\n",
2127                "            [\n",
2128                "                Literal(\n",
2129                "                    \"89 eur\",\n",
2130                "                ),\n",
2131                "                Boolean(\n",
2132                "                    true,\n",
2133                "                ),\n",
2134                "                Map(\n",
2135                "                    {},\n",
2136                "                ),\n",
2137                "                List(\n",
2138                "                    [],\n",
2139                "                ),\n",
2140                "            ],\n",
2141                "        ),\n",
2142                "    },\n",
2143                ")",
2144            )
2145        );
2146    }
2147
2148    #[test]
2149    fn deep_chains_clone_eq_debug_and_drop_on_small_stack() {
2150        const DEPTH: usize = 1_000_000;
2151        std::thread::Builder::new()
2152            .stack_size(256 * 1024)
2153            .spawn(|| {
2154                let mut list = EvaluateDataValue::Literal("leaf".to_string());
2155                for _ in 0..DEPTH {
2156                    list = EvaluateDataValue::List(vec![list]);
2157                }
2158                let cloned = list.clone();
2159                assert!(list == cloned);
2160                let rendered = format!("{list:?}");
2161                assert_eq!(
2162                    rendered.len(),
2163                    DEPTH * "List([".len() + "Literal(\"leaf\")".len() + DEPTH * "])".len()
2164                );
2165                drop(cloned);
2166
2167                let mut map = EvaluateDataValue::Boolean(false);
2168                for _ in 0..DEPTH {
2169                    let mut level = IndexMap::new();
2170                    level.insert("n".to_string(), map);
2171                    map = EvaluateDataValue::Map(level);
2172                }
2173                let cloned = map.clone();
2174                assert!(map == cloned);
2175                assert!(map != list);
2176                drop(cloned);
2177                drop(map);
2178                drop(list);
2179            })
2180            .expect("spawn small-stack thread")
2181            .join()
2182            .expect("deep run data must not overflow the stack");
2183    }
2184
2185    #[test]
2186    fn eq_is_order_insensitive_for_maps_and_ordered_for_lists() {
2187        let forward = EvaluateDataValue::Map(ordered(&[
2188            ("a", EvaluateDataValue::from("1")),
2189            ("b", EvaluateDataValue::from("2")),
2190        ]));
2191        let reversed = EvaluateDataValue::Map(ordered(&[
2192            ("b", EvaluateDataValue::from("2")),
2193            ("a", EvaluateDataValue::from("1")),
2194        ]));
2195        assert!(forward == reversed);
2196        assert!(
2197            EvaluateDataValue::List(vec![
2198                EvaluateDataValue::from("1"),
2199                EvaluateDataValue::from("2")
2200            ]) != EvaluateDataValue::List(vec![
2201                EvaluateDataValue::from("2"),
2202                EvaluateDataValue::from("1")
2203            ])
2204        );
2205        assert!(EvaluateDataValue::from("1") != EvaluateDataValue::Boolean(true));
2206    }
2207
2208    #[test]
2209    fn request_error_names_first_key_in_caller_order() {
2210        let engine = load_engine(
2211            r#"
2212spec inner
2213data n: 1
2214
2215spec s
2216uses left: inner
2217uses right: inner
2218rule out: 1
2219"#,
2220        );
2221        let now = crate::DateTimeValue::now();
2222        let overlay = EvaluateDataValue::Map(ordered(&[("n", EvaluateDataValue::from("2"))]));
2223        let left_first = engine
2224            .evaluate(
2225                None,
2226                "s",
2227                Some(&now),
2228                ordered(&[("left", overlay.clone()), ("right", overlay.clone())]),
2229                None,
2230                false,
2231            )
2232            .expect_err("uses alias map is a request error");
2233        assert!(left_first.to_string().contains("'left'"), "{left_first}");
2234        assert!(!left_first.to_string().contains("'right'"), "{left_first}");
2235
2236        let right_first = engine
2237            .evaluate(
2238                None,
2239                "s",
2240                Some(&now),
2241                ordered(&[("right", overlay.clone()), ("left", overlay)]),
2242                None,
2243                false,
2244            )
2245            .expect_err("uses alias map is a request error");
2246        assert!(right_first.to_string().contains("'right'"), "{right_first}");
2247        assert!(!right_first.to_string().contains("'left'"), "{right_first}");
2248    }
2249
2250    fn roster_engine() -> crate::Engine {
2251        load_engine(
2252            r#"
2253spec person
2254data age: number
2255  -> minimum 0
2256data score: number
2257  -> minimum 0
2258
2259spec roster
2260uses person
2261data people: person list
2262rule n: people -> count
2263"#,
2264        )
2265    }
2266
2267    #[test]
2268    fn row_veto_names_first_field_in_caller_order() {
2269        let engine = roster_engine();
2270        let now = crate::DateTimeValue::now();
2271        let age_first = engine
2272            .evaluate(
2273                None,
2274                "roster",
2275                Some(&now),
2276                ordered(&[(
2277                    "people",
2278                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2279                        ("age", EvaluateDataValue::from("-1")),
2280                        ("score", EvaluateDataValue::from("-1")),
2281                    ]))]),
2282                )]),
2283                Some(&["n".to_string()]),
2284                false,
2285            )
2286            .expect("evaluate");
2287        let reason = age_first
2288            .results
2289            .get("n")
2290            .expect("n")
2291            .veto_reason
2292            .as_deref()
2293            .expect("veto");
2294        assert!(reason.contains("age"), "{reason}");
2295        assert!(!reason.contains("score"), "{reason}");
2296
2297        let score_first = engine
2298            .evaluate(
2299                None,
2300                "roster",
2301                Some(&now),
2302                ordered(&[(
2303                    "people",
2304                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2305                        ("score", EvaluateDataValue::from("-1")),
2306                        ("age", EvaluateDataValue::from("-1")),
2307                    ]))]),
2308                )]),
2309                Some(&["n".to_string()]),
2310                false,
2311            )
2312            .expect("evaluate");
2313        let reason = score_first
2314            .results
2315            .get("n")
2316            .expect("n")
2317            .veto_reason
2318            .as_deref()
2319            .expect("veto");
2320        assert!(reason.contains("score"), "{reason}");
2321        assert!(!reason.contains("age"), "{reason}");
2322    }
2323
2324    #[test]
2325    fn row_unknown_fields_are_ignored() {
2326        let engine = roster_engine();
2327        let now = crate::DateTimeValue::now();
2328        let bogus_first = engine
2329            .evaluate(
2330                None,
2331                "roster",
2332                Some(&now),
2333                ordered(&[(
2334                    "people",
2335                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2336                        ("bogus", EvaluateDataValue::from("1")),
2337                        ("also", EvaluateDataValue::from("2")),
2338                        ("age", EvaluateDataValue::from("1")),
2339                        ("score", EvaluateDataValue::from("1")),
2340                    ]))]),
2341                )]),
2342                None,
2343                false,
2344            )
2345            .expect("unknown row fields are ignored");
2346        assert!(bogus_first.results.get("n").is_some());
2347
2348        let also_first = engine
2349            .evaluate(
2350                None,
2351                "roster",
2352                Some(&now),
2353                ordered(&[(
2354                    "people",
2355                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2356                        ("also", EvaluateDataValue::from("2")),
2357                        ("bogus", EvaluateDataValue::from("1")),
2358                        ("age", EvaluateDataValue::from("1")),
2359                        ("score", EvaluateDataValue::from("1")),
2360                    ]))]),
2361                )]),
2362                None,
2363                false,
2364            )
2365            .expect("unknown row fields are ignored");
2366        assert!(also_first.results.get("n").is_some());
2367    }
2368
2369    fn tags_engine(limits: crate::ResourceLimits) -> crate::Engine {
2370        let mut engine = crate::Engine::with_limits(limits);
2371        engine
2372            .load([(
2373                crate::SourceType::Volatile,
2374                "spec tagged\ndata tags: text list\nrule n: tags -> count".to_string(),
2375            )])
2376            .expect("load");
2377        engine
2378    }
2379
2380    fn evaluate_tags(engine: &crate::Engine, tags: Vec<String>) -> crate::Response {
2381        let now = crate::DateTimeValue::now();
2382        engine
2383            .evaluate(
2384                None,
2385                "tagged",
2386                Some(&now),
2387                ordered(&[(
2388                    "tags",
2389                    EvaluateDataValue::List(
2390                        tags.into_iter().map(EvaluateDataValue::Literal).collect(),
2391                    ),
2392                )]),
2393                Some(&["n".to_string()]),
2394                false,
2395            )
2396            .expect("evaluate")
2397    }
2398
2399    #[test]
2400    fn value_list_over_value_byte_limit_binds() {
2401        let engine = tags_engine(crate::ResourceLimits::default());
2402        let limit = crate::ResourceLimits::default().max_data_value_bytes;
2403        let tags: Vec<String> = (0..200).map(|i| format!("customer-tag-{i:04}")).collect();
2404        let total: usize = tags.iter().map(String::len).sum();
2405        assert!(
2406            total > limit,
2407            "fixture must exceed {limit} bytes, has {total}"
2408        );
2409
2410        let response = evaluate_tags(&engine, tags);
2411        let result = response.results.get("n").expect("n");
2412        assert_eq!(result.veto_reason, None);
2413        assert_eq!(result.result(), Some("200"));
2414    }
2415
2416    #[test]
2417    fn value_list_item_over_value_byte_limit_vetoes() {
2418        let engine = tags_engine(crate::ResourceLimits {
2419            max_data_value_bytes: 50,
2420            ..crate::ResourceLimits::default()
2421        });
2422        let response = evaluate_tags(
2423            &engine,
2424            vec!["vip".to_string(), "x".repeat(51), "trial".to_string()],
2425        );
2426        let reason = response
2427            .results
2428            .get("n")
2429            .expect("n")
2430            .veto_reason
2431            .as_deref()
2432            .expect("veto");
2433        assert!(
2434            reason.contains("list item 2 exceeds the size limit."),
2435            "{reason}"
2436        );
2437    }
2438}