use super::palette::StyleId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Span {
pub start: usize,
pub end: usize,
pub style: StyleId,
pub priority: u16,
pub padded: bool,
}
#[cfg(test)]
impl Span {
pub fn new(start: usize, end: usize, style: StyleId, priority: u16) -> Self {
Self {
start,
end,
style,
priority,
padded: false,
}
}
}
#[derive(Debug)]
pub(crate) struct Collector {
spans: Vec<Span>,
}
impl Collector {
pub const fn new() -> Self {
Self { spans: Vec::new() }
}
pub fn push(&mut self, start: usize, end: usize, style: StyleId) {
self.push_impl(start, end, style, false);
}
pub fn push_padded(&mut self, start: usize, end: usize, style: StyleId) {
self.push_impl(start, end, style, true);
}
fn push_impl(&mut self, start: usize, end: usize, style: StyleId, padded: bool) {
if start >= end {
return;
}
if let Some(last) = self.spans.last_mut()
&& last.style == style
&& last.padded == padded
&& last.end == start
{
last.end = end;
return;
}
self.spans.push(Span {
start,
end,
style,
priority: 0,
padded,
});
}
#[cfg(test)]
pub(crate) fn into_spans(self) -> Vec<Span> {
self.spans
}
pub fn reset(&mut self) {
self.spans.clear();
}
pub fn drain_into(&mut self, spans: &mut Vec<Span>, priority: u16) {
for span in &mut self.spans {
span.priority = priority;
}
spans.append(&mut self.spans);
}
}
pub(crate) trait Finder: std::fmt::Debug + Sync + Send + BoxedCloneFinder {
fn find_spans(&self, input: &str, collector: &mut Collector);
}
pub(crate) trait BoxedCloneFinder {
fn boxed_clone(&self) -> Box<dyn Finder>;
}
impl<T: Finder + Clone + 'static> BoxedCloneFinder for T {
fn boxed_clone(&self) -> Box<dyn Finder> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Finder> {
fn clone(&self) -> Self {
self.as_ref().boxed_clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn coalesces_adjacent_same_style() {
let style = StyleId::new(0);
let mut collector = Collector::new();
collector.push(0, 1, style);
collector.push(1, 2, style);
collector.push(2, 3, style);
let spans = collector.into_spans();
assert_eq!(spans.len(), 1);
assert_eq!(spans[0].start, 0);
assert_eq!(spans[0].end, 3);
}
#[test]
fn does_not_coalesce_different_styles() {
let red = StyleId::new(0);
let blue = StyleId::new(1);
let mut collector = Collector::new();
collector.push(0, 1, red);
collector.push(1, 2, blue);
collector.push(2, 3, red);
let spans = collector.into_spans();
assert_eq!(spans.len(), 3);
}
#[test]
fn does_not_coalesce_non_adjacent() {
let style = StyleId::new(0);
let mut collector = Collector::new();
collector.push(0, 1, style);
collector.push(3, 4, style);
let spans = collector.into_spans();
assert_eq!(spans.len(), 2);
}
#[test]
fn reset_clears_spans() {
let style = StyleId::new(0);
let mut collector = Collector::new();
collector.push_padded(0, 3, style);
collector.reset();
assert!(collector.into_spans().is_empty());
}
#[test]
fn push_padded_marks_span_padded() {
let style = StyleId::new(0);
let mut collector = Collector::new();
collector.push_padded(0, 3, style);
let spans = collector.into_spans();
assert_eq!(spans.len(), 1);
assert!(spans[0].padded);
}
#[test]
fn does_not_coalesce_padded_with_plain() {
let style = StyleId::new(0);
let mut collector = Collector::new();
collector.push(0, 1, style);
collector.push_padded(1, 2, style);
let spans = collector.into_spans();
assert_eq!(spans.len(), 2);
assert!(!spans[0].padded);
assert!(spans[1].padded);
}
}