diff options
author | Martin Fischer <martin@push-f.com> | 2023-08-17 08:59:05 +0200 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2023-08-19 11:41:55 +0200 |
commit | c169e78f120ea9be451f337306b8bff6c1fb4955 (patch) | |
tree | d04dd60435a2191f3d34b3680a87d92ff5df0edc /src/offset.rs | |
parent | 91074b6e7e6e8463f15ca26bc39e70b80f954227 (diff) |
refactor!: remove Span trait, just use Range
`std::mem::size_of::<Range<NoopOffset>>()` is 0
so there's no need to abstract over Range.
Diffstat (limited to 'src/offset.rs')
-rw-r--r-- | src/offset.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/offset.rs b/src/offset.rs new file mode 100644 index 0000000..f1f436d --- /dev/null +++ b/src/offset.rs @@ -0,0 +1,117 @@ +//! Source code offsets. +//! +//! The [`DefaultEmitter`](crate::DefaultEmitter) 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<usize>`]. +//! You can easily use any existing reader by wrapping it in the [`PosTrackingReader`] struct +//! which implements the [`Position<usize>`] 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<usize, Output = Self> + + Sub<usize, Output = Self> + + AddAssign<usize> + + 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<T> { + /// Returns the byte index of the current position. + fn position(&self) -> T; +} + +impl<R: Reader> Position<NoopOffset> for R { + fn position(&self) -> NoopOffset { + NoopOffset + } +} + +/// Wraps a [`Reader`] so that it implements [`Position<usize>`]. +pub struct PosTrackingReader<R> { + /// The wrapped reader. + reader: R, + /// The current position. + position: usize, +} + +impl<R> PosTrackingReader<R> { + /// Wraps the given [`Reader`] so that it implements [`Position<usize>`] 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<R> Position<usize> for PosTrackingReader<R> { + fn position(&self) -> usize { + self.position - 1 + } +} + +impl Add<usize> for NoopOffset { + type Output = Self; + + fn add(self, _rhs: usize) -> NoopOffset { + self + } +} + +impl Sub<usize> for NoopOffset { + type Output = Self; + + fn sub(self, _rhs: usize) -> NoopOffset { + self + } +} + +impl AddAssign<usize> for NoopOffset { + fn add_assign(&mut self, _rhs: usize) {} +} + +impl<R: Reader> Reader for PosTrackingReader<R> { + type Error = R::Error; + + fn read_char(&mut self) -> Result<Option<char>, 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<bool, Self::Error> { + match self.reader.try_read_string(s, case_sensitive)? { + true => { + self.position += s.len(); + Ok(true) + } + false => Ok(false), + } + } +} |