diff options
Diffstat (limited to 'crates/stdx')
-rw-r--r-- | crates/stdx/src/lib.rs | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index c0356344c..08ac6f70f 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | //! Missing batteries for standard libraries. | 1 | //! Missing batteries for standard libraries. |
2 | |||
3 | use std::{cell::Cell, fmt, time::Instant}; | 2 | use std::{cell::Cell, fmt, time::Instant}; |
4 | 3 | ||
5 | #[inline(always)] | 4 | #[inline(always)] |
@@ -125,7 +124,89 @@ pub fn replace(buf: &mut String, from: char, to: &str) { | |||
125 | *buf = buf.replace(from, to) | 124 | *buf = buf.replace(from, to) |
126 | } | 125 | } |
127 | 126 | ||
128 | pub fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { | 127 | pub fn split_delim(haystack: &str, delim: char) -> Option<(&str, &str)> { |
129 | let idx = haystack.find(delim)?; | 128 | let idx = haystack.find(delim)?; |
130 | Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) | 129 | Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) |
131 | } | 130 | } |
131 | |||
132 | pub fn trim_indent(mut text: &str) -> String { | ||
133 | if text.starts_with('\n') { | ||
134 | text = &text[1..]; | ||
135 | } | ||
136 | let indent = text | ||
137 | .lines() | ||
138 | .filter(|it| !it.trim().is_empty()) | ||
139 | .map(|it| it.len() - it.trim_start().len()) | ||
140 | .min() | ||
141 | .unwrap_or(0); | ||
142 | lines_with_ends(text) | ||
143 | .map( | ||
144 | |line| { | ||
145 | if line.len() <= indent { | ||
146 | line.trim_start_matches(' ') | ||
147 | } else { | ||
148 | &line[indent..] | ||
149 | } | ||
150 | }, | ||
151 | ) | ||
152 | .collect() | ||
153 | } | ||
154 | |||
155 | pub fn lines_with_ends(text: &str) -> LinesWithEnds { | ||
156 | LinesWithEnds { text } | ||
157 | } | ||
158 | |||
159 | pub struct LinesWithEnds<'a> { | ||
160 | text: &'a str, | ||
161 | } | ||
162 | |||
163 | impl<'a> Iterator for LinesWithEnds<'a> { | ||
164 | type Item = &'a str; | ||
165 | fn next(&mut self) -> Option<&'a str> { | ||
166 | if self.text.is_empty() { | ||
167 | return None; | ||
168 | } | ||
169 | let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1); | ||
170 | let (res, next) = self.text.split_at(idx); | ||
171 | self.text = next; | ||
172 | Some(res) | ||
173 | } | ||
174 | } | ||
175 | |||
176 | #[cfg(test)] | ||
177 | mod tests { | ||
178 | use super::*; | ||
179 | |||
180 | #[test] | ||
181 | fn test_trim_indent() { | ||
182 | assert_eq!(trim_indent(""), ""); | ||
183 | assert_eq!( | ||
184 | trim_indent( | ||
185 | " | ||
186 | hello | ||
187 | world | ||
188 | " | ||
189 | ), | ||
190 | "hello\nworld\n" | ||
191 | ); | ||
192 | assert_eq!( | ||
193 | trim_indent( | ||
194 | " | ||
195 | hello | ||
196 | world" | ||
197 | ), | ||
198 | "hello\nworld" | ||
199 | ); | ||
200 | assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n"); | ||
201 | assert_eq!( | ||
202 | trim_indent( | ||
203 | " | ||
204 | fn main() { | ||
205 | return 92; | ||
206 | } | ||
207 | " | ||
208 | ), | ||
209 | "fn main() {\n return 92;\n}\n" | ||
210 | ); | ||
211 | } | ||
212 | } | ||