From f3acb5cc0faf739f09112607bb98b6594184bd8b Mon Sep 17 00:00:00 2001 From: tslil Date: Mon, 24 Aug 2026 20:39:57 +0100 Subject: The big transposition-ing: move from functions as data to data of functions Basically Rust will fight you all the way if you treat it like Haskell, instead work with traits and impls on ZSTs. --- rprt-engine/src/selection.rs | 138 +++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 70 deletions(-) (limited to 'rprt-engine/src/selection.rs') diff --git a/rprt-engine/src/selection.rs b/rprt-engine/src/selection.rs index a3259b8..45a895b 100644 --- a/rprt-engine/src/selection.rs +++ b/rprt-engine/src/selection.rs @@ -1,4 +1,5 @@ use crate::buffer::BufferID; +use crate::state::EditorState; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Display; @@ -69,26 +70,55 @@ pub enum Selection { }, } -// rust is ... sigh. I have to make a wrapper class and worse still _other -// people_ have to deal with my wrapper class! -#[derive(Error, Debug)] -pub enum VectoriseError -where - E: std::error::Error + 'static, -{ - #[error("An error occurred during processing: {0}")] - ProcessingError(E), - - #[error("A selection error occurred: {0}")] - SelectionError(SelectionError), -} +/// A type that knows how to act on the rank-0 (scalar) and rank-1 (interval) +/// elements of a [`Selection`], and so can be vectorised over one. +pub trait Vectorisable { + /// Action on a rank-0 element. + fn rank0( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + pos: usize, + ) -> Result; -impl From for VectoriseError -where - E: std::error::Error + 'static, -{ - fn from(err: E) -> Self { - VectoriseError::ProcessingError(err) + /// Action on a rank-1 element. + fn rank1( + &self, + param: &P, + es: &EditorState, + buffer_id: BufferID, + interval: &Interval, + ) -> Result; + + /// Vectorise `sel`: apply `rank0`/`rank1` to every element and union + /// the results. + fn vectorise(&self, param: &P, es: &EditorState, sel: &Selection) -> Result { + // Single rank-0/rank-1 elements pass through as-is (no rank + // promotion); multi-element selections are unioned. + match sel { + Selection::Scalar { buffer_id, pos } => Self::rank0(self, param, es, *buffer_id, *pos), + Selection::Vector { + buffer_id, + interval, + } => Self::rank1(self, param, es, *buffer_id, interval), + Selection::Vectors { buffer_id, ranges } => { + let mut selections = Vec::with_capacity(ranges.len()); + for interval in ranges { + selections.push(Self::rank1(self, param, es, *buffer_id, interval)?); + } + Ok(Selection::union(selections)) + } + Selection::MultiVectors { multi_ranges } => { + let mut selections = Vec::new(); + for (buffer_id, ranges) in multi_ranges { + for interval in ranges { + selections.push(Self::rank1(self, param, es, *buffer_id, interval)?); + } + } + Ok(Selection::union(selections)) + } + } } } @@ -210,15 +240,20 @@ impl Selection { } } - pub fn union(selections: Vec) -> Result { + /// Union the given selections into a single selection. + /// + /// The target rank is the maximum rank needed to hold all inputs (rank 3 + /// if they span multiple buffers), to which every input can be promoted, + /// so this operation cannot fail. + pub fn union(selections: Vec) -> Self { if selections.is_empty() { - return Ok(Self::empty()); + return Self::empty(); } let all_buffers: HashSet = selections.iter().flat_map(|s| s.buffers()).collect(); if all_buffers.is_empty() { - return Ok(Self::empty()); + return Self::empty(); } let max_rank = if all_buffers.len() > 1 { @@ -232,9 +267,13 @@ impl Selection { .max(Rank::Two) // At least rank 2 }; - let promoted: Result, SelectionError> = - selections.iter().map(|s| s.promote(max_rank)).collect(); - let promoted = promoted?; + let promoted: Vec = selections + .iter() + .map(|s| { + s.promote(max_rank) + .expect("union promotes to the max rank present, which cannot fail") + }) + .collect(); match max_rank { Rank::Two => { @@ -248,10 +287,10 @@ impl Selection { } } - Ok(Self::Vectors { + Self::Vectors { ranges: all_intervals, buffer_id, - }) + } } Rank::Three => { let mut buffer_intervals: HashMap> = @@ -273,52 +312,11 @@ impl Selection { } } - Ok(Self::MultiVectors { + Self::MultiVectors { multi_ranges: buffer_intervals, - }) + } } _ => unreachable!(), } } - - pub fn vectorise( - &self, - state: &S, - fn_rank_zero: impl Fn(&S, BufferID, usize) -> Result + 'static, - fn_rank_one: impl Fn(&S, BufferID, &Interval) -> Result + 'static, - ) -> Result> - where - E: std::error::Error + 'static, - { - // look at this mess! - use VectoriseError::{ProcessingError, SelectionError}; - let do_rank_one = |b: usize, rs: &Vec| { - rs.iter() - .map(|int| fn_rank_one(state, b, int).map_err(ProcessingError)) - .collect::>() - }; - - match self { - &Self::Scalar { buffer_id, pos } => { - fn_rank_zero(state, buffer_id, pos).map_err(ProcessingError) - } - Self::Vector { - interval, - buffer_id, - } => fn_rank_one(state, *buffer_id, interval).map_err(VectoriseError::ProcessingError), - Self::Vectors { buffer_id, ranges } => { - let results = do_rank_one(*buffer_id, ranges)?; - Self::union(results).map_err(SelectionError) - } - Self::MultiVectors { multi_ranges } => { - let all_ok: Vec> = multi_ranges - .iter() - .map(|(buffer_id, ranges)| do_rank_one(*buffer_id, ranges)) - .collect::>()?; - // It would seem that Rust has no built in monadic flatten, or in general cannot lift things to operate on Result... :( - let results: Vec = all_ok.into_iter().flatten().collect(); - Self::union(results).map_err(SelectionError) - } - } - } } -- cgit v1.2.3