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, Value};
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, parser_value_to_value_kind, row_literal, BoundValueKind,
9    DataDefinition, DataPath, InstanceKey, KeyField, LemmaType, ListKey, ListValue, LiteralValue,
10    RowBinding, RowEnd, RowOrigin, Source, SpecIdentity, SpecValue, TypeSpecification,
11    TypedLiteral, UnitTokenResolver, 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/// Read a parsed Lemma literal as a value of `lemma_type`, keeping the written unit.
573pub(crate) fn typed_literal_from_parsed(
574    parsed: &Value,
575    lemma_type: &Arc<LemmaType>,
576    unit_index: &UnitIndex,
577    named_types: &IndexMap<String, Arc<LemmaType>>,
578) -> Result<TypedLiteral, String> {
579    let resolve = UnitTokenResolver::Typed {
580        unit_index,
581        named_types,
582        lemma_type: lemma_type.as_ref(),
583    };
584    let kind = parser_value_to_value_kind(parsed, lemma_type.as_ref(), Some(&resolve))?;
585    let binding = match binding_unit_from_parser_value(parsed) {
586        Some(written) => Some(resolve.resolve(&written)?),
587        None => None,
588    };
589    Ok(typed_literal_with_binding_unit(kind, binding, lemma_type))
590}
591
592fn typed_literal_with_binding_unit(
593    kind: ValueKind,
594    binding_unit: Option<String>,
595    lemma_type: &Arc<LemmaType>,
596) -> TypedLiteral {
597    let typed_type = match binding_unit {
598        Some(unit)
599            if matches!(
600                &lemma_type.specifications,
601                TypeSpecification::Measure { .. } | TypeSpecification::Ratio { .. }
602            ) =>
603        {
604            Arc::new(lemma_type.as_ref().clone().with_unit(unit))
605        }
606        Some(_) | None => Arc::clone(lemma_type),
607    };
608    TypedLiteral {
609        value: kind,
610        lemma_type: typed_type,
611    }
612}
613
614fn binding_unit_from_parser_value(value: &Value) -> Option<String> {
615    use crate::parsing::ast::Value;
616    match value {
617        Value::NumberWithUnit(_, unit) => Some(unit.clone()),
618        Value::Range(left, right) => match (left.as_ref(), right.as_ref()) {
619            (Value::NumberWithUnit(_, left_unit), Value::NumberWithUnit(_, right_unit))
620                if left_unit == right_unit =>
621            {
622                Some(left_unit.clone())
623            }
624            _ => None,
625        },
626        _ => None,
627    }
628}
629
630/// Resolve a unit the caller actually wrote to a bare declared name.
631///
632/// Qualified paths go through [`UnitIndex::resolve_with_named_types`]. Bare names
633/// accepted only when this data type (or range element) declares that exact name.
634/// Never invents a unit the caller did not pass.
635fn single_unit_literal_map(map: &IndexMap<String, EvaluateDataValue>) -> Option<(&str, &str)> {
636    if map.len() != 1 {
637        return None;
638    }
639    let (unit, value) = map.iter().next()?;
640    match value {
641        EvaluateDataValue::Literal(magnitude) => Some((magnitude.as_str(), unit.as_str())),
642        _ => None,
643    }
644}
645
646enum ShapeCheck<'a> {
647    Accepts,
648    Mismatch(String),
649    Rows {
650        rows: &'a [EvaluateDataValue],
651        element: &'a LemmaType,
652    },
653}
654
655fn shape_check<'a>(input: &'a EvaluateDataValue, lemma_type: &'a LemmaType) -> ShapeCheck<'a> {
656    let type_spec = &lemma_type.specifications;
657    match (input, type_spec) {
658        (EvaluateDataValue::Literal(_), TypeSpecification::List { .. }) => ShapeCheck::Mismatch(
659            format!("literal overlay is not valid for list data ({type_spec})"),
660        ),
661        (EvaluateDataValue::Literal(_), _) => ShapeCheck::Accepts,
662        (EvaluateDataValue::Boolean(_), TypeSpecification::Boolean { .. }) => ShapeCheck::Accepts,
663        (EvaluateDataValue::Boolean(_), _) => ShapeCheck::Mismatch(format!(
664            "boolean input is only valid for boolean data, not {type_spec}"
665        )),
666        (
667            EvaluateDataValue::Map(map),
668            TypeSpecification::Measure { .. } | TypeSpecification::Ratio { .. },
669        ) => {
670            if single_unit_literal_map(map).is_some() {
671                ShapeCheck::Accepts
672            } else {
673                ShapeCheck::Mismatch(
674                    "measure and ratio data must be a convenience string or a single-unit map"
675                        .to_string(),
676                )
677            }
678        }
679        (EvaluateDataValue::Map(_), _) => ShapeCheck::Mismatch(format!(
680            "object overlay is only valid for measure or ratio data, not {type_spec}"
681        )),
682        (EvaluateDataValue::List(rows), TypeSpecification::List { .. }) => ShapeCheck::Rows {
683            rows: rows.as_slice(),
684            element: lemma_type.list_element_lemma_type().as_ref(),
685        },
686        (EvaluateDataValue::List(_), _) => ShapeCheck::Mismatch(format!(
687            "array overlay is not valid for scalar data ({type_spec})"
688        )),
689    }
690}
691
692struct ShapeFrame<'a> {
693    rows: std::slice::Iter<'a, EvaluateDataValue>,
694    element: &'a LemmaType,
695    position: usize,
696}
697
698/// First shape mismatch in `input` against `lemma_type`, at any list depth. `None` if every
699/// value matches the type its parse node will use. List items are reported by 1-based position.
700fn evaluate_data_shape_error(input: &EvaluateDataValue, lemma_type: &LemmaType) -> Option<String> {
701    let mut frames: Vec<ShapeFrame<'_>> = Vec::new();
702    let mut pending = Some((input, lemma_type));
703    loop {
704        if let Some((value, ty)) = pending.take() {
705            match shape_check(value, ty) {
706                ShapeCheck::Accepts => {}
707                ShapeCheck::Mismatch(message) => return Some(item_path_message(&frames, message)),
708                ShapeCheck::Rows { rows, element } => frames.push(ShapeFrame {
709                    rows: rows.iter(),
710                    element,
711                    position: 0,
712                }),
713            }
714        }
715        let frame = frames.last_mut()?;
716        match frame.rows.next() {
717            Some(child) => {
718                frame.position += 1;
719                pending = Some((child, frame.element));
720            }
721            None => {
722                frames.pop();
723            }
724        }
725    }
726}
727
728fn item_path_message(frames: &[ShapeFrame<'_>], message: String) -> String {
729    if frames.is_empty() {
730        return message;
731    }
732    let positions: Vec<String> = frames.iter().map(|f| f.position.to_string()).collect();
733    format!("list item {}: {message}", positions.join(", item "))
734}
735
736/// Run-data values for fields rows leave unset, by the row as evaluation reads it and
737/// its template slot. Fields a row seals are rejected at resolve.
738#[derive(Debug, Clone, Default)]
739pub(crate) struct RowOverrides {
740    pub(crate) fields: IndexMap<InstanceKey, IndexMap<usize, OperationResult>>,
741    /// Stored lists, by owning row and list path, with a key field set by run data:
742    /// their store index does not hold the run-data keys.
743    pub(crate) keyed_lists: HashSet<(Option<InstanceKey>, DataPath)>,
744}
745
746impl RowOverrides {
747    fn extend(&mut self, other: RowOverrides) {
748        for (key, fields) in other.fields {
749            self.fields.entry(key).or_default().extend(fields);
750        }
751        self.keyed_lists.extend(other.keyed_lists);
752    }
753
754    /// These overrides, read on the rows of a level that is itself the row `owner`.
755    fn under(self, owner: &InstanceKey) -> RowOverrides {
756        RowOverrides {
757            fields: self
758                .fields
759                .into_iter()
760                .map(|(key, fields)| (key.under(owner), fields))
761                .collect(),
762            keyed_lists: self
763                .keyed_lists
764                .into_iter()
765                .map(|(list_owner, path)| {
766                    let list_owner = match list_owner {
767                        Some(key) => key.under(owner),
768                        None => owner.clone(),
769                    };
770                    (Some(list_owner), path)
771                })
772                .collect(),
773        }
774    }
775}
776
777/// User-provided data values resolved against a plan's type declarations.
778///
779/// Lightweight and cheap to construct — no plan cloning required. The
780/// [`ExecutionPlan`] stays immutable; callers pass `(&ExecutionPlan, &EvaluateData)`
781/// to evaluation and show paths.
782#[derive(Debug, Clone, Default)]
783pub struct EvaluateData {
784    /// Caller bindings by plan data slot, in caller order: successful literals or Veto
785    /// (bad override) per Data.
786    pub bindings: IndexMap<usize, OperationResult>,
787    /// Caller-supplied fields of rows the plan holds: `uses` rows (`i.x`, `i.j.x`) and
788    /// spec-written list rows (`staff.1.salary`, `i.staff.2.x`).
789    pub(crate) row_overrides: RowOverrides,
790    /// Input keys that did not match any plan Data (including Import aliases).
791    pub ignored_unknown: Vec<String>,
792}
793
794impl EvaluateData {
795    /// Parse and validate caller-supplied values against the plan's data declarations.
796    ///
797    /// Unknown keys and Import aliases are ignored (recorded in [`Self::ignored_unknown`]).
798    /// Parse, constraint, options, decimals, and input-oversize failures bind that Data
799    /// as [`OperationResult::Veto`]; evaluation still runs. Duplicate canonical keys Error.
800    pub fn resolve(
801        plan: &ExecutionPlan,
802        raw_values: IndexMap<String, EvaluateDataValue>,
803        limits: &ResourceLimits,
804    ) -> Result<Self, Error> {
805        Self::resolve_level(plan, raw_values, limits, ListLevel::Top)
806    }
807
808    fn resolve_level(
809        plan: &ExecutionPlan,
810        raw_values: IndexMap<String, EvaluateDataValue>,
811        limits: &ResourceLimits,
812        level: ListLevel,
813    ) -> Result<Self, Error> {
814        if level == ListLevel::Top {
815            reject_whole_list_with_row_keys(plan, &raw_values)?;
816        }
817        match run_resolve(ResolveFrame::Level(LevelFrame::new(
818            plan, None, raw_values, limits, level,
819        )))? {
820            ResolveOutput::Level(evaluate_data) => Ok(evaluate_data),
821            ResolveOutput::Binding(..) => {
822                panic!("BUG: root level frame returned a list binding")
823            }
824        }
825    }
826}
827
828enum ResolveOutput {
829    Level(EvaluateData),
830    /// A list value, with the run-data fields its rows' nested rows take.
831    Binding(OperationResult, RowOverrides),
832}
833
834enum ResolveStep<'a> {
835    Call(ResolveFrame<'a>),
836    Return(ResolveOutput),
837}
838
839enum ResolveFrame<'a> {
840    Level(LevelFrame<'a>),
841    List(ListFrame<'a>),
842}
843
844impl<'a> ResolveFrame<'a> {
845    fn resume(&mut self, input: Option<ResolveOutput>) -> Result<ResolveStep<'a>, Error> {
846        match self {
847            ResolveFrame::Level(frame) => frame.resume(input),
848            ResolveFrame::List(frame) => frame.resume(input),
849        }
850    }
851}
852
853fn run_resolve(root: ResolveFrame<'_>) -> Result<ResolveOutput, Error> {
854    let mut frames = vec![root];
855    let mut input = None;
856    loop {
857        let frame = frames.last_mut().expect("BUG: resolve frame stack empty");
858        match frame.resume(input.take())? {
859            ResolveStep::Call(child) => frames.push(child),
860            ResolveStep::Return(output) => {
861                frames.pop();
862                if frames.is_empty() {
863                    return Ok(output);
864                }
865                input = Some(output);
866            }
867        }
868    }
869}
870
871/// One level of run data: its entries in caller order. A list entry suspends the level
872/// until its rows are resolved.
873struct LevelFrame<'a> {
874    plan: &'a ExecutionPlan,
875    /// Evaluation-data key of the run-data row this level writes (`items.1`); `None`
876    /// for the request's plan.
877    row_key: Option<Arc<str>>,
878    limits: &'a ResourceLimits,
879    level: ListLevel,
880    entries: indexmap::map::IntoIter<String, EvaluateDataValue>,
881    seen_canonical: HashSet<String>,
882    evaluate_data: EvaluateData,
883    awaiting: Option<AwaitBinding>,
884}
885
886/// Where a suspended level frame puts the binding a nested list frame returns:
887/// a plan data slot, or one field of a row the plan holds.
888enum AwaitBinding {
889    Data { slot: usize },
890    RowField { key: InstanceKey, slot: usize },
891}
892
893impl<'a> LevelFrame<'a> {
894    fn new(
895        plan: &'a ExecutionPlan,
896        row_key: Option<Arc<str>>,
897        raw_values: IndexMap<String, EvaluateDataValue>,
898        limits: &'a ResourceLimits,
899        level: ListLevel,
900    ) -> Self {
901        let capacity = raw_values.len();
902        Self {
903            plan,
904            row_key,
905            limits,
906            level,
907            entries: raw_values.into_iter(),
908            seen_canonical: HashSet::with_capacity(capacity),
909            evaluate_data: EvaluateData {
910                bindings: IndexMap::with_capacity(capacity),
911                ..EvaluateData::default()
912            },
913            awaiting: None,
914        }
915    }
916
917    fn resume(&mut self, input: Option<ResolveOutput>) -> Result<ResolveStep<'a>, Error> {
918        match (self.awaiting.take(), input) {
919            (None, None) => {}
920            (
921                Some(AwaitBinding::Data { slot }),
922                Some(ResolveOutput::Binding(binding, overrides)),
923            ) => {
924                self.evaluate_data.bindings.insert(slot, binding);
925                self.evaluate_data.row_overrides.extend(overrides);
926            }
927            (
928                Some(AwaitBinding::RowField { key, slot }),
929                Some(ResolveOutput::Binding(binding, overrides)),
930            ) => {
931                self.evaluate_data
932                    .row_overrides
933                    .fields
934                    .entry(key)
935                    .or_default()
936                    .insert(slot, binding);
937                self.evaluate_data.row_overrides.extend(overrides);
938            }
939            _ => panic!("BUG: level frame resumed with an unexpected output"),
940        }
941        self.advance()
942    }
943
944    fn advance(&mut self) -> Result<ResolveStep<'a>, Error> {
945        let plan = self.plan;
946        let limits = self.limits;
947        let level = self.level;
948        let evaluate_data = &mut self.evaluate_data;
949        let seen_canonical = &mut self.seen_canonical;
950
951        for (name, raw_value) in self.entries.by_ref() {
952            let canonical = canonical_logical_name(&name);
953            if seen_canonical.contains(canonical.as_ref()) {
954                let canonical = canonical.into_owned();
955                return Err(Error::request(
956                    format!("Duplicate data key '{canonical}'"),
957                    Some("Data keys are case-insensitive; remove the duplicate"),
958                )
959                .with_related_data(name));
960            }
961            seen_canonical.insert(canonical.to_string());
962
963            let Some((slot, data_path, data_definition)) =
964                resolve_evaluate_data_target(plan, &canonical)
965            else {
966                let row_field = match resolve_row_field_key(plan, &canonical) {
967                    RowKeyLookup::Field(row_field) => row_field,
968                    RowKeyLookup::Unknown => {
969                        evaluate_data.ignored_unknown.push(name);
970                        continue;
971                    }
972                    RowKeyLookup::Invalid(error) => return Err(error.with_related_data(name)),
973                };
974                if matches!(row_field.definition, DataDefinition::Import { .. }) {
975                    if matches!(
976                        raw_value,
977                        EvaluateDataValue::Map(_) | EvaluateDataValue::List(_)
978                    ) {
979                        return Err(Error::request(
980                            format!(
981                                "data '{name}': '{name}' is a uses alias; objects and arrays are not a nesting level"
982                            ),
983                            Some(format!("bind fields with dotted keys, e.g. '{name}.<field>'")),
984                        )
985                        .with_related_data(name));
986                    }
987                    evaluate_data.ignored_unknown.push(name);
988                    continue;
989                }
990                if row_field.sealed {
991                    return Err(Error::request(
992                        format!(
993                            "data '{name}' is sealed by `uses -> with` and cannot be supplied at evaluate"
994                        ),
995                        Some("remove this key, or change the `uses` binding if the value should be caller-owned data"),
996                    )
997                    .with_related_data(name));
998                }
999                if let Some(list) = row_field.keyed_list {
1000                    evaluate_data.row_overrides.keyed_lists.insert(list);
1001                }
1002                if matches!(row_field.definition, DataDefinition::InstanceList { .. }) {
1003                    self.awaiting = Some(AwaitBinding::RowField {
1004                        key: row_field.key,
1005                        slot: row_field.slot,
1006                    });
1007                    return Ok(ResolveStep::Call(ResolveFrame::List(ListFrame {
1008                        plan: row_field.template,
1009                        path: row_field.field,
1010                        list_key: canonical.into_owned(),
1011                        under: self.row_key.clone(),
1012                        name,
1013                        overrides: RowOverrides::default(),
1014                        limits,
1015                        level,
1016                        raw_value: Some(raw_value),
1017                        rows: None,
1018                    })));
1019                }
1020                let type_arc = Arc::clone(
1021                    row_field
1022                        .definition
1023                        .resolved_type_arc()
1024                        .expect("BUG: scalar row field is typed template data"),
1025                );
1026                let binding = resolve_scalar_binding(
1027                    plan,
1028                    &name,
1029                    &canonical,
1030                    &raw_value,
1031                    &type_arc,
1032                    row_field.definition.source(),
1033                    limits,
1034                )?;
1035                evaluate_data
1036                    .row_overrides
1037                    .fields
1038                    .entry(row_field.key)
1039                    .or_default()
1040                    .insert(row_field.slot, binding);
1041                continue;
1042            };
1043
1044            if data_definition.is_bound() {
1045                return Err(Error::request(
1046                    format!(
1047                        "data '{name}' is sealed by `uses -> with` and cannot be supplied at evaluate"
1048                    ),
1049                    Some("remove this key, or change the `uses` binding if the value should be caller-owned data"),
1050                )
1051                .with_related_data(name));
1052            }
1053
1054            let data_source = data_definition.source();
1055            let type_arc = match data_definition {
1056                DataDefinition::TypeDeclaration { resolved_type, .. }
1057                | DataDefinition::Value { resolved_type, .. }
1058                | DataDefinition::Reference { resolved_type, .. } => Arc::clone(resolved_type),
1059                DataDefinition::Import { .. } => {
1060                    if matches!(
1061                        raw_value,
1062                        EvaluateDataValue::Map(_) | EvaluateDataValue::List(_)
1063                    ) {
1064                        return Err(Error::request(
1065                            format!(
1066                                "data '{name}': '{name}' is a uses alias; objects and arrays are not a nesting level"
1067                            ),
1068                            Some(format!("bind fields with dotted keys, e.g. '{name}.<field>'")),
1069                        )
1070                        .with_related_data(name));
1071                    }
1072                    evaluate_data.ignored_unknown.push(name);
1073                    continue;
1074                }
1075                DataDefinition::InstanceList { .. } => {
1076                    self.awaiting = Some(AwaitBinding::Data { slot });
1077                    return Ok(ResolveStep::Call(ResolveFrame::List(ListFrame {
1078                        plan,
1079                        path: data_path,
1080                        list_key: data_path.input_key(),
1081                        under: self.row_key.clone(),
1082                        name,
1083                        overrides: RowOverrides::default(),
1084                        limits,
1085                        level,
1086                        raw_value: Some(raw_value),
1087                        rows: None,
1088                    })));
1089                }
1090            };
1091
1092            let binding = resolve_scalar_binding(
1093                plan,
1094                &name,
1095                &canonical,
1096                &raw_value,
1097                &type_arc,
1098                data_source,
1099                limits,
1100            )?;
1101            evaluate_data.bindings.insert(slot, binding);
1102        }
1103
1104        Ok(ResolveStep::Return(ResolveOutput::Level(std::mem::take(
1105            evaluate_data,
1106        ))))
1107    }
1108}
1109
1110/// One caller value against its slot type: parse, size-check and validate it. The
1111/// result to bind — the parsed value or a veto for a bad overlay; structural
1112/// failures (shape, too many rows) are request errors.
1113fn resolve_scalar_binding(
1114    plan: &ExecutionPlan,
1115    name: &str,
1116    input_key: &str,
1117    raw_value: &EvaluateDataValue,
1118    type_arc: &Arc<LemmaType>,
1119    data_source: &Source,
1120    limits: &ResourceLimits,
1121) -> Result<OperationResult, Error> {
1122    if raw_value.is_empty() && type_arc.empty_runtime_input_vetoes() {
1123        return Ok(OperationResult::Veto(VetoType::computation(format!(
1124            "Data {} cannot be empty.",
1125            type_arc.label_for_data_input(input_key)
1126        ))));
1127    }
1128
1129    if let Some(message) = evaluate_data_shape_error(raw_value, type_arc) {
1130        return Err(Error::request(
1131            format!("data '{name}': {message}"),
1132            Some(format!("fix the overlay for '{name}'")),
1133        )
1134        .with_related_data(name));
1135    }
1136
1137    let typed = match parse_data_value(
1138        raw_value,
1139        type_arc,
1140        data_source,
1141        plan.expression_unit_index(),
1142        &plan.resolved_types.resolved,
1143    ) {
1144        Ok(value) => value,
1145        Err(error) => {
1146            return Ok(OperationResult::Veto(VetoType::computation(
1147                type_arc.data_veto_message(input_key, error.message()),
1148            )));
1149        }
1150    };
1151
1152    if let ValueKind::List(rows) = &typed.value {
1153        if rows.len() > limits.max_list_rows {
1154            return Err(Error::request(
1155                format!(
1156                    "data '{name}' has {} rows, over max_list_rows {}",
1157                    rows.len(),
1158                    limits.max_list_rows
1159                ),
1160                Some("reduce the list size or raise max_list_rows".to_string()),
1161            )
1162            .with_related_data(name));
1163        }
1164    }
1165
1166    let literal_value = LiteralValue {
1167        value: typed.value.clone(),
1168    };
1169    let oversize = match &typed.value {
1170        ValueKind::List(items) => items
1171            .iter()
1172            .position(|item| item.byte_size() > limits.max_data_value_bytes)
1173            .map(|index| {
1174                format!(
1175                    "Data {} list item {} exceeds the size limit.",
1176                    type_arc.label_for_data_input(input_key),
1177                    index + 1
1178                )
1179            }),
1180        _ => (literal_value.byte_size() > limits.max_data_value_bytes).then(|| {
1181            format!(
1182                "Data {} exceeds the size limit.",
1183                type_arc.label_for_data_input(input_key)
1184            )
1185        }),
1186    };
1187    if let Some(reason) = oversize {
1188        return Ok(OperationResult::Veto(VetoType::computation(reason)));
1189    }
1190
1191    if let Err(message) = validate_value_against_type(
1192        typed.lemma_type.as_ref(),
1193        &literal_value,
1194        plan.expression_unit_index(),
1195    ) {
1196        return Ok(OperationResult::Veto(VetoType::computation(
1197            type_arc.data_veto_message(input_key, &message),
1198        )));
1199    }
1200
1201    Ok(OperationResult::from_bound(BoundValueKind {
1202        value: typed.value.clone(),
1203        unit: typed.lemma_type.unit().map(std::sync::Arc::from),
1204    }))
1205}
1206
1207/// Plan data slot and path matched by `input_key`: local data or data of a flattened
1208/// import. Fields of spec-written rows (`staff.1.salary`) are not plan data; they
1209/// resolve through [`resolve_row_field_key`].
1210fn resolve_evaluate_data_target<'a>(
1211    plan: &'a ExecutionPlan,
1212    key: &str,
1213) -> Option<(usize, &'a DataPath, &'a DataDefinition)> {
1214    let path = plan.input_key_index.get(key)?;
1215    let (slot, path, definition) = plan
1216        .data
1217        .get_full(path)
1218        .unwrap_or_else(|| panic!("BUG: indexed input '{key}' has no data definition"));
1219    Some((slot, path, definition))
1220}
1221
1222/// A stored list as a row reads it: the row holding it (`None` for the plan) and its path.
1223type ListAddress = (Option<InstanceKey>, DataPath);
1224
1225/// A dotted key naming one field of a row the plan holds: a field of a `uses` row
1226/// (`i.x`, `i.j.x`) or of a spec-written list row (`staff.2.salary`, `i.staff.1.x`).
1227struct RowFieldKey<'a> {
1228    /// The row as evaluation reads it.
1229    key: InstanceKey,
1230    template: &'a ExecutionPlan,
1231    slot: usize,
1232    field: &'a DataPath,
1233    definition: &'a DataDefinition,
1234    /// The row binds the field itself.
1235    sealed: bool,
1236    /// The stored list whose index the field overrides: set when the row is a stored
1237    /// row and the field is one of its list's key fields.
1238    keyed_list: Option<ListAddress>,
1239    /// Every list the key steps through a row of, with its dotted key.
1240    lists: Vec<(ListAddress, String)>,
1241}
1242
1243enum RowKeyLookup<'a> {
1244    Field(RowFieldKey<'a>),
1245    /// No row field of the plan: an ignorable key.
1246    Unknown,
1247    /// A key under a list that names no row or field of it: the caller addressed a row
1248    /// that is not there, or a field those rows do not have.
1249    Invalid(Error),
1250}
1251
1252/// `i.j.x`, `staff.2.salary`, `i.staff.1.x` → the field of the row the key names,
1253/// stepping through `uses` slots and the rows of stored lists from the plan.
1254fn resolve_row_field_key<'a>(plan: &'a ExecutionPlan, key: &str) -> RowKeyLookup<'a> {
1255    let parts: Vec<&str> = key.split('.').collect();
1256    let mut template = plan;
1257    let mut holder: Option<InstanceKey> = None;
1258    let mut keyed: Option<(ListAddress, &'a ListKey)> = None;
1259    let mut lists: Vec<(ListAddress, String)> = Vec::new();
1260    let mut index = 0;
1261    loop {
1262        let Some(path) = template.input_key_index.get(parts[index]) else {
1263            return unknown_under_list(key, &lists);
1264        };
1265        let (slot, _, definition) = template
1266            .data
1267            .get_full(path)
1268            .expect("BUG: indexed input key has a data definition");
1269        let row = holder.as_ref().map(|key| key.row.as_ref());
1270        if index + 1 == parts.len() {
1271            if matches!(definition, DataDefinition::Import { .. }) && !lists.is_empty() {
1272                return unknown_under_list(key, &lists);
1273            }
1274            let Some(key) = holder else {
1275                return RowKeyLookup::Unknown;
1276            };
1277            let keyed_list = keyed.and_then(|(list, list_key)| match list_key {
1278                ListKey::Fields(fields) => fields
1279                    .iter()
1280                    .any(|field| field.path == *path)
1281                    .then_some(list),
1282                ListKey::Numbered => None,
1283            });
1284            return RowKeyLookup::Field(RowFieldKey {
1285                sealed: key.row.bindings[slot].is_some(),
1286                key,
1287                template,
1288                slot,
1289                field: path,
1290                definition,
1291                keyed_list,
1292                lists,
1293            });
1294        }
1295        match definition {
1296            DataDefinition::Import { .. } => {
1297                let import = Arc::clone(template.import_row(row, path, None));
1298                template = template.row_template(&import.identity);
1299                holder = Some(InstanceKey::new(import, holder));
1300                keyed = None;
1301                index += 1;
1302            }
1303            DataDefinition::InstanceList { .. } => {
1304                let list_key = parts[..=index].join(".");
1305                let source = template
1306                    .instance_lists
1307                    .get(path)
1308                    .unwrap_or_else(|| panic!("BUG: instance list '{list_key}' has no source"));
1309                let (rows, owner) = match row.and_then(|row| row.bindings[slot].as_ref()) {
1310                    None => (source.store.rows.as_slice(), holder.clone()),
1311                    Some(RowBinding::Rows { rows, hops, .. }) => {
1312                        let holder = holder.as_ref().expect("BUG: a bound list is a row field");
1313                        (rows.as_slice(), holder.scope(*hops).cloned())
1314                    }
1315                    Some(
1316                        RowBinding::Bound(_)
1317                        | RowBinding::Deferred { .. }
1318                        | RowBinding::Instance(_),
1319                    ) => {
1320                        return RowKeyLookup::Invalid(Error::request(
1321                            format!(
1322                                "data '{key}' is sealed by `uses -> with` and cannot be supplied at evaluate"
1323                            ),
1324                            Some(format!(
1325                                "'{list_key}' is bound by `uses -> with`; its rows take no run data"
1326                            )),
1327                        ));
1328                    }
1329                };
1330                let rest = &parts[index + 1..];
1331                let number = match rest.first().map(|row| row.parse::<usize>()) {
1332                    Some(Ok(number)) if rest.len() > 1 => number,
1333                    _ => {
1334                        return RowKeyLookup::Invalid(no_list_data(
1335                            key,
1336                            &list_key,
1337                            format!("fields of a row of '{list_key}' are keyed '{list_key}.<row>.<field>'"),
1338                        ));
1339                    }
1340                };
1341                if number == 0 || number > rows.len() {
1342                    return RowKeyLookup::Invalid(no_list_data(
1343                        key,
1344                        &list_key,
1345                        format!(
1346                            "'{list_key}' has {} spec-written row(s); rows are numbered from 1",
1347                            rows.len()
1348                        ),
1349                    ));
1350                }
1351                let list = (owner.clone(), path.clone());
1352                lists.push((list.clone(), list_key));
1353                keyed = match row.and_then(|row| row.bindings[slot].as_ref()) {
1354                    None => Some((list, &source.key)),
1355                    Some(_) => None,
1356                };
1357                let listed = Arc::clone(&rows[number - 1]);
1358                template = template.row_template(&listed.identity);
1359                holder = Some(InstanceKey::new(listed, owner));
1360                index += 2;
1361            }
1362            DataDefinition::Value { .. }
1363            | DataDefinition::TypeDeclaration { .. }
1364            | DataDefinition::Reference { .. } => {
1365                return unknown_under_list(key, &lists);
1366            }
1367        }
1368    }
1369}
1370
1371/// Template, definition, instance and field path of a `uses` instance field a dotted
1372/// run-data key names (`i.x`, `i.j.x`); `None` for any other key.
1373pub(crate) fn row_field_definition<'a>(
1374    plan: &'a ExecutionPlan,
1375    key: &str,
1376) -> Option<(
1377    &'a ExecutionPlan,
1378    &'a DataDefinition,
1379    InstanceKey,
1380    &'a DataPath,
1381)> {
1382    match resolve_row_field_key(plan, key) {
1383        RowKeyLookup::Field(row_field) if row_field.lists.is_empty() => Some((
1384            row_field.template,
1385            row_field.definition,
1386            row_field.key,
1387            row_field.field,
1388        )),
1389        RowKeyLookup::Field(_) => None,
1390        RowKeyLookup::Unknown | RowKeyLookup::Invalid(_) => None,
1391    }
1392}
1393
1394/// A key that names nothing past its last list row (`items.1.bonus`): a request error
1395/// under a list, an ignorable key otherwise.
1396fn unknown_under_list<'a>(key: &str, lists: &[(ListAddress, String)]) -> RowKeyLookup<'a> {
1397    match lists.last() {
1398        Some((_, list_key)) => {
1399            let field = key[list_key.len() + 1..]
1400                .split_once('.')
1401                .map_or("", |(_, field)| field);
1402            RowKeyLookup::Invalid(no_list_data(
1403                key,
1404                list_key,
1405                format!("rows of '{list_key}' have no field '{field}'"),
1406            ))
1407        }
1408        None => RowKeyLookup::Unknown,
1409    }
1410}
1411
1412fn no_list_data(key: &str, list_key: &str, hint: String) -> Error {
1413    Error::request(
1414        format!("data '{key}' names no data of '{list_key}'"),
1415        Some(hint),
1416    )
1417}
1418
1419/// A request that replaces a list as a whole and also supplies a field of one of its
1420/// spec-written rows is contradictory: the whole list has no such row.
1421fn reject_whole_list_with_row_keys(
1422    plan: &ExecutionPlan,
1423    raw_values: &IndexMap<String, EvaluateDataValue>,
1424) -> Result<(), Error> {
1425    let mut whole_lists: HashSet<ListAddress> = HashSet::new();
1426    let mut row_fields: Vec<(&String, Vec<(ListAddress, String)>)> = Vec::new();
1427    for name in raw_values.keys() {
1428        let canonical = canonical_logical_name(name);
1429        if let Some((_, path, definition)) = resolve_evaluate_data_target(plan, &canonical) {
1430            if matches!(definition, DataDefinition::InstanceList { .. }) {
1431                whole_lists.insert((None, path.clone()));
1432            }
1433            continue;
1434        }
1435        if let RowKeyLookup::Field(row_field) = resolve_row_field_key(plan, &canonical) {
1436            if matches!(row_field.definition, DataDefinition::InstanceList { .. }) {
1437                whole_lists.insert((Some(row_field.key.clone()), row_field.field.clone()));
1438            }
1439            row_fields.push((name, row_field.lists));
1440        }
1441    }
1442    for (name, lists) in row_fields {
1443        for (list, list_key) in lists {
1444            if whole_lists.contains(&list) {
1445                return Err(Error::request(
1446                    format!(
1447                        "data '{list_key}': cannot supply both the whole list and dotted row keys such as '{name}'"
1448                    ),
1449                    Some(format!(
1450                        "pass either '{list_key}' as an array, or only dotted keys under '{list_key}'"
1451                    )),
1452                )
1453                .with_related_data(list_key));
1454            }
1455        }
1456    }
1457    Ok(())
1458}
1459
1460/// Whether a run-data list value is a top-level one, whose whole row tree is checked
1461/// against the list limits, or rows nested inside one already checked.
1462#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1463enum ListLevel {
1464    Top,
1465    Nested,
1466}
1467
1468/// Rows of one top-level list value across all its levels, `template` being the plan
1469/// its rows read. Over `max_list_nodes` rows in total, or rows nesting deeper than
1470/// `max_list_depth` levels (the spec holding the list is level 1), is a request Error.
1471/// Node count is checked first so a wide tree reports its size, not its depth.
1472fn check_list_tree(
1473    template: &ExecutionPlan,
1474    raw_rows: &[EvaluateDataValue],
1475    limits: &ResourceLimits,
1476) -> Result<(), String> {
1477    let mut stack: Vec<(&ExecutionPlan, &EvaluateDataValue, usize)> =
1478        raw_rows.iter().map(|row| (template, row, 2)).collect();
1479    let mut nodes = 0usize;
1480    let mut deepest = 0usize;
1481    while let Some((row_template, raw_row, depth)) = stack.pop() {
1482        nodes += 1;
1483        if nodes > limits.max_list_nodes {
1484            return Err(format!(
1485                "has more than max_list_nodes {} rows across all levels",
1486                limits.max_list_nodes
1487            ));
1488        }
1489        deepest = deepest.max(depth);
1490        let EvaluateDataValue::Map(fields) = raw_row else {
1491            continue;
1492        };
1493        for (field, value) in fields {
1494            let EvaluateDataValue::List(rows) = value else {
1495                continue;
1496            };
1497            let Some(source) = row_template
1498                .input_key_index
1499                .get(canonical_logical_name(field).as_ref())
1500                .and_then(|path| row_template.instance_lists.get(path))
1501            else {
1502                continue;
1503            };
1504            let nested = row_template.row_template(&source.identity);
1505            stack.extend(rows.iter().map(|row| (nested, row, depth + 1)));
1506        }
1507    }
1508    if deepest > limits.max_list_depth {
1509        return Err(format!(
1510            "rows nest {deepest} levels deep counting the spec itself, over max_list_depth {}",
1511            limits.max_list_depth
1512        ));
1513    }
1514    Ok(())
1515}
1516
1517/// Frame of one instance list entry: validates the list, then resolves each row through
1518/// a level frame of the row template. A vetoed field, duplicate key or overlapping key
1519/// range resolves the list to a veto.
1520struct ListFrame<'a> {
1521    plan: &'a ExecutionPlan,
1522    path: &'a DataPath,
1523    /// Dotted key of the list from the plan of its level, naming its rows.
1524    list_key: String,
1525    /// Evaluation-data key of the run-data row holding the list; `None` for a list of
1526    /// the request's plan.
1527    under: Option<Arc<str>>,
1528    name: String,
1529    /// Run-data fields of the rows' nested rows, keyed under the rows.
1530    overrides: RowOverrides,
1531    limits: &'a ResourceLimits,
1532    level: ListLevel,
1533    raw_value: Option<EvaluateDataValue>,
1534    rows: Option<ListRows<'a>>,
1535}
1536
1537struct ListRows<'a> {
1538    source: &'a InstanceListSource,
1539    /// Row spec identity, shared by the rows.
1540    identity: Arc<SpecIdentity>,
1541    template: &'a ExecutionPlan,
1542    row_type: Arc<LemmaType>,
1543    raw_rows: std::vec::IntoIter<EvaluateDataValue>,
1544    number: usize,
1545    literals: Vec<TypedLiteral>,
1546    /// Template data slot and type of each key field, in key order.
1547    key_slots: Box<[KeySlot<'a>]>,
1548    keyed: Vec<Vec<TypedLiteral>>,
1549}
1550
1551struct KeySlot<'a> {
1552    field: &'a KeyField,
1553    slot: usize,
1554    field_type: Arc<LemmaType>,
1555}
1556
1557fn request_error(name: &str, message: String) -> Error {
1558    Error::request(format!("data '{name}': {message}"), None::<String>).with_related_data(name)
1559}
1560
1561impl<'a> ListFrame<'a> {
1562    fn resume(&mut self, input: Option<ResolveOutput>) -> Result<ResolveStep<'a>, Error> {
1563        match self.rows.take() {
1564            None => {
1565                assert!(
1566                    input.is_none(),
1567                    "BUG: list frame started with a child output"
1568                );
1569                let raw_value = self
1570                    .raw_value
1571                    .take()
1572                    .expect("BUG: list frame started twice");
1573                let rows = self.start(raw_value)?;
1574                self.next_row(rows)
1575            }
1576            Some(mut rows) => {
1577                let Some(ResolveOutput::Level(row_data)) = input else {
1578                    panic!("BUG: list frame resumed without a row result");
1579                };
1580                match self.finish_row(&mut rows, row_data)? {
1581                    Some(veto) => Ok(ResolveStep::Return(ResolveOutput::Binding(
1582                        veto,
1583                        RowOverrides::default(),
1584                    ))),
1585                    None => self.next_row(rows),
1586                }
1587            }
1588        }
1589    }
1590
1591    fn start(&self, raw_value: EvaluateDataValue) -> Result<ListRows<'a>, Error> {
1592        let plan = self.plan;
1593        let source = plan
1594            .instance_lists
1595            .get(self.path)
1596            .expect("BUG: instance list data without a source");
1597        let template = plan.row_template(&source.identity);
1598        let Some(raw_rows) = raw_value.into_list() else {
1599            return Err(request_error(
1600                &self.name,
1601                format!(
1602                    "rows of a `{}` list are an array of objects",
1603                    source.identity.spec
1604                ),
1605            ));
1606        };
1607        if raw_rows.len() > self.limits.max_list_rows {
1608            return Err(request_error(
1609                &self.name,
1610                format!(
1611                    "has {} rows, over max_list_rows {}",
1612                    raw_rows.len(),
1613                    self.limits.max_list_rows
1614                ),
1615            ));
1616        }
1617        if self.level == ListLevel::Top {
1618            check_list_tree(template, &raw_rows, self.limits)
1619                .map_err(|message| request_error(&self.name, message))?;
1620        }
1621        let row_type = Arc::new(LemmaType::primitive(TypeSpecification::Spec {
1622            spec: source.identity.clone(),
1623        }));
1624        let key_slots = match &source.key {
1625            ListKey::Fields(key_fields) => key_fields
1626                .iter()
1627                .map(|field| KeySlot {
1628                    field,
1629                    slot: template
1630                        .data
1631                        .get_index_of(&field.path)
1632                        .expect("BUG: key field is template data"),
1633                    field_type: row_end_type(template, &RowEnd::Data(field.path.clone())),
1634                })
1635                .collect(),
1636            ListKey::Numbered => Box::default(),
1637        };
1638        Ok(ListRows {
1639            source,
1640            identity: Arc::new(source.identity.clone()),
1641            template,
1642            row_type,
1643            literals: Vec::with_capacity(raw_rows.len()),
1644            key_slots,
1645            keyed: Vec::new(),
1646            number: 0,
1647            raw_rows: raw_rows.into_iter(),
1648        })
1649    }
1650
1651    fn next_row(&mut self, mut rows: ListRows<'a>) -> Result<ResolveStep<'a>, Error> {
1652        let Some(raw_row) = rows.raw_rows.next() else {
1653            return Ok(ResolveStep::Return(ResolveOutput::Binding(
1654                self.finish_list(rows),
1655                std::mem::take(&mut self.overrides),
1656            )));
1657        };
1658        rows.number += 1;
1659        let Some(fields) = raw_row.into_map() else {
1660            return Err(request_error(
1661                &self.name,
1662                format!("row {} must be an object", rows.number),
1663            ));
1664        };
1665        let template = rows.template;
1666        let origin = crate::planning::semantics::numbered_row_origin(&self.list_key, rows.number);
1667        let row_key: Arc<str> = match &self.under {
1668            Some(under) => Arc::from(format!("{under}.{origin}")),
1669            None => Arc::from(origin),
1670        };
1671        self.rows = Some(rows);
1672        Ok(ResolveStep::Call(ResolveFrame::Level(LevelFrame::new(
1673            template,
1674            Some(row_key),
1675            fields,
1676            self.limits,
1677            ListLevel::Nested,
1678        ))))
1679    }
1680
1681    /// Adds the resolved row `rows.number`. `Some` is a veto of the whole list.
1682    fn finish_row(
1683        &mut self,
1684        rows: &mut ListRows<'a>,
1685        row_data: EvaluateData,
1686    ) -> Result<Option<OperationResult>, Error> {
1687        let number = rows.number;
1688        let name = self.name.as_str();
1689        let mut bindings: Vec<Option<RowBinding>> = vec![None; rows.template.data.len()];
1690        if let Some(veto) = row_data
1691            .row_overrides
1692            .fields
1693            .values()
1694            .flat_map(IndexMap::values)
1695            .find_map(|binding| match binding {
1696                OperationResult::Veto(veto) => Some(veto),
1697                OperationResult::Value(_) => None,
1698            })
1699        {
1700            return Ok(Some(OperationResult::Veto(VetoType::computation(format!(
1701                "In {name} row {number}: {veto}"
1702            )))));
1703        }
1704        for (slot, binding) in row_data.bindings {
1705            if let OperationResult::Veto(veto) = &binding {
1706                return Ok(Some(OperationResult::Veto(VetoType::computation(format!(
1707                    "In {name} row {number}: {veto}"
1708                )))));
1709            }
1710            bindings[slot] = Some(RowBinding::Bound(binding));
1711        }
1712        if let ListKey::Fields(_) = &rows.source.key {
1713            let mut values = Vec::with_capacity(rows.key_slots.len());
1714            for KeySlot {
1715                field: key_field,
1716                slot,
1717                field_type,
1718            } in &rows.key_slots
1719            {
1720                let key_path = &key_field.path;
1721                let value = match bindings.get(*slot).and_then(Option::as_ref) {
1722                    Some(RowBinding::Bound(OperationResult::Value(bound))) => {
1723                        typed_from_bound(bound, field_type)
1724                    }
1725                    Some(RowBinding::Bound(OperationResult::Veto(_))) => {
1726                        unreachable!("BUG: vetoed row binding returned above")
1727                    }
1728                    Some(
1729                        RowBinding::Deferred { .. }
1730                        | RowBinding::Rows { .. }
1731                        | RowBinding::Instance(_),
1732                    ) => {
1733                        unreachable!("BUG: evaluation data rows bind resolved values")
1734                    }
1735                    None => default_of(&rows.template.data, key_path).ok_or_else(|| {
1736                        request_error(
1737                            name,
1738                            format!(
1739                                "row {number} has no value for key field '{}'",
1740                                key_field.name
1741                            ),
1742                        )
1743                    })?,
1744                };
1745                values.push(value);
1746            }
1747            rows.keyed.push(values);
1748        }
1749        let literal = row_literal(
1750            Arc::clone(&rows.identity),
1751            RowOrigin::Numbered {
1752                list: self.list_key.clone(),
1753                number,
1754                under: self.under.clone(),
1755            },
1756            bindings,
1757            Arc::clone(&rows.row_type),
1758        );
1759        let ValueKind::Spec(SpecValue::Row(key)) = &literal.value else {
1760            unreachable!("BUG: a row literal is a row value");
1761        };
1762        self.overrides.extend(row_data.row_overrides.under(key));
1763        rows.literals.push(literal);
1764        Ok(None)
1765    }
1766
1767    fn finish_list(&self, rows: ListRows<'a>) -> OperationResult {
1768        let literals: Arc<[TypedLiteral]> = rows.literals.into();
1769        let list = match &rows.source.key {
1770            ListKey::Fields(key_fields) => {
1771                let field_names: Vec<String> = key_fields
1772                    .iter()
1773                    .map(|key_field| key_field.name.clone())
1774                    .collect();
1775                match KeyIndex::build_numbered(field_names, &rows.keyed) {
1776                    Ok(index) => ListValue::indexed(literals, Arc::new(index)),
1777                    Err(conflict) => {
1778                        return OperationResult::Veto(VetoType::computation(format!(
1779                            "{}: {conflict}",
1780                            self.name
1781                        )));
1782                    }
1783                }
1784            }
1785            ListKey::Numbered => ListValue::from(literals),
1786        };
1787        OperationResult::from_literal(LiteralValue {
1788            value: ValueKind::List(list),
1789        })
1790    }
1791}
1792
1793#[cfg(test)]
1794mod tests {
1795    use super::*;
1796    use crate::computation::rational::{decimal_to_rational, rational_new, rational_one};
1797    use crate::planning::semantics::{
1798        primitive_number_arc, MeasureUnit, MeasureUnits, RatioUnit, RatioUnits, TypeExtends,
1799    };
1800    use rust_decimal::Decimal;
1801
1802    fn dummy_source() -> Source {
1803        Source::new(
1804            crate::parsing::source::SourceType::Volatile,
1805            crate::parsing::ast::Span {
1806                start: 0,
1807                end: 0,
1808                line: 1,
1809                col: 1,
1810            },
1811        )
1812    }
1813
1814    fn mass_measure_type() -> Arc<LemmaType> {
1815        Arc::new(LemmaType::new(
1816            "Mass".to_string(),
1817            TypeSpecification::Measure {
1818                minimum: None,
1819                maximum: None,
1820                units: MeasureUnits::from(vec![
1821                    MeasureUnit {
1822                        name: "kilogram".to_string(),
1823                        scale: crate::literals::UnitScale::Literal(rational_one()),
1824                        derived_measure_factors: Vec::new(),
1825                        decomposition: crate::literals::BaseMeasureVector::new(),
1826                        minimum: None,
1827                        maximum: None,
1828                        suggestion_magnitude: None,
1829                    },
1830                    MeasureUnit {
1831                        name: "gram".to_string(),
1832                        scale: crate::literals::UnitScale::Literal(
1833                            decimal_to_rational(Decimal::new(1, 3)).expect("factor"),
1834                        ),
1835                        derived_measure_factors: Vec::new(),
1836                        decomposition: crate::literals::BaseMeasureVector::new(),
1837                        minimum: None,
1838                        maximum: None,
1839                        suggestion_magnitude: None,
1840                    },
1841                ]),
1842                traits: Vec::new(),
1843                decomposition: None,
1844                unit: None,
1845                help: String::new(),
1846            },
1847            TypeExtends::Primitive,
1848        ))
1849    }
1850
1851    fn ratio_with_percent_type() -> Arc<LemmaType> {
1852        Arc::new(LemmaType::new(
1853            "Rate".to_string(),
1854            TypeSpecification::Ratio {
1855                minimum: None,
1856                maximum: None,
1857                units: RatioUnits::from(vec![
1858                    RatioUnit {
1859                        name: "percent".to_string(),
1860                        value: decimal_to_rational(Decimal::new(100, 0)).expect("factor"),
1861                        minimum: None,
1862                        maximum: None,
1863                        suggestion_magnitude: None,
1864                    },
1865                    RatioUnit {
1866                        name: "fraction".to_string(),
1867                        value: rational_one(),
1868                        minimum: None,
1869                        maximum: None,
1870                        suggestion_magnitude: None,
1871                    },
1872                ]),
1873                unit: None,
1874                help: String::new(),
1875            },
1876            TypeExtends::Primitive,
1877        ))
1878    }
1879
1880    #[test]
1881    fn string_input_parsed_against_type() {
1882        let ty = primitive_number_arc();
1883        let lit = parse_data_value(
1884            &EvaluateDataValue::Literal("42".to_string()),
1885            ty,
1886            &dummy_source(),
1887            &UnitIndex::new(),
1888            &IndexMap::new(),
1889        )
1890        .unwrap();
1891        assert!(matches!(lit.value, ValueKind::Number(_)));
1892    }
1893
1894    fn unit_map(unit: &str, magnitude: &str) -> EvaluateDataValue {
1895        let mut map = IndexMap::new();
1896        map.insert(
1897            unit.to_string(),
1898            EvaluateDataValue::Literal(magnitude.into()),
1899        );
1900        EvaluateDataValue::Map(map)
1901    }
1902
1903    #[test]
1904    fn single_unit_measure_map_parses() {
1905        let ty = mass_measure_type();
1906        let lit = parse_data_value(
1907            &unit_map("kilogram", "2"),
1908            &ty,
1909            &dummy_source(),
1910            &UnitIndex::new(),
1911            &IndexMap::new(),
1912        )
1913        .unwrap();
1914        let ValueKind::Measure(magnitude) = &lit.value else {
1915            panic!("expected measure");
1916        };
1917        assert_eq!(magnitude, &rational_new(2, 1));
1918    }
1919
1920    #[test]
1921    fn single_unit_ratio_map_parses() {
1922        let ty = ratio_with_percent_type();
1923        let lit = parse_data_value(
1924            &unit_map("percent", "5"),
1925            &ty,
1926            &dummy_source(),
1927            &UnitIndex::new(),
1928            &IndexMap::new(),
1929        )
1930        .unwrap();
1931        let ValueKind::Ratio(canonical) = &lit.value else {
1932            panic!("expected ratio");
1933        };
1934        assert_eq!(
1935            *canonical,
1936            decimal_to_rational(Decimal::new(5, 2)).expect("canonical")
1937        );
1938    }
1939
1940    #[test]
1941    fn multi_key_map_is_shape_error() {
1942        let mut map = IndexMap::new();
1943        map.insert(
1944            "kilogram".to_string(),
1945            EvaluateDataValue::Literal("2".to_string()),
1946        );
1947        map.insert(
1948            "gram".to_string(),
1949            EvaluateDataValue::Literal("2000".into()),
1950        );
1951        let message = evaluate_data_shape_error(&EvaluateDataValue::Map(map), &mass_measure_type())
1952            .expect("shape");
1953        assert!(message.contains("single-unit"), "{message}");
1954    }
1955
1956    #[test]
1957    fn empty_map_is_shape_error() {
1958        let message = evaluate_data_shape_error(
1959            &EvaluateDataValue::Map(IndexMap::new()),
1960            &mass_measure_type(),
1961        )
1962        .expect("shape");
1963        assert!(message.contains("single-unit"), "{message}");
1964    }
1965
1966    #[test]
1967    fn map_on_text_is_shape_error() {
1968        let message = evaluate_data_shape_error(
1969            &unit_map("eur", "84"),
1970            &LemmaType::primitive(TypeSpecification::Text {
1971                length: None,
1972                help: String::new(),
1973            }),
1974        )
1975        .expect("shape");
1976        assert!(message.contains("text"), "{message}");
1977    }
1978
1979    #[test]
1980    fn list_on_scalar_is_shape_error() {
1981        let message = evaluate_data_shape_error(
1982            &EvaluateDataValue::List(vec![EvaluateDataValue::Literal("1".to_string())]),
1983            &LemmaType::primitive(TypeSpecification::Number {
1984                minimum: None,
1985                maximum: None,
1986                decimals: None,
1987                help: String::new(),
1988            }),
1989        )
1990        .expect("shape");
1991        assert!(message.contains("array"), "{message}");
1992    }
1993
1994    #[test]
1995    fn boolean_on_number_is_shape_error() {
1996        let message = evaluate_data_shape_error(
1997            &EvaluateDataValue::Boolean(true),
1998            &LemmaType::primitive(TypeSpecification::Number {
1999                minimum: None,
2000                maximum: None,
2001                decimals: None,
2002                help: String::new(),
2003            }),
2004        )
2005        .expect("shape");
2006        assert!(message.contains("boolean"), "{message}");
2007    }
2008
2009    fn mass_list_type() -> LemmaType {
2010        LemmaType::list_of(mass_measure_type())
2011    }
2012
2013    #[test]
2014    fn nested_boolean_item_is_shape_error() {
2015        let message = evaluate_data_shape_error(
2016            &EvaluateDataValue::List(vec![
2017                EvaluateDataValue::Literal("1 kilogram".to_string()),
2018                EvaluateDataValue::Boolean(true),
2019            ]),
2020            &mass_list_type(),
2021        )
2022        .expect("shape");
2023        assert!(
2024            message.starts_with("list item 2: boolean input is only valid for boolean data"),
2025            "{message}"
2026        );
2027    }
2028
2029    #[test]
2030    fn nested_map_item_is_shape_error() {
2031        let text_list =
2032            LemmaType::list_of(Arc::new(LemmaType::primitive(TypeSpecification::Text {
2033                length: None,
2034                help: String::new(),
2035            })));
2036        let message = evaluate_data_shape_error(
2037            &EvaluateDataValue::List(vec![unit_map("eur", "84")]),
2038            &text_list,
2039        )
2040        .expect("shape");
2041        assert!(
2042            message.starts_with("list item 1: object overlay"),
2043            "{message}"
2044        );
2045    }
2046
2047    #[test]
2048    fn nested_list_item_is_shape_error() {
2049        let message = evaluate_data_shape_error(
2050            &EvaluateDataValue::List(vec![EvaluateDataValue::List(vec![
2051                EvaluateDataValue::Literal("1 eur".to_string()),
2052            ])]),
2053            &mass_list_type(),
2054        )
2055        .expect("shape");
2056        assert!(
2057            message.starts_with("list item 1: array overlay"),
2058            "{message}"
2059        );
2060    }
2061
2062    #[test]
2063    fn deeply_nested_item_reports_full_item_path() {
2064        let nested = LemmaType::list_of(Arc::new(mass_list_type()));
2065        let message = evaluate_data_shape_error(
2066            &EvaluateDataValue::List(vec![
2067                EvaluateDataValue::List(vec![EvaluateDataValue::Literal("1 kilogram".to_string())]),
2068                EvaluateDataValue::List(vec![EvaluateDataValue::Boolean(true)]),
2069            ]),
2070            &nested,
2071        )
2072        .expect("shape");
2073        assert!(
2074            message
2075                .starts_with("list item 2, item 1: boolean input is only valid for boolean data"),
2076            "{message}"
2077        );
2078    }
2079
2080    #[test]
2081    fn nested_literal_under_list_type_is_shape_error() {
2082        let nested = LemmaType::list_of(Arc::new(mass_list_type()));
2083        let message = evaluate_data_shape_error(
2084            &EvaluateDataValue::List(vec![EvaluateDataValue::Literal("1 kilogram".to_string())]),
2085            &nested,
2086        )
2087        .expect("shape");
2088        assert!(
2089            message.starts_with("list item 1: literal overlay"),
2090            "{message}"
2091        );
2092    }
2093
2094    #[test]
2095    fn valid_nested_measure_list_has_no_shape_error() {
2096        let message = evaluate_data_shape_error(
2097            &EvaluateDataValue::List(vec![
2098                unit_map("kilogram", "2"),
2099                EvaluateDataValue::Literal("500 gram".to_string()),
2100            ]),
2101            &mass_list_type(),
2102        );
2103        assert!(message.is_none(), "{message:?}");
2104    }
2105
2106    fn load_engine(code: &str) -> crate::Engine {
2107        let mut engine = crate::Engine::new();
2108        engine
2109            .load([(crate::SourceType::Volatile, code.to_string())])
2110            .expect("load");
2111        engine
2112    }
2113
2114    fn ordered(pairs: &[(&str, EvaluateDataValue)]) -> IndexMap<String, EvaluateDataValue> {
2115        pairs
2116            .iter()
2117            .map(|(k, v)| (k.to_string(), v.clone()))
2118            .collect()
2119    }
2120
2121    fn sample_shallow() -> EvaluateDataValue {
2122        EvaluateDataValue::Map(ordered(&[
2123            (
2124                "price",
2125                EvaluateDataValue::Map(ordered(&[("eur", EvaluateDataValue::from("84"))])),
2126            ),
2127            (
2128                "fares",
2129                EvaluateDataValue::List(vec![
2130                    EvaluateDataValue::from("89 eur"),
2131                    EvaluateDataValue::Boolean(true),
2132                    EvaluateDataValue::Map(IndexMap::new()),
2133                    EvaluateDataValue::List(Vec::new()),
2134                ]),
2135            ),
2136        ]))
2137    }
2138
2139    #[test]
2140    fn debug_matches_derived_layout_compact_and_pretty() {
2141        let value = sample_shallow();
2142        assert_eq!(
2143            format!("{value:?}"),
2144            r#"Map({"price": Map({"eur": Literal("84")}), "fares": List([Literal("89 eur"), Boolean(true), Map({}), List([])])})"#
2145        );
2146        assert_eq!(
2147            format!("{value:#?}"),
2148            concat!(
2149                "Map(\n",
2150                "    {\n",
2151                "        \"price\": Map(\n",
2152                "            {\n",
2153                "                \"eur\": Literal(\n",
2154                "                    \"84\",\n",
2155                "                ),\n",
2156                "            },\n",
2157                "        ),\n",
2158                "        \"fares\": List(\n",
2159                "            [\n",
2160                "                Literal(\n",
2161                "                    \"89 eur\",\n",
2162                "                ),\n",
2163                "                Boolean(\n",
2164                "                    true,\n",
2165                "                ),\n",
2166                "                Map(\n",
2167                "                    {},\n",
2168                "                ),\n",
2169                "                List(\n",
2170                "                    [],\n",
2171                "                ),\n",
2172                "            ],\n",
2173                "        ),\n",
2174                "    },\n",
2175                ")",
2176            )
2177        );
2178    }
2179
2180    #[test]
2181    fn deep_chains_clone_eq_debug_and_drop_on_small_stack() {
2182        const DEPTH: usize = 1_000_000;
2183        std::thread::Builder::new()
2184            .stack_size(256 * 1024)
2185            .spawn(|| {
2186                let mut list = EvaluateDataValue::Literal("leaf".to_string());
2187                for _ in 0..DEPTH {
2188                    list = EvaluateDataValue::List(vec![list]);
2189                }
2190                let cloned = list.clone();
2191                assert!(list == cloned);
2192                let rendered = format!("{list:?}");
2193                assert_eq!(
2194                    rendered.len(),
2195                    DEPTH * "List([".len() + "Literal(\"leaf\")".len() + DEPTH * "])".len()
2196                );
2197                drop(cloned);
2198
2199                let mut map = EvaluateDataValue::Boolean(false);
2200                for _ in 0..DEPTH {
2201                    let mut level = IndexMap::new();
2202                    level.insert("n".to_string(), map);
2203                    map = EvaluateDataValue::Map(level);
2204                }
2205                let cloned = map.clone();
2206                assert!(map == cloned);
2207                assert!(map != list);
2208                drop(cloned);
2209                drop(map);
2210                drop(list);
2211            })
2212            .expect("spawn small-stack thread")
2213            .join()
2214            .expect("deep run data must not overflow the stack");
2215    }
2216
2217    #[test]
2218    fn eq_is_order_insensitive_for_maps_and_ordered_for_lists() {
2219        let forward = EvaluateDataValue::Map(ordered(&[
2220            ("a", EvaluateDataValue::from("1")),
2221            ("b", EvaluateDataValue::from("2")),
2222        ]));
2223        let reversed = EvaluateDataValue::Map(ordered(&[
2224            ("b", EvaluateDataValue::from("2")),
2225            ("a", EvaluateDataValue::from("1")),
2226        ]));
2227        assert!(forward == reversed);
2228        assert!(
2229            EvaluateDataValue::List(vec![
2230                EvaluateDataValue::from("1"),
2231                EvaluateDataValue::from("2")
2232            ]) != EvaluateDataValue::List(vec![
2233                EvaluateDataValue::from("2"),
2234                EvaluateDataValue::from("1")
2235            ])
2236        );
2237        assert!(EvaluateDataValue::from("1") != EvaluateDataValue::Boolean(true));
2238    }
2239
2240    #[test]
2241    fn request_error_names_first_key_in_caller_order() {
2242        let engine = load_engine(
2243            r#"
2244spec inner
2245data n: 1
2246
2247spec s
2248uses left: inner
2249uses right: inner
2250rule out: 1
2251"#,
2252        );
2253        let now = crate::DateTimeValue::now();
2254        let overlay = EvaluateDataValue::Map(ordered(&[("n", EvaluateDataValue::from("2"))]));
2255        let left_first = engine
2256            .evaluate(
2257                None,
2258                "s",
2259                Some(&now),
2260                ordered(&[("left", overlay.clone()), ("right", overlay.clone())]),
2261                None,
2262                false,
2263            )
2264            .expect_err("uses alias map is a request error");
2265        assert!(left_first.to_string().contains("'left'"), "{left_first}");
2266        assert!(!left_first.to_string().contains("'right'"), "{left_first}");
2267
2268        let right_first = engine
2269            .evaluate(
2270                None,
2271                "s",
2272                Some(&now),
2273                ordered(&[("right", overlay.clone()), ("left", overlay)]),
2274                None,
2275                false,
2276            )
2277            .expect_err("uses alias map is a request error");
2278        assert!(right_first.to_string().contains("'right'"), "{right_first}");
2279        assert!(!right_first.to_string().contains("'left'"), "{right_first}");
2280    }
2281
2282    fn roster_engine() -> crate::Engine {
2283        load_engine(
2284            r#"
2285spec person
2286data age: number
2287  -> minimum 0
2288data score: number
2289  -> minimum 0
2290
2291spec roster
2292uses person
2293data people: person list
2294rule n: people -> count
2295"#,
2296        )
2297    }
2298
2299    #[test]
2300    fn row_veto_names_first_field_in_caller_order() {
2301        let engine = roster_engine();
2302        let now = crate::DateTimeValue::now();
2303        let age_first = engine
2304            .evaluate(
2305                None,
2306                "roster",
2307                Some(&now),
2308                ordered(&[(
2309                    "people",
2310                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2311                        ("age", EvaluateDataValue::from("-1")),
2312                        ("score", EvaluateDataValue::from("-1")),
2313                    ]))]),
2314                )]),
2315                Some(&["n".to_string()]),
2316                false,
2317            )
2318            .expect("evaluate");
2319        let reason = age_first
2320            .results
2321            .get("n")
2322            .expect("n")
2323            .veto_reason
2324            .as_deref()
2325            .expect("veto");
2326        assert!(reason.contains("age"), "{reason}");
2327        assert!(!reason.contains("score"), "{reason}");
2328
2329        let score_first = engine
2330            .evaluate(
2331                None,
2332                "roster",
2333                Some(&now),
2334                ordered(&[(
2335                    "people",
2336                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2337                        ("score", EvaluateDataValue::from("-1")),
2338                        ("age", EvaluateDataValue::from("-1")),
2339                    ]))]),
2340                )]),
2341                Some(&["n".to_string()]),
2342                false,
2343            )
2344            .expect("evaluate");
2345        let reason = score_first
2346            .results
2347            .get("n")
2348            .expect("n")
2349            .veto_reason
2350            .as_deref()
2351            .expect("veto");
2352        assert!(reason.contains("score"), "{reason}");
2353        assert!(!reason.contains("age"), "{reason}");
2354    }
2355
2356    #[test]
2357    fn row_unknown_fields_are_ignored() {
2358        let engine = roster_engine();
2359        let now = crate::DateTimeValue::now();
2360        let bogus_first = engine
2361            .evaluate(
2362                None,
2363                "roster",
2364                Some(&now),
2365                ordered(&[(
2366                    "people",
2367                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2368                        ("bogus", EvaluateDataValue::from("1")),
2369                        ("also", EvaluateDataValue::from("2")),
2370                        ("age", EvaluateDataValue::from("1")),
2371                        ("score", EvaluateDataValue::from("1")),
2372                    ]))]),
2373                )]),
2374                None,
2375                false,
2376            )
2377            .expect("unknown row fields are ignored");
2378        assert!(bogus_first.results.get("n").is_some());
2379
2380        let also_first = engine
2381            .evaluate(
2382                None,
2383                "roster",
2384                Some(&now),
2385                ordered(&[(
2386                    "people",
2387                    EvaluateDataValue::List(vec![EvaluateDataValue::Map(ordered(&[
2388                        ("also", EvaluateDataValue::from("2")),
2389                        ("bogus", EvaluateDataValue::from("1")),
2390                        ("age", EvaluateDataValue::from("1")),
2391                        ("score", EvaluateDataValue::from("1")),
2392                    ]))]),
2393                )]),
2394                None,
2395                false,
2396            )
2397            .expect("unknown row fields are ignored");
2398        assert!(also_first.results.get("n").is_some());
2399    }
2400
2401    fn tags_engine(limits: crate::ResourceLimits) -> crate::Engine {
2402        let mut engine = crate::Engine::with_limits(limits);
2403        engine
2404            .load([(
2405                crate::SourceType::Volatile,
2406                "spec tagged\ndata tags: text list\nrule n: tags -> count".to_string(),
2407            )])
2408            .expect("load");
2409        engine
2410    }
2411
2412    fn evaluate_tags(engine: &crate::Engine, tags: Vec<String>) -> crate::Response {
2413        let now = crate::DateTimeValue::now();
2414        engine
2415            .evaluate(
2416                None,
2417                "tagged",
2418                Some(&now),
2419                ordered(&[(
2420                    "tags",
2421                    EvaluateDataValue::List(
2422                        tags.into_iter().map(EvaluateDataValue::Literal).collect(),
2423                    ),
2424                )]),
2425                Some(&["n".to_string()]),
2426                false,
2427            )
2428            .expect("evaluate")
2429    }
2430
2431    #[test]
2432    fn value_list_over_value_byte_limit_binds() {
2433        let engine = tags_engine(crate::ResourceLimits::default());
2434        let limit = crate::ResourceLimits::default().max_data_value_bytes;
2435        let tags: Vec<String> = (0..200).map(|i| format!("customer-tag-{i:04}")).collect();
2436        let total: usize = tags.iter().map(String::len).sum();
2437        assert!(
2438            total > limit,
2439            "fixture must exceed {limit} bytes, has {total}"
2440        );
2441
2442        let response = evaluate_tags(&engine, tags);
2443        let result = response.results.get("n").expect("n");
2444        assert_eq!(result.veto_reason, None);
2445        assert_eq!(result.result(), Some("200"));
2446    }
2447
2448    #[test]
2449    fn value_list_item_over_value_byte_limit_vetoes() {
2450        let engine = tags_engine(crate::ResourceLimits {
2451            max_data_value_bytes: 50,
2452            ..crate::ResourceLimits::default()
2453        });
2454        let response = evaluate_tags(
2455            &engine,
2456            vec!["vip".to_string(), "x".repeat(51), "trial".to_string()],
2457        );
2458        let reason = response
2459            .results
2460            .get("n")
2461            .expect("n")
2462            .veto_reason
2463            .as_deref()
2464            .expect("veto");
2465        assert!(
2466            reason.contains("list item 2 exceeds the size limit."),
2467            "{reason}"
2468        );
2469    }
2470}