initiative_core/utils/
substr.rs1use std::ops::{Deref, Range};
2
3#[derive(Clone, Debug)]
11pub struct Substr<'a> {
12 phrase: &'a str,
13 inner_range: Range<usize>,
14 outer_range: Range<usize>,
15}
16
17impl<'a> Substr<'a> {
18 pub fn new(phrase: &'a str, inner_range: Range<usize>, outer_range: Range<usize>) -> Self {
19 assert!(inner_range.start >= outer_range.start);
20 assert!(inner_range.end <= outer_range.end);
21 assert!(outer_range.end <= phrase.len());
22
23 Self {
24 phrase,
25 inner_range,
26 outer_range,
27 }
28 }
29
30 pub fn as_str(&self) -> &'a str {
32 &self.phrase[self.inner_range.clone()]
33 }
34
35 pub fn as_outer_str(&self) -> &'a str {
37 &self.phrase[self.outer_range.clone()]
38 }
39
40 pub fn as_original_str(&self) -> &'a str {
42 self.phrase
43 }
44
45 pub fn range(&self) -> Range<usize> {
47 self.outer_range.clone()
48 }
49
50 pub fn is_at_end(&self) -> bool {
52 self.outer_range.end == self.phrase.len()
53 }
54
55 pub fn is_quoted(&self) -> bool {
57 self.inner_range != self.outer_range
58 }
59
60 pub fn can_complete(&self) -> bool {
63 self.is_at_end() && !self.is_quoted()
64 }
65
66 pub fn after(&self) -> Substr<'a> {
68 Substr {
69 phrase: self.phrase,
70 inner_range: self.outer_range.end..self.phrase.len(),
71 outer_range: self.outer_range.end..self.phrase.len(),
72 }
73 }
74}
75
76impl Eq for Substr<'_> {}
77
78impl PartialEq for Substr<'_> {
79 fn eq(&self, other: &Self) -> bool {
80 self == other.as_str()
81 }
82}
83
84impl PartialEq<str> for Substr<'_> {
85 fn eq(&self, other: &str) -> bool {
86 self.as_str() == other
87 }
88}
89
90impl AsRef<str> for Substr<'_> {
91 fn as_ref(&self) -> &str {
92 self.as_str()
93 }
94}
95
96impl Deref for Substr<'_> {
97 type Target = str;
98
99 fn deref(&self) -> &Self::Target {
100 self.as_str()
101 }
102}
103
104impl<'a> From<&'a str> for Substr<'a> {
105 fn from(input: &'a str) -> Substr<'a> {
106 Substr {
107 phrase: input,
108 inner_range: 0..input.len(),
109 outer_range: 0..input.len(),
110 }
111 }
112}
113
114impl<'a> From<&'a String> for Substr<'a> {
115 fn from(input: &'a String) -> Substr<'a> {
116 Substr {
117 phrase: input,
118 inner_range: 0..input.len(),
119 outer_range: 0..input.len(),
120 }
121 }
122}
123
124#[cfg(test)]
125mod test {
126 use super::*;
127
128 #[test]
129 fn new_test() {
130 assert_eq!(
131 Substr {
132 phrase: "abc",
133 inner_range: 1..2,
134 outer_range: 0..3,
135 },
136 Substr::new("abc", 1..2, 0..3),
137 );
138
139 Substr::new("", 0..0, 0..0);
140 Substr::new("a", 0..1, 0..1);
141 }
142
143 #[test]
144 #[should_panic]
145 fn new_test_inner_starts_before_outer() {
146 Substr::new("abc", 0..2, 1..3);
147 }
148
149 #[test]
150 #[should_panic]
151 fn new_test_inner_ends_after_outer() {
152 Substr::new("abc", 1..3, 0..2);
153 }
154
155 #[test]
156 #[should_panic]
157 fn new_test_range_too_long() {
158 Substr::new("abc", 0..2, 0..4);
159 }
160
161 #[test]
162 fn as_str_test() {
163 let (as_str, as_outer_str, as_original_str) = {
164 let substr = Substr::new("abcde", 2..3, 1..4);
165 (
166 substr.as_str(),
167 substr.as_outer_str(),
168 substr.as_original_str(),
169 )
170 };
171
172 assert_eq!("c", as_str);
173 assert_eq!("bcd", as_outer_str);
174 assert_eq!("abcde", as_original_str);
175 }
176
177 #[test]
178 fn range_test() {
179 assert_eq!(0..3, Substr::new("abc", 1..2, 0..3).range());
180 }
181
182 #[test]
183 fn is_at_end_test() {
184 assert!(Substr::new("abc", 1..2, 1..3).is_at_end());
185 assert!(!Substr::new("abc", 1..2, 1..2).is_at_end());
186 }
187
188 #[test]
189 fn is_quoted_test() {
190 assert!(Substr::new("abc", 1..3, 0..3).is_quoted());
191 assert!(!Substr::new("abc", 1..3, 1..3).is_quoted());
192 }
193
194 #[test]
195 fn can_complete_test() {
196 assert!(Substr::new("abc", 1..3, 1..3).can_complete());
197 assert!(!Substr::new("abc", 1..2, 1..2).can_complete());
198 assert!(!Substr::new("abc", 1..2, 1..3).can_complete());
199 }
200
201 #[test]
202 fn after_test() {
203 assert_eq!("c", Substr::new("abc", 0..1, 0..2).after().as_str());
204 }
205}