1 //! Provides the `IndexStr` type to keep track of a substring's index into its
2 //! original string is.
3 
4 use std::fmt;
5 use std::ops::{Range, RangeFrom, RangeTo};
6 use string::String;
7 
8 /// The `IndexStr` type allows us to take substrings from an original input and
9 /// keep track of what index the substring is at in the original input.
10 #[derive(Clone, Copy, PartialEq, Eq)]
11 pub struct IndexStr<'a> {
12     idx: usize,
13     string: &'a [u8],
14 }
15 
16 #[allow(dead_code)]
17 impl<'a> IndexStr<'a> {
18     /// Construct a new `IndexStr` (with `index == 0`) from the given input.
19     #[inline]
new(string: &'a [u8]) -> IndexStr<'a>20     pub fn new(string: &'a [u8]) -> IndexStr<'a> {
21         IndexStr {
22             idx: 0,
23             string: string,
24         }
25     }
26 
27     /// Return the length of the string.
28     #[inline]
len(&self) -> usize29     pub fn len(&self) -> usize {
30         self.string.len()
31     }
32 
33     /// Return true if the string is empty, false otherwise.
34     #[inline]
is_empty(&self) -> bool35     pub fn is_empty(&self) -> bool {
36         self.string.is_empty()
37     }
38 
39     /// Get the index into the original input that this `IndexStr` is at.
40     #[inline]
index(&self) -> usize41     pub fn index(&self) -> usize {
42         self.idx
43     }
44 
45     /// Peek at the next byte in this `IndexStr`.
46     #[inline]
peek(&self) -> Option<u8>47     pub fn peek(&self) -> Option<u8> {
48         self.as_ref().get(0).cloned()
49     }
50 
51     /// Peek at the second next byte in this `IndexStr`.
52     #[inline]
peek_second(&self) -> Option<u8>53     pub fn peek_second(&self) -> Option<u8> {
54         self.as_ref().get(1).cloned()
55     }
56 
57     /// Split the string in two at the given index, resulting in the tuple where
58     /// the first item has range `[0, idx)`, and the second has range `[idx,
59     /// len)`.
60     ///
61     /// Panics if the index is out of bounds.
62     #[inline]
split_at(&self, idx: usize) -> (IndexStr<'a>, IndexStr<'a>)63     pub fn split_at(&self, idx: usize) -> (IndexStr<'a>, IndexStr<'a>) {
64         (self.range_to(..idx), self.range_from(idx..))
65     }
66 
67     /// The same as `split_at`, but returns a `Result` rather than panicking
68     /// when the index is out of bounds.
69     #[inline]
try_split_at(&self, idx: usize) -> Option<(IndexStr<'a>, IndexStr<'a>)>70     pub fn try_split_at(&self, idx: usize) -> Option<(IndexStr<'a>, IndexStr<'a>)> {
71         if idx > self.len() {
72             None
73         } else {
74             Some(self.split_at(idx))
75         }
76     }
77 
78     /// Pop the next byte off the front of this string, returning it and the new
79     /// tail string, or `None` if this string is empty.
80     #[inline]
next(&self) -> Option<(u8, IndexStr<'a>)>81     pub fn next(&self) -> Option<(u8, IndexStr<'a>)> {
82         if self.is_empty() {
83             None
84         } else {
85             let byte = self.string[0];
86             Some((byte, self.range_from(1..)))
87         }
88     }
89 
90     /// Pop the next byte off the front of this string, returning it and the new
91     /// tail string, or the given error if this string is empty.
92     #[inline]
next_or<E>(&self, error: E) -> Result<(u8, IndexStr<'a>), E>93     pub fn next_or<E>(&self, error: E) -> Result<(u8, IndexStr<'a>), E> {
94         self.next().ok_or(error)
95     }
96 }
97 
98 /// # Range Methods
99 ///
100 /// Unfortunately, `std::ops::Index` *must* return a reference, so we can't
101 /// implement `Index<Range<usize>>` to return a new `IndexStr` the way we would
102 /// like to. Instead, we abandon fancy indexing operators and have these plain
103 /// old methods.
104 ///
105 /// All of these methods panic on an out-of-bounds index.
106 #[allow(dead_code)]
107 impl<'a> IndexStr<'a> {
108     /// Take the given `start..end` range of the underlying string and return a
109     /// new `IndexStr`.
110     #[inline]
range(&self, idx: Range<usize>) -> IndexStr<'a>111     pub fn range(&self, idx: Range<usize>) -> IndexStr<'a> {
112         IndexStr {
113             idx: self.idx + idx.start,
114             string: &self.string[idx],
115         }
116     }
117 
118     /// Take the given `start..` range of the underlying string and return a new
119     /// `IndexStr`.
120     #[inline]
range_from(&self, idx: RangeFrom<usize>) -> IndexStr<'a>121     pub fn range_from(&self, idx: RangeFrom<usize>) -> IndexStr<'a> {
122         IndexStr {
123             idx: self.idx + idx.start,
124             string: &self.string[idx],
125         }
126     }
127 
128     /// Take the given `..end` range of the underlying string and return a new
129     /// `IndexStr`.
130     #[inline]
range_to(&self, idx: RangeTo<usize>) -> IndexStr<'a>131     pub fn range_to(&self, idx: RangeTo<usize>) -> IndexStr<'a> {
132         IndexStr {
133             idx: self.idx,
134             string: &self.string[idx],
135         }
136     }
137 }
138 
139 impl<'a> AsRef<[u8]> for IndexStr<'a> {
140     #[inline]
as_ref(&self) -> &[u8]141     fn as_ref(&self) -> &[u8] {
142         self.string
143     }
144 }
145 
146 impl<'a> From<&'a [u8]> for IndexStr<'a> {
from(s: &[u8]) -> IndexStr147     fn from(s: &[u8]) -> IndexStr {
148         IndexStr::new(s)
149     }
150 }
151 
152 impl<'a> Into<&'a [u8]> for IndexStr<'a> {
into(self) -> &'a [u8]153     fn into(self) -> &'a [u8] {
154         self.string
155     }
156 }
157 
158 impl<'a, 'b> PartialEq<&'a [u8]> for IndexStr<'b> {
eq(&self, rhs: &&[u8]) -> bool159     fn eq(&self, rhs: &&[u8]) -> bool {
160         self.string == *rhs
161     }
162 }
163 
164 impl<'a> fmt::Debug for IndexStr<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result165     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166         write!(
167             f,
168             "IndexStr {{ idx: {}, string: \"{}\" }}",
169             self.idx,
170             String::from_utf8_lossy(self.as_ref())
171         )
172     }
173 }
174