//! Source code spans. //! //! The [`DefaultEmitter`](crate::DefaultEmitter) is generic over a [`Span`]. //! This library comes with two Span implementations: //! //! * one for `()` which acts as the no-op implementation for when you don't want to track spans //! * one for [`Range`] for when you do want to track spans //! //! To use the latter your reader however has to implement [`Position`]. //! You can easily use any existing reader by wrapping it in the [`PosTrackingReader`] struct //! which implements the [`Position`] trait and takes care of tracking the current position. use std::ops::{Add, Range}; use crate::reader::{IntoReader, Reader}; pub struct NoopOffset; /// A trait to be implemented by readers that track their own position. pub trait Position { /// Returns the byte index of the current position. fn position(&self) -> T; } impl Position for R { fn position(&self) -> NoopOffset { NoopOffset } } /// Wraps a [`Reader`] so that it implements [`Position`]. pub struct PosTrackingReader { /// The wrapped reader. reader: R, /// The current position. position: usize, } impl PosTrackingReader { /// Wraps the given [`Reader`] so that it implements [`Position`] with the position starting from 0. pub fn new<'a>(into_reader: impl IntoReader<'a, Reader = R>) -> Self { Self { reader: into_reader.into_reader(), position: 0, } } } impl Position for PosTrackingReader { fn position(&self) -> usize { self.position } } /// A byte range in the source code. pub trait Span: Default + Clone { type Offset: Add; /// Constructs a new span from the given byte offsets. fn new(start: Self::Offset, end: Self::Offset) -> Self; /// Extends the span by the length of the given string. fn push_str(&mut self, str: &str); } impl Span for () { type Offset = NoopOffset; fn new(_start: Self::Offset, _end: Self::Offset) -> Self { () } fn push_str(&mut self, _str: &str) {} } impl Add for NoopOffset { type Output = Self; fn add(self, _rhs: usize) -> NoopOffset { self } } impl Span for Range { type Offset = usize; fn new(start: Self::Offset, end: Self::Offset) -> Self { start - 1..end - 1 } fn push_str(&mut self, str: &str) { self.end += str.len(); } } impl Reader for PosTrackingReader { type Error = R::Error; fn read_char(&mut self) -> Result, Self::Error> { match self.reader.read_char()? { Some(char) => { self.position += char.len_utf8(); Ok(Some(char)) } None => Ok(None), } } fn try_read_string(&mut self, s: &str, case_sensitive: bool) -> Result { match self.reader.try_read_string(s, case_sensitive)? { true => { self.position += s.len(); Ok(true) } false => Ok(false), } } }