//! Source code offsets. //! //! The [`Emitter`](crate::Emitter) is generic over an [`Offset`]. //! This library comes with two Offset implementations: //! //! * [`NoopOffset`] for when you don't want to track source offsets //! * `usize` for when you do want to track source offsets //! //! 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::fmt::Debug; use std::ops::{Add, AddAssign, Sub}; use crate::reader::{IntoReader, Reader}; /// A byte offset in the source. pub trait Offset: Default + Copy + Eq + Ord + Add + Sub + AddAssign + Debug { } impl Offset for usize {} impl Offset for NoopOffset {} /// A zero-sized no-op implementation of [`Offset`] (for when you don't want to track offsets). #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] 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 } } impl Add for NoopOffset { type Output = Self; fn add(self, _rhs: usize) -> NoopOffset { self } } impl Sub for NoopOffset { type Output = Self; fn sub(self, _rhs: usize) -> NoopOffset { self } } impl AddAssign for NoopOffset { fn add_assign(&mut self, _rhs: usize) {} } 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 += self.reader.len_of_char_in_current_encoding(char); 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 => { for c in s.chars() { self.position += self.reader.len_of_char_in_current_encoding(c); } Ok(true) } false => Ok(false), } } fn len_of_char_in_current_encoding(&self, c: char) -> usize { self.reader.len_of_char_in_current_encoding(c) } }