use crate::computation::{OperationResult, VetoType};
use crate::evaluation::{tree, EvaluationContext};
use crate::planning::execution_plan::{validate_value_against_type, ExecutionPlan};
use crate::planning::semantics::{
row_field_input_key, DataPath, LemmaType, RowEnd, RowInstance, RowOrigin, SpecValue, ValueKind,
};
use std::sync::Arc;
pub(crate) fn checked_row_field(
template: &ExecutionPlan,
field: &DataPath,
value: OperationResult,
) -> OperationResult {
let OperationResult::Value(bound) = &value else {
return value;
};
let (_, _, definition) = template
.peel_data(field)
.expect("BUG: row field path must peel on template");
let field_type = definition
.resolved_type_arc()
.expect("BUG: row field must be typed data of the template");
match validate_value_against_type(
field_type,
&bound.to_literal(),
&template.resolved_types.unit_index,
) {
Ok(()) => value,
Err(message) => OperationResult::Veto(VetoType::computation(format!(
"Field '{}' is invalid: {message}",
field.input_key()
))),
}
}
pub(crate) fn take_row_context(
row: &Arc<RowInstance>,
template: &ExecutionPlan,
ctx: &mut EvaluationContext,
) -> EvaluationContext {
tree::build_row_context(row, template, ctx)
}
pub(crate) fn self_rows(row: &RowInstance, template: &ExecutionPlan) -> Vec<Arc<RowInstance>> {
let mut children = Vec::new();
for (path, binding) in &row.bindings {
let is_self_list = template
.instance_lists
.get(path)
.is_some_and(|source| source.identity == template.identity);
if !is_self_list {
continue;
}
let crate::computation::OperationResult::Value(bound) = binding else {
continue;
};
let ValueKind::List(items) = &bound.value else {
panic!("BUG: self-list field `{path}` bound to a non-list value");
};
for item in items {
match &item.value {
ValueKind::Spec(SpecValue::Row(child)) => children.push(Arc::clone(child)),
other => panic!("BUG: self-list field `{path}` holds a non-row value {other:?}"),
}
}
}
children
}
#[derive(Clone)]
pub(crate) struct RowRead {
pub(crate) row: Arc<RowInstance>,
pub(crate) end: RowEnd,
pub(crate) result: OperationResult,
pub(crate) result_type: Arc<LemmaType>,
}
impl RowRead {
pub(crate) fn same_field(&self, other: &RowRead) -> bool {
self.row == other.row && self.end == other.end
}
}
pub(crate) fn row_key(row: &RowInstance, key: &str) -> String {
match &row.origin {
RowOrigin::Numbered { list, number } => row_field_input_key(list, *number, key),
RowOrigin::Appended { number } => format!("appended row {number}.{key}"),
}
}
pub(crate) fn from_row(
result: OperationResult,
row: &RowInstance,
template: &ExecutionPlan,
ctx: &mut EvaluationContext,
) -> OperationResult {
let translated = row_result(result, row, template);
match &translated {
OperationResult::Veto(VetoType::MissingRowData { key }) => {
ctx.missing_row_keys.push(key.clone());
}
OperationResult::Veto(VetoType::MissingData { data, .. }) => {
ctx.missing_row_keys.push(data.input_key());
}
OperationResult::Value(_) | OperationResult::Veto(_) => {}
}
translated
}
pub(crate) fn row_result(
result: OperationResult,
row: &RowInstance,
template: &ExecutionPlan,
) -> OperationResult {
let key = match &result {
OperationResult::Veto(VetoType::MissingData { data, .. })
if template.data.contains_key(data) =>
{
data.input_key()
}
OperationResult::Veto(VetoType::MissingRowData { key }) => key.clone(),
OperationResult::Value(_) | OperationResult::Veto(_) => return result,
};
match &row.origin {
RowOrigin::Numbered { .. } => OperationResult::Veto(VetoType::MissingRowData {
key: row_key(row, &key),
}),
RowOrigin::Appended { .. } => OperationResult::Veto(VetoType::computation(format!(
"{} has no value for '{key}'.",
row.origin
))),
}
}