1//! Provides the `IndexStr` type to keep track of a substring's index into its
2//! original string is.
34use alloc::string::String;
5use core::fmt;
6use core::ops::{Range, RangeFrom, RangeTo};
78/// 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)]
11pub struct IndexStr<'a> {
12 idx: usize,
13 string: &'a [u8],
14}
1516#[allow(dead_code)]
17impl<'a> IndexStr<'a> {
18/// Construct a new `IndexStr` (with `index == 0`) from the given input.
19#[inline]
20pub fn new(string: &'a [u8]) -> IndexStr<'a> {
21 IndexStr {
22 idx: 0,
23 string: string,
24 }
25 }
2627/// Return the length of the string.
28#[inline]
29pub fn len(&self) -> usize {
30self.string.len()
31 }
3233/// Return true if the string is empty, false otherwise.
34#[inline]
35pub fn is_empty(&self) -> bool {
36self.string.is_empty()
37 }
3839/// Get the index into the original input that this `IndexStr` is at.
40#[inline]
41pub fn index(&self) -> usize {
42self.idx
43 }
4445/// Peek at the next byte in this `IndexStr`.
46#[inline]
47pub fn peek(&self) -> Option<u8> {
48self.as_ref().get(0).cloned()
49 }
5051/// Peek at the second next byte in this `IndexStr`.
52#[inline]
53pub fn peek_second(&self) -> Option<u8> {
54self.as_ref().get(1).cloned()
55 }
5657/// 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]
63pub fn split_at(&self, idx: usize) -> (IndexStr<'a>, IndexStr<'a>) {
64 (self.range_to(..idx), self.range_from(idx..))
65 }
6667/// The same as `split_at`, but returns a `Result` rather than panicking
68 /// when the index is out of bounds.
69#[inline]
70pub fn try_split_at(&self, idx: usize) -> Option<(IndexStr<'a>, IndexStr<'a>)> {
71if idx > self.len() {
72None
73} else {
74Some(self.split_at(idx))
75 }
76 }
7778/// 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]
81pub fn next(&self) -> Option<(u8, IndexStr<'a>)> {
82if self.is_empty() {
83None
84} else {
85let byte = self.string[0];
86Some((byte, self.range_from(1..)))
87 }
88 }
8990/// 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]
93pub fn next_or<E>(&self, error: E) -> Result<(u8, IndexStr<'a>), E> {
94self.next().ok_or(error)
95 }
96}
9798/// # 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)]
107impl<'a> IndexStr<'a> {
108/// Take the given `start..end` range of the underlying string and return a
109 /// new `IndexStr`.
110#[inline]
111pub fn range(&self, idx: Range<usize>) -> IndexStr<'a> {
112 IndexStr {
113 idx: self.idx + idx.start,
114 string: &self.string[idx],
115 }
116 }
117118/// Take the given `start..` range of the underlying string and return a new
119 /// `IndexStr`.
120#[inline]
121pub fn range_from(&self, idx: RangeFrom<usize>) -> IndexStr<'a> {
122 IndexStr {
123 idx: self.idx + idx.start,
124 string: &self.string[idx],
125 }
126 }
127128/// Take the given `..end` range of the underlying string and return a new
129 /// `IndexStr`.
130#[inline]
131pub fn range_to(&self, idx: RangeTo<usize>) -> IndexStr<'a> {
132 IndexStr {
133 idx: self.idx,
134 string: &self.string[idx],
135 }
136 }
137}
138139impl<'a> AsRef<[u8]> for IndexStr<'a> {
140#[inline]
141fn as_ref(&self) -> &[u8] {
142self.string
143 }
144}
145146impl<'a> From<&'a [u8]> for IndexStr<'a> {
147fn from(s: &[u8]) -> IndexStr {
148 IndexStr::new(s)
149 }
150}
151152impl<'a> Into<&'a [u8]> for IndexStr<'a> {
153fn into(self) -> &'a [u8] {
154self.string
155 }
156}
157158impl<'a, 'b> PartialEq<&'a [u8]> for IndexStr<'b> {
159fn eq(&self, rhs: &&[u8]) -> bool {
160self.string == *rhs
161 }
162}
163164impl<'a> fmt::Debug for IndexStr<'a> {
165fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166write!(
167 f,
168"IndexStr {{ idx: {}, string: \"{}\" }}",
169self.idx,
170 String::from_utf8_lossy(self.as_ref())
171 )
172 }
173}