aboutsummaryrefslogtreecommitdiff
path: root/src/offset.rs
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2023-08-17 08:59:05 +0200
committerMartin Fischer <martin@push-f.com>2023-08-19 11:41:55 +0200
commitc169e78f120ea9be451f337306b8bff6c1fb4955 (patch)
treed04dd60435a2191f3d34b3680a87d92ff5df0edc /src/offset.rs
parent91074b6e7e6e8463f15ca26bc39e70b80f954227 (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.rs117
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),
+ }
+ }
+}