use crate::buffer::BufferID; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Display; use thiserror::Error; #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)] pub enum Rank { Zero, One, Two, Three, } impl Display for Rank { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Rank::Zero => write!(f, "Rank 0"), Rank::One => write!(f, "Rank 1"), Rank::Two => write!(f, "Rank 2"), Rank::Three => write!(f, "Rank 3"), } } } #[derive(Error, Debug)] pub enum SelectionError { #[error("Cannnot change selection of {from} to {to}")] InvalidPromotion { from: Rank, to: Rank }, } #[derive(Debug, Clone)] pub struct Interval { pub start: usize, pub end: usize, capture_groups: Vec<(usize, usize)>, } impl Interval { pub fn new(start: usize, end: usize) -> Self { Self { start, end, capture_groups: Vec::new(), } } pub fn is_disjoint(&self, other: &Interval) -> bool { self.start >= other.end || self.end <= other.start } } #[derive(Debug, Clone)] pub enum Selection { Position { buffer_id: BufferID, pos: usize, }, Range { buffer_id: BufferID, interval: Interval, }, Ranges { buffer_id: BufferID, ranges: Vec, }, MultiRanges { multi_ranges: HashMap>, }, } // 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), } impl Selection { pub fn empty() -> Self { Self::MultiRanges { multi_ranges: HashMap::new(), } } fn normalise(&mut self) { if let Self::MultiRanges { multi_ranges } = self { multi_ranges.retain(|_, v| !v.is_empty()) } } pub fn is_empty(&mut self) -> bool { self.normalise(); match self { Self::Position { .. } => return false, Self::Range { .. } => return false, Self::Ranges { ranges, .. } => ranges.is_empty(), Self::MultiRanges { multi_ranges } => multi_ranges.is_empty(), } } fn rank(&self) -> Rank { match self { Self::Position { .. } => Rank::Zero, Self::Range { .. } => Rank::One, Self::Ranges { .. } => Rank::Two, Self::MultiRanges { .. } => Rank::Three, } } fn promote(&self, to_rank: Rank) -> Result { use Rank::*; match (self, to_rank) { (Self::Position { .. }, Zero) => Ok(self.clone()), (Self::Range { .. }, One) => Ok(self.clone()), (Self::Ranges { .. }, Two) => Ok(self.clone()), (Self::MultiRanges { .. }, Three) => Ok(self.clone()), // Position (Self::Position { pos, buffer_id }, One) => Ok(Self::Range { interval: Interval { start: pos + 0, end: pos + 1, capture_groups: Vec::new(), }, buffer_id: *buffer_id, }), (Self::Position { pos, buffer_id }, Two) => Ok(Self::Ranges { ranges: vec![Interval { start: pos + 0, end: pos + 1, capture_groups: Vec::new(), }], buffer_id: *buffer_id, }), (Self::Position { pos, buffer_id }, Three) => { let mut map = HashMap::new(); map.insert( *buffer_id, vec![Interval { start: pos + 0, end: pos + 1, capture_groups: Vec::new(), }], ); Ok(Self::MultiRanges { multi_ranges: map }) } // Range promotions ( Self::Range { interval, buffer_id, }, Two, ) => Ok(Self::Ranges { ranges: vec![interval.clone()], buffer_id: *buffer_id, }), ( Self::Range { interval, buffer_id, }, Three, ) => { let mut map = HashMap::new(); map.insert(*buffer_id, vec![interval.clone()]); Ok(Self::MultiRanges { multi_ranges: map }) } // Ranges promotion (Self::Ranges { ranges, buffer_id }, Three) => { let mut map = HashMap::new(); map.insert(*buffer_id, ranges.clone()); Ok(Self::MultiRanges { multi_ranges: map }) } // all other cases (sel, target) => Err(SelectionError::InvalidPromotion { from: sel.rank(), to: target, }), } } fn buffers(&self) -> Vec { match self { Self::Position { buffer_id, .. } => vec![*buffer_id], Self::Range { buffer_id, .. } => vec![*buffer_id], Self::Ranges { buffer_id, .. } => vec![*buffer_id], Self::MultiRanges { multi_ranges } => multi_ranges.keys().copied().collect(), } } pub fn union(selections: Vec) -> Result { if selections.is_empty() { return Ok(Self::empty()); } let all_buffers: HashSet = selections.iter().flat_map(|s| s.buffers()).collect(); if all_buffers.is_empty() { return Ok(Self::empty()); } let max_rank = if all_buffers.len() > 1 { Rank::Three } else { selections .iter() .map(|s| s.rank()) .max() .unwrap_or(Rank::Two) .max(Rank::Two) // At least rank 2 }; let promoted: Result, SelectionError> = selections.iter().map(|s| s.promote(max_rank)).collect(); let promoted = promoted?; match max_rank { Rank::Two => { let mut all_intervals = Vec::new(); let buffer_id = *all_buffers.iter().next().unwrap(); for sel in &promoted { match sel { Self::Ranges { ranges, .. } => all_intervals.extend(ranges.clone()), _ => unreachable!("promote() to Rank::Two should always produce Ranges"), } } Ok(Self::Ranges { ranges: all_intervals, buffer_id, }) } Rank::Three => { let mut buffer_intervals: HashMap> = all_buffers.iter().map(|&b| (b, Vec::new())).collect(); for sel in &promoted { match sel { Self::MultiRanges { multi_ranges } => { for (&buffer, intervals) in multi_ranges { buffer_intervals .entry(buffer) .or_insert_with(Vec::new) .extend(intervals.clone()); } } _ => unreachable!( "promote() to Rank::Three should always produce MultiRanges" ), } } Ok(Self::MultiRanges { multi_ranges: buffer_intervals, }) } _ => unreachable!(), } } pub fn vectorise( fn_rank_zero: impl Fn(&S, BufferID, usize) -> Result, fn_rank_one: impl Fn(&S, BufferID, &Interval) -> Result, state: &S, selection: &Selection, ) -> 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 selection { &Self::Position { buffer_id, pos } => { fn_rank_zero(state, buffer_id, pos).map_err(ProcessingError) } Self::Range { interval, buffer_id, } => fn_rank_one(state, *buffer_id, interval).map_err(VectoriseError::ProcessingError), Self::Ranges { buffer_id, ranges } => { let results = do_rank_one(*buffer_id, ranges)?; Self::union(results).map_err(SelectionError) } Self::MultiRanges { 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) } } } }