use crate::{ expression::{Composite, HookKind}, selection::{Selection, SelectionError, VectoriseError}, selection_functions::{evaluate_selection_function, SFError}, state::{EditorState, GroupedChangeError, StateChange, StateResult}, }; 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, } 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 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 (sel, g_changes) = _eval(es, *g, Some(f_result), right.or_else(|| left), 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 (g_result, mut changes) = _eval(es, *g, right.or_else(|| left.clone()), 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(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(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) }