1 //! Missing batteries for standard libraries.
2 use std::{cmp::Ordering, ops, process, time::Instant};
7 pub use crate::macros::{on_assert_failure, set_assert_hook};
10 pub fn is_ci() -> bool {
11 option_env!("CI").is_some()
15 pub fn timeit(label: &'static str) -> impl Drop {
23 eprintln!("{}: {:.2?}", self.label, self.start.elapsed())
27 Guard { label, start: Instant::now() }
30 /// Prints backtrace to stderr, useful for debugging.
31 #[cfg(feature = "backtrace")]
32 pub fn print_backtrace() {
33 let bt = backtrace::Backtrace::new();
34 eprintln!("{:?}", bt);
36 #[cfg(not(feature = "backtrace"))]
37 pub fn print_backtrace() {
39 r#"Enable the backtrace feature.
40 Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
45 pub fn to_lower_snake_case(s: &str) -> String {
46 to_snake_case(s, char::to_ascii_lowercase)
48 pub fn to_upper_snake_case(s: &str) -> String {
49 to_snake_case(s, char::to_ascii_uppercase)
51 fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
52 let mut buf = String::with_capacity(s.len());
55 // `&& prev` is required to not insert `_` before the first symbol.
56 if c.is_ascii_uppercase() && prev {
57 // This check is required to not translate `Weird_Case` into `weird__case`.
58 if !buf.ends_with('_') {
64 buf.push(change_case(&c));
69 pub fn replace(buf: &mut String, from: char, to: &str) {
70 if !buf.contains(from) {
73 // FIXME: do this in place.
74 *buf = buf.replace(from, to)
77 // https://github.com/rust-lang/rust/issues/74773
78 pub fn split_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
79 let mut split = haystack.splitn(2, delim);
80 let prefix = split.next()?;
81 let suffix = split.next()?;
82 Some((prefix, suffix))
84 pub fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
85 let mut split = haystack.rsplitn(2, delim);
86 let suffix = split.next()?;
87 let prefix = split.next()?;
88 Some((prefix, suffix))
91 pub fn trim_indent(mut text: &str) -> String {
92 if text.starts_with('\n') {
97 .filter(|it| !it.trim().is_empty())
98 .map(|it| it.len() - it.trim_start().len())
101 lines_with_ends(text)
104 if line.len() <= indent {
105 line.trim_start_matches(' ')
114 pub fn lines_with_ends(text: &str) -> LinesWithEnds {
115 LinesWithEnds { text }
118 pub struct LinesWithEnds<'a> {
122 impl<'a> Iterator for LinesWithEnds<'a> {
124 fn next(&mut self) -> Option<&'a str> {
125 if self.text.is_empty() {
128 let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
129 let (res, next) = self.text.split_at(idx);
135 /// Returns `idx` such that:
138 /// ∀ x in slice[..idx]: pred(x)
139 /// && ∀ x in slice[idx..]: !pred(x)
142 /// https://github.com/rust-lang/rust/issues/73831
143 pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
145 P: FnMut(&T) -> bool,
148 let mut right = slice.len();
150 while left != right {
151 let mid = left + (right - left) / 2;
153 // When left < right, left <= mid < right.
154 // Therefore left always increases and right always decreases,
155 // and either of them is selected.
156 // In both cases left <= right is satisfied.
157 // Therefore if left < right in a step,
158 // left <= right is satisfied in the next step.
159 // Therefore as long as left != right, 0 <= left < right <= len is satisfied
160 // and if this case 0 <= mid < len is satisfied too.
161 let value = unsafe { slice.get_unchecked(mid) };
172 pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
174 F: FnMut(&T) -> Ordering,
176 let start = partition_point(slice, |it| key(it) == Ordering::Less);
177 let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
181 pub struct JodChild(pub process::Child);
183 impl ops::Deref for JodChild {
184 type Target = process::Child;
185 fn deref(&self) -> &process::Child {
190 impl ops::DerefMut for JodChild {
191 fn deref_mut(&mut self) -> &mut process::Child {
196 impl Drop for JodChild {
198 let _ = self.0.kill();
199 let _ = self.0.wait();
208 fn test_trim_indent() {
209 assert_eq!(trim_indent(""), "");
227 assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n");
236 "fn main() {\n return 92;\n}\n"