use crate::{ expression::{Composite, HookKind}, selection::{Selection, SelectionError, VectoriseError}, selection_functions::{evaluate_selection_function, SFError}, state::{EditorState, GroupedChangeError, StateChange, StateResult}, text::Text, }; use thiserror::Error; #[derive(Error, Debug)] pub enum EvaluationError { #[error("{0}")] SelectionFunctionError(VectoriseError), #[error("{0}")] SelectionUnionError(SelectionError), #[error("Error in processing group {0}")] GroupError(GroupedChangeError), #[error("Invalid right-only application")] IROApplication, #[error("Not yet implemented {0}")] UnimplementedError(&'static str), #[error("Hook used in niladic context")] NiladicRightHook, #[error("Text argument provided to a selection function")] TextArgument, } // Helper functions // Absolutely crazy that in 2026 Rust doesn't have a built-in Either type OR // (even better) a way to do anonymous sum types! Of course it has anonymous // product types, but big Cartesian doesn't want you to have + #[derive(Clone)] enum Either { L(L), R(R), } fn ensure_selection( left: Option>, ) -> Result, EvaluationError> { match left { Some(Either::L(left)) => Ok(Some(left)), None => Ok(None), _ => Err(EvaluationError::TextArgument), } } fn commit_if_needed( es: &mut EditorState, changes: Vec, commit: bool, ) -> Vec { if commit { es.commit_changes(changes); Vec::new() } else { changes } } fn _eval( es: &mut EditorState, comp: Composite, left: Option>, right: Option, commit: bool, ) -> Result { if left.is_none() && right.is_some() { return Err(EvaluationError::IROApplication); }; match comp { Composite::SelectionFunction { func, search_mod, result_transform, } => { let left: Option = ensure_selection(left)?; let sel = evaluate_selection_function(es, func, search_mod, result_transform, left, right) .map_err(EvaluationError::SelectionFunctionError)?; Ok((sel, Vec::new())) } Composite::TextFunction { func, swapped } => { Err(EvaluationError::UnimplementedError("text function")) } Composite::Hook { kind, f, g } => match kind { HookKind::Before => { let (f_result, mut changes) = _eval(es, *f, left.clone(), None, commit)?; changes = commit_if_needed(es, changes, commit); let right = if right.is_some() { Ok(right) } else { ensure_selection(left) }?; let (sel, g_changes) = _eval(es, *g, Either::L(f_result).into(), right, commit)?; let more_changes = commit_if_needed(es, g_changes, commit); changes.extend(more_changes); Ok((sel, changes)) } HookKind::After => { if left.is_none() { return Err(EvaluationError::NiladicRightHook); } let combi_left = match right { Some(right) => Some(Either::L(right)), None => left.clone(), }; let (g_result, mut changes) = _eval(es, *g, combi_left, None, commit)?; changes = commit_if_needed(es, changes, commit); let (sel, f_changes) = _eval(es, *f, left.clone(), Some(g_result), commit)?; let more_changes = commit_if_needed(es, f_changes, commit); changes.extend(more_changes); Ok((sel, changes)) } }, Composite::Train2 { f, g } => { let (left, mut f_changes) = _eval(es, *f, left, right, commit)?; f_changes = commit_if_needed(es, f_changes, commit); let (sel, g_changes) = _eval(es, *g, Some(Either::L(left)), None, commit)?; let more_changes = commit_if_needed(es, g_changes, commit); f_changes.extend(more_changes); Ok((sel, f_changes)) } Composite::Train3 { f, g, h } => { let (f_left, mut f_changes) = _eval(es, *f, left.clone(), right.clone(), commit)?; f_changes = commit_if_needed(es, f_changes, commit); let (g_right, h_changes) = _eval(es, *h, left, right, commit)?; let h_changes = commit_if_needed(es, h_changes, commit); let (sel, g_changes) = _eval(es, *g, Some(Either::L(f_left)), Some(g_right), commit)?; let g_changes = commit_if_needed(es, g_changes, commit); f_changes.extend(h_changes); f_changes.extend(g_changes); Ok((sel, f_changes)) } Composite::Group { operations } => { let results: Vec<(Selection, Vec)> = operations .into_iter() .map(|op| _eval(es, op, left.clone(), right.clone(), commit)) .collect::>()?; let (selections, arms) = results.into_iter().unzip(); let states = if commit { es.commit_changes_grouped(arms).map(|_| Vec::new()) } else { es.validate_grouped_changes(&arms) .map(|_| arms.into_iter().flatten().collect()) } .map_err(EvaluationError::GroupError)?; let sel = Selection::union(selections).map_err(EvaluationError::SelectionUnionError)?; Ok((sel, states)) } } } pub fn evaluate(es: &mut EditorState, comp: Composite) -> Result { let (sel, changes) = _eval(es, comp, None, None, true)?; es.commit_changes(changes); Ok(sel) }