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 {
17 let start = Instant::now();
18 defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
21 /// Prints backtrace to stderr, useful for debugging.
22 #[cfg(feature = "backtrace")]
23 pub fn print_backtrace() {
24 let bt = backtrace::Backtrace::new();
25 eprintln!("{:?}", bt);
27 #[cfg(not(feature = "backtrace"))]
28 pub fn print_backtrace() {
30 r#"Enable the backtrace feature.
31 Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
36 pub fn to_lower_snake_case(s: &str) -> String {
37 to_snake_case(s, char::to_ascii_lowercase)
39 pub fn to_upper_snake_case(s: &str) -> String {
40 to_snake_case(s, char::to_ascii_uppercase)
42 fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
43 let mut buf = String::with_capacity(s.len());
46 // `&& prev` is required to not insert `_` before the first symbol.
47 if c.is_ascii_uppercase() && prev {
48 // This check is required to not translate `Weird_Case` into `weird__case`.
49 if !buf.ends_with('_') {
55 buf.push(change_case(&c));
60 pub fn replace(buf: &mut String, from: char, to: &str) {
61 if !buf.contains(from) {
64 // FIXME: do this in place.
65 *buf = buf.replace(from, to)
68 pub fn trim_indent(mut text: &str) -> String {
69 if text.starts_with('\n') {
74 .filter(|it| !it.trim().is_empty())
75 .map(|it| it.len() - it.trim_start().len())
78 text.split_inclusive('\n')
81 if line.len() <= indent {
82 line.trim_start_matches(' ')
91 pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
93 F: FnMut(&T) -> Ordering,
95 let start = slice.partition_point(|it| key(it) == Ordering::Less);
96 let len = slice[start..].partition_point(|it| key(it) == Ordering::Equal);
101 pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
102 struct D<F: FnOnce()>(Option<F>);
103 impl<F: FnOnce()> Drop for D<F> {
105 if let Some(f) = self.0.take() {
113 #[cfg_attr(not(target_arch = "wasm32"), repr(transparent))]
114 pub struct JodChild(pub std::process::Child);
116 impl ops::Deref for JodChild {
117 type Target = std::process::Child;
118 fn deref(&self) -> &std::process::Child {
123 impl ops::DerefMut for JodChild {
124 fn deref_mut(&mut self) -> &mut std::process::Child {
129 impl Drop for JodChild {
131 let _ = self.0.kill();
132 let _ = self.0.wait();
137 pub fn into_inner(self) -> std::process::Child {
138 if cfg!(target_arch = "wasm32") {
139 panic!("no processes on wasm");
141 // SAFETY: repr transparent, except on WASM
142 unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
146 // feature: iter_order_by
148 pub fn iter_eq_by<I, I2, F>(this: I2, other: I, mut eq: F) -> bool
152 F: FnMut(I2::Item, I::Item) -> bool,
154 let mut other = other.into_iter();
155 let mut this = this.into_iter();
158 let x = match this.next() {
159 None => return other.next().is_none(),
163 let y = match other.next() {
164 None => return false,
179 fn test_trim_indent() {
180 assert_eq!(trim_indent(""), "");
198 assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n");
207 "fn main() {\n return 92;\n}\n"