1 //! Missing batteries for standard libraries.
2 use std::{cmp::Ordering, ops, time::Instant};
8 pub use always_assert::{always, never};
11 pub fn is_ci() -> bool {
12 option_env!("CI").is_some()
16 pub fn timeit(label: &'static str) -> impl Drop {
24 eprintln!("{}: {:.2?}", self.label, self.start.elapsed())
28 Guard { label, start: Instant::now() }
31 /// Prints backtrace to stderr, useful for debugging.
32 #[cfg(feature = "backtrace")]
33 pub fn print_backtrace() {
34 let bt = backtrace::Backtrace::new();
35 eprintln!("{:?}", bt);
37 #[cfg(not(feature = "backtrace"))]
38 pub fn print_backtrace() {
40 r#"Enable the backtrace feature.
41 Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
46 pub fn to_lower_snake_case(s: &str) -> String {
47 to_snake_case(s, char::to_ascii_lowercase)
49 pub fn to_upper_snake_case(s: &str) -> String {
50 to_snake_case(s, char::to_ascii_uppercase)
52 fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
53 let mut buf = String::with_capacity(s.len());
56 // `&& prev` is required to not insert `_` before the first symbol.
57 if c.is_ascii_uppercase() && prev {
58 // This check is required to not translate `Weird_Case` into `weird__case`.
59 if !buf.ends_with('_') {
65 buf.push(change_case(&c));
70 pub fn replace(buf: &mut String, from: char, to: &str) {
71 if !buf.contains(from) {
74 // FIXME: do this in place.
75 *buf = buf.replace(from, to)
78 // https://github.com/rust-lang/rust/issues/74773
79 pub fn split_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
80 let mut split = haystack.splitn(2, delim);
81 let prefix = split.next()?;
82 let suffix = split.next()?;
83 Some((prefix, suffix))
85 pub fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
86 let mut split = haystack.rsplitn(2, delim);
87 let suffix = split.next()?;
88 let prefix = split.next()?;
89 Some((prefix, suffix))
92 pub fn trim_indent(mut text: &str) -> String {
93 if text.starts_with('\n') {
98 .filter(|it| !it.trim().is_empty())
99 .map(|it| it.len() - it.trim_start().len())
102 lines_with_ends(text)
105 if line.len() <= indent {
106 line.trim_start_matches(' ')
115 pub fn lines_with_ends(text: &str) -> LinesWithEnds {
116 LinesWithEnds { text }
119 pub struct LinesWithEnds<'a> {
123 impl<'a> Iterator for LinesWithEnds<'a> {
125 fn next(&mut self) -> Option<&'a str> {
126 if self.text.is_empty() {
129 let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
130 let (res, next) = self.text.split_at(idx);
136 /// Returns `idx` such that:
139 /// ∀ x in slice[..idx]: pred(x)
140 /// && ∀ x in slice[idx..]: !pred(x)
143 /// https://github.com/rust-lang/rust/issues/73831
144 pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
146 P: FnMut(&T) -> bool,
149 let mut right = slice.len();
151 while left != right {
152 let mid = left + (right - left) / 2;
154 // When left < right, left <= mid < right.
155 // Therefore left always increases and right always decreases,
156 // and either of them is selected.
157 // In both cases left <= right is satisfied.
158 // Therefore if left < right in a step,
159 // left <= right is satisfied in the next step.
160 // Therefore as long as left != right, 0 <= left < right <= len is satisfied
161 // and if this case 0 <= mid < len is satisfied too.
162 let value = unsafe { slice.get_unchecked(mid) };
173 pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
175 F: FnMut(&T) -> Ordering,
177 let start = partition_point(slice, |it| key(it) == Ordering::Less);
178 let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
183 pub struct JodChild(pub std::process::Child);
185 impl ops::Deref for JodChild {
186 type Target = std::process::Child;
187 fn deref(&self) -> &std::process::Child {
192 impl ops::DerefMut for JodChild {
193 fn deref_mut(&mut self) -> &mut std::process::Child {
198 impl Drop for JodChild {
200 let _ = self.0.kill();
201 let _ = self.0.wait();
206 pub fn into_inner(self) -> std::process::Child {
207 // SAFETY: repr transparent
208 unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
217 fn test_trim_indent() {
218 assert_eq!(trim_indent(""), "");
236 assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n");
245 "fn main() {\n return 92;\n}\n"