diff options
Diffstat (limited to 'crates/stdx/src')
-rw-r--r-- | crates/stdx/src/lib.rs | 111 | ||||
-rw-r--r-- | crates/stdx/src/macros.rs | 40 |
2 files changed, 131 insertions, 20 deletions
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 71a57fba2..988853ed2 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -1,31 +1,13 @@ | |||
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 | ||
4 | mod macros; | ||
5 | |||
5 | #[inline(always)] | 6 | #[inline(always)] |
6 | pub fn is_ci() -> bool { | 7 | pub fn is_ci() -> bool { |
7 | option_env!("CI").is_some() | 8 | option_env!("CI").is_some() |
8 | } | 9 | } |
9 | 10 | ||
10 | #[macro_export] | ||
11 | macro_rules! eprintln { | ||
12 | ($($tt:tt)*) => {{ | ||
13 | if $crate::is_ci() { | ||
14 | panic!("Forgot to remove debug-print?") | ||
15 | } | ||
16 | std::eprintln!($($tt)*) | ||
17 | }} | ||
18 | } | ||
19 | |||
20 | /// Appends formatted string to a `String`. | ||
21 | #[macro_export] | ||
22 | macro_rules! format_to { | ||
23 | ($buf:expr) => (); | ||
24 | ($buf:expr, $lit:literal $($arg:tt)*) => { | ||
25 | { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); } | ||
26 | }; | ||
27 | } | ||
28 | |||
29 | pub trait SepBy: Sized { | 11 | pub trait SepBy: Sized { |
30 | /// Returns an `impl fmt::Display`, which joins elements via a separator. | 12 | /// Returns an `impl fmt::Display`, which joins elements via a separator. |
31 | fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>; | 13 | fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>; |
@@ -88,6 +70,8 @@ where | |||
88 | Ok(()) | 70 | Ok(()) |
89 | } | 71 | } |
90 | } | 72 | } |
73 | |||
74 | #[must_use] | ||
91 | pub fn timeit(label: &'static str) -> impl Drop { | 75 | pub fn timeit(label: &'static str) -> impl Drop { |
92 | struct Guard { | 76 | struct Guard { |
93 | label: &'static str, | 77 | label: &'static str, |
@@ -124,3 +108,90 @@ pub fn replace(buf: &mut String, from: char, to: &str) { | |||
124 | // FIXME: do this in place. | 108 | // FIXME: do this in place. |
125 | *buf = buf.replace(from, to) | 109 | *buf = buf.replace(from, to) |
126 | } | 110 | } |
111 | |||
112 | pub fn split_delim(haystack: &str, delim: char) -> Option<(&str, &str)> { | ||
113 | let idx = haystack.find(delim)?; | ||
114 | Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) | ||
115 | } | ||
116 | |||
117 | pub fn trim_indent(mut text: &str) -> String { | ||
118 | if text.starts_with('\n') { | ||
119 | text = &text[1..]; | ||
120 | } | ||
121 | let indent = text | ||
122 | .lines() | ||
123 | .filter(|it| !it.trim().is_empty()) | ||
124 | .map(|it| it.len() - it.trim_start().len()) | ||
125 | .min() | ||
126 | .unwrap_or(0); | ||
127 | lines_with_ends(text) | ||
128 | .map( | ||
129 | |line| { | ||
130 | if line.len() <= indent { | ||
131 | line.trim_start_matches(' ') | ||
132 | } else { | ||
133 | &line[indent..] | ||
134 | } | ||
135 | }, | ||
136 | ) | ||
137 | .collect() | ||
138 | } | ||
139 | |||
140 | pub fn lines_with_ends(text: &str) -> LinesWithEnds { | ||
141 | LinesWithEnds { text } | ||
142 | } | ||
143 | |||
144 | pub struct LinesWithEnds<'a> { | ||
145 | text: &'a str, | ||
146 | } | ||
147 | |||
148 | impl<'a> Iterator for LinesWithEnds<'a> { | ||
149 | type Item = &'a str; | ||
150 | fn next(&mut self) -> Option<&'a str> { | ||
151 | if self.text.is_empty() { | ||
152 | return None; | ||
153 | } | ||
154 | let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1); | ||
155 | let (res, next) = self.text.split_at(idx); | ||
156 | self.text = next; | ||
157 | Some(res) | ||
158 | } | ||
159 | } | ||
160 | |||
161 | #[cfg(test)] | ||
162 | mod tests { | ||
163 | use super::*; | ||
164 | |||
165 | #[test] | ||
166 | fn test_trim_indent() { | ||
167 | assert_eq!(trim_indent(""), ""); | ||
168 | assert_eq!( | ||
169 | trim_indent( | ||
170 | " | ||
171 | hello | ||
172 | world | ||
173 | " | ||
174 | ), | ||
175 | "hello\nworld\n" | ||
176 | ); | ||
177 | assert_eq!( | ||
178 | trim_indent( | ||
179 | " | ||
180 | hello | ||
181 | world" | ||
182 | ), | ||
183 | "hello\nworld" | ||
184 | ); | ||
185 | assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n"); | ||
186 | assert_eq!( | ||
187 | trim_indent( | ||
188 | " | ||
189 | fn main() { | ||
190 | return 92; | ||
191 | } | ||
192 | " | ||
193 | ), | ||
194 | "fn main() {\n return 92;\n}\n" | ||
195 | ); | ||
196 | } | ||
197 | } | ||
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs new file mode 100644 index 000000000..bf298460f --- /dev/null +++ b/crates/stdx/src/macros.rs | |||
@@ -0,0 +1,40 @@ | |||
1 | //! Convenience macros. | ||
2 | #[macro_export] | ||
3 | macro_rules! eprintln { | ||
4 | ($($tt:tt)*) => {{ | ||
5 | if $crate::is_ci() { | ||
6 | panic!("Forgot to remove debug-print?") | ||
7 | } | ||
8 | std::eprintln!($($tt)*) | ||
9 | }} | ||
10 | } | ||
11 | |||
12 | /// Appends formatted string to a `String`. | ||
13 | #[macro_export] | ||
14 | macro_rules! format_to { | ||
15 | ($buf:expr) => (); | ||
16 | ($buf:expr, $lit:literal $($arg:tt)*) => { | ||
17 | { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); } | ||
18 | }; | ||
19 | } | ||
20 | |||
21 | // Generates `From` impls for `Enum E { Foo(Foo), Bar(Bar) }` enums | ||
22 | #[macro_export] | ||
23 | macro_rules! impl_from { | ||
24 | ($($variant:ident $(($($sub_variant:ident),*))?),* for $enum:ident) => { | ||
25 | $( | ||
26 | impl From<$variant> for $enum { | ||
27 | fn from(it: $variant) -> $enum { | ||
28 | $enum::$variant(it) | ||
29 | } | ||
30 | } | ||
31 | $($( | ||
32 | impl From<$sub_variant> for $enum { | ||
33 | fn from(it: $sub_variant) -> $enum { | ||
34 | $enum::$variant($variant::$sub_variant(it)) | ||
35 | } | ||
36 | } | ||
37 | )*)? | ||
38 | )* | ||
39 | } | ||
40 | } | ||