cpp_demangle/
index_str.rs

1//! Provides the `IndexStr` type to keep track of a substring's index into its
2//! original string is.
3
4use alloc::string::String;
5use core::fmt;
6use core::ops::{Range, RangeFrom, RangeTo};
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)]
11pub struct IndexStr<'a> {
12    idx: usize,
13    string: &'a [u8],
14}
15
16#[allow(dead_code)]
17impl<'a> IndexStr<'a> {
18    /// Construct a new `IndexStr` (with `index == 0`) from the given input.
19    #[inline]
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]
29    pub fn len(&self) -> usize {
30        self.string.len()
31    }
32
33    /// Return true if the string is empty, false otherwise.
34    #[inline]
35    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]
41    pub fn index(&self) -> usize {
42        self.idx
43    }
44
45    /// Peek at the next byte in this `IndexStr`.
46    #[inline]
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]
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]
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]
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]
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]
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)]
107impl<'a> IndexStr<'a> {
108    /// Take the given `start..end` range of the underlying string and return a
109    /// new `IndexStr`.
110    #[inline]
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]
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]
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
139impl<'a> AsRef<[u8]> for IndexStr<'a> {
140    #[inline]
141    fn as_ref(&self) -> &[u8] {
142        self.string
143    }
144}
145
146impl<'a> From<&'a [u8]> for IndexStr<'a> {
147    fn from(s: &[u8]) -> IndexStr {
148        IndexStr::new(s)
149    }
150}
151
152impl<'a> Into<&'a [u8]> for IndexStr<'a> {
153    fn into(self) -> &'a [u8] {
154        self.string
155    }
156}
157
158impl<'a, 'b> PartialEq<&'a [u8]> for IndexStr<'b> {
159    fn eq(&self, rhs: &&[u8]) -> bool {
160        self.string == *rhs
161    }
162}
163
164impl<'a> fmt::Debug for IndexStr<'a> {
165    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}