use crate::computation::{OperationResult, VetoType};
use crate::evaluation::{tree, EvaluationContext};
use crate::planning::execution_plan::{validate_value_against_type, ExecutionPlan};
use crate::planning::semantics::{
DataDefinition, DataPath, InstanceKey, LemmaType, RowEnd, RowOrigin, SpecValue, ValueKind,
};
use std::sync::Arc;
fn row_field_violation(
template: &ExecutionPlan,
field: &DataPath,
value: &OperationResult,
) -> Option<String> {
let OperationResult::Value(bound) = value else {
return None;
};
let definition = template
.data
.get(field)
.expect("BUG: row field path must be data of the template");
let field_type = definition
.resolved_type_arc()
.expect("BUG: row field must be typed data of the template");
validate_value_against_type(
field_type,
&bound.to_literal(),
&template.resolved_types.unit_index,
None,
)
.err()
}
pub(crate) fn checked_row_field(
template: &ExecutionPlan,
field: &DataPath,
value: OperationResult,
) -> OperationResult {
match row_field_violation(template, field, &value) {
None => value,
Some(message) => OperationResult::Veto(VetoType::computation(format!(
"Field '{}' is invalid: {message}",
field.input_key()
))),
}
}
pub(crate) fn checked_deferred_field(
key: &InstanceKey,
template: &ExecutionPlan,
field: &DataPath,
value: OperationResult,
) -> OperationResult {
match row_field_violation(template, field, &value) {
None => value,
Some(message) => {
let name = key
.field_key(&field.input_key())
.unwrap_or_else(|| field.input_key());
OperationResult::Veto(VetoType::computation(format!(
"Reference '{name}' violates its declared constraint: {message}"
)))
}
}
}
pub(crate) fn take_row_context(
key: &InstanceKey,
plan: &ExecutionPlan,
template: &ExecutionPlan,
ctx: &mut EvaluationContext,
) -> EvaluationContext {
tree::build_row_context(key, plan, template, ctx)
}
pub(crate) fn self_rows(
key: &InstanceKey,
template: &ExecutionPlan,
slots: &[DataPath],
bindings: &[(usize, OperationResult)],
) -> Vec<InstanceKey> {
let mut children = Vec::new();
for (slot, value) in bindings {
let (path, definition) = template
.data
.get_index(*slot)
.expect("BUG: row binding slot is template data");
let is_read_self_list = matches!(definition, DataDefinition::InstanceList { .. })
&& slots.contains(path)
&& template
.instance_lists
.get(path)
.is_some_and(|source| source.identity == template.identity);
if !is_read_self_list {
continue;
}
let OperationResult::Value(bound) = value else {
continue;
};
let ValueKind::List(items) = &bound.value else {
panic!("BUG: self-list field `{path}` of {key} bound to a non-list value");
};
for item in items.iter() {
match &item.value {
ValueKind::Spec(SpecValue::Row(child)) => children.push(child.clone()),
other => panic!("BUG: self-list field `{path}` holds a non-row value {other:?}"),
}
}
}
children
}
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct ReadRow {
pub(crate) key: InstanceKey,
pub(crate) position: Option<usize>,
}
impl ReadRow {
pub(crate) fn listed(key: InstanceKey, position: usize) -> Self {
let position = matches!(key.row.origin, RowOrigin::Import { .. }).then_some(position);
Self { key, position }
}
pub(crate) fn list_position(&self) -> (usize, bool) {
match (&self.key.row.origin, self.position) {
(RowOrigin::Import { .. }, Some(position)) => (position + 1, true),
(origin, _) => origin.list_position(),
}
}
}
#[derive(Clone)]
pub(crate) struct RowRead {
pub(crate) row: ReadRow,
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: &InstanceKey, key: &str) -> Option<String> {
row.field_key(key)
}
pub(crate) fn from_row(
result: OperationResult,
key: &InstanceKey,
template: &ExecutionPlan,
ctx: &mut EvaluationContext,
) -> OperationResult {
match row_result(result, key, template) {
OperationResult::Veto(VetoType::MissingRowData { key }) => {
ctx.missing_row_keys.push(key.clone());
OperationResult::Veto(VetoType::MissingRowData { key })
}
OperationResult::Veto(VetoType::MissingData { data, suggestion }) => {
ctx.missing_row_keys.push(data.input_key());
let suggestion = suggestion.or_else(|| ctx.missing_data_suggestion(&data));
OperationResult::Veto(VetoType::MissingData { data, suggestion })
}
translated => translated,
}
}
pub(crate) fn owner_result(
result: OperationResult,
owner: Option<&InstanceKey>,
plan: &ExecutionPlan,
) -> OperationResult {
match owner {
Some(owner) => row_result(result, owner, plan),
None => match result {
OperationResult::Veto(VetoType::MissingData { data, .. }) => {
OperationResult::Veto(VetoType::MissingRowData {
key: data.input_key(),
})
}
other => other,
},
}
}
pub(crate) fn row_result(
result: OperationResult,
key: &InstanceKey,
template: &ExecutionPlan,
) -> OperationResult {
let data = match result {
OperationResult::Veto(VetoType::MissingData { data, .. })
if template.data_leaf.contains_key(&data) =>
{
data
}
OperationResult::Value(_) | OperationResult::Veto(_) => return result,
};
if let Some(mut segments) = key.segments() {
segments.extend(data.segments);
return OperationResult::Veto(VetoType::missing_data(
DataPath::new(segments, data.data),
None,
));
}
let field = data.input_key();
match row_key(key, &field) {
Some(key) => OperationResult::Veto(VetoType::MissingRowData { key }),
None => OperationResult::Veto(VetoType::computation(format!(
"Field '{field}' is not set on {key}."
))),
}
}