use std::{ fmt, ops::{Deref, Range}, }; use eyre::{bail, eyre}; #[derive(Clone, Eq, PartialEq)] pub struct Span<'a> { complete_str: &'a str, range: Range, } impl<'a> Span<'a> { pub fn new(complete_str: &'a str) -> Self { Self { complete_str, range: 0..complete_str.len(), } } pub const fn empty() -> Self { Span { complete_str: "", range: 0..0, } } pub fn get(&self, slice: Range) -> Option { let start = self.range.start.checked_add(slice.start)?; let end = self.range.start.checked_add(slice.end)?; if end > self.range.end || end < start { return None; } Some(Self { complete_str: self.complete_str, range: Range { start, end }, }) } pub fn complete_str(&self) -> Self { Self::new(self.complete_str) } pub fn split_at(&self, i: usize) -> Option<(Self, Self)> { let head = self.get(0..i)?; let tail = self.get(i..self.range.len())?; Some((head, tail)) } pub fn trim_end_matches(&self, p: &str) -> Self { if !self.ends_with(p) { return self.clone(); } Self { range: self.range.start..self.range.end - p.len(), complete_str: self.complete_str, } } /// Try to merge the spans. /// /// If either spans is empty, this just returns the other one. /// This only works if spans are pointing into the same backing buffer, and are adjacent. pub fn try_merge(&self, other: &Self) -> eyre::Result { if self.is_empty() { return Ok(other.clone()); } if other.is_empty() { return Ok(self.clone()); } if self.complete_str.as_ptr() != other.complete_str.as_ptr() { bail!("Can't merge different strings"); } if self.range.end == other.range.start { Ok(Self { range: self.range.start..other.range.end, ..*self }) } else if self.range.start == other.range.end { Ok(Self { range: other.range.start..self.range.end, ..*self }) } else { Err(eyre!("String: {:?}", self.complete_str) .wrap_err(eyre!("Span 2: {:?}", other.deref())) .wrap_err(eyre!("Span 1: {:?}", self.deref())) .wrap_err("Can't merge disjoint string spans")) } } } impl Deref for Span<'_> { type Target = str; fn deref(&self) -> &Self::Target { &self.complete_str[self.range.clone()] } } impl<'a> fmt::Debug for Span<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Span") .field(&self.range) .field(&self.deref()) .finish() } }