summaryrefslogtreecommitdiff
path: root/src/offset.rs
blob: bdb069a344c8e4baf15cfd0c822e954224fc921d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! 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<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
    }
}

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 += 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<bool, Self::Error> {
        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)
    }
}