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