2020-03-28 05:01:25 -05:00
|
|
|
//! Missing batteries for standard libraries.
|
2022-07-20 07:59:42 -05:00
|
|
|
|
|
|
|
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
|
|
|
|
|
2022-06-13 06:34:07 -05:00
|
|
|
use std::process::Command;
|
2021-04-20 08:06:20 -05:00
|
|
|
use std::{cmp::Ordering, ops, time::Instant};
|
2022-06-15 11:35:48 -05:00
|
|
|
use std::{io as sio, iter};
|
2020-03-28 05:01:25 -05:00
|
|
|
|
2020-07-13 08:54:12 -05:00
|
|
|
mod macros;
|
2022-08-25 13:31:02 -05:00
|
|
|
pub mod hash;
|
2021-04-20 08:06:20 -05:00
|
|
|
pub mod process;
|
2020-09-29 13:08:27 -05:00
|
|
|
pub mod panic_context;
|
2022-01-01 20:25:47 -06:00
|
|
|
pub mod non_empty_vec;
|
2020-07-13 08:54:12 -05:00
|
|
|
|
2021-01-26 13:11:12 -06:00
|
|
|
pub use always_assert::{always, never};
|
2021-01-14 09:25:19 -06:00
|
|
|
|
2020-04-06 09:58:16 -05:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn is_ci() -> bool {
|
|
|
|
option_env!("CI").is_some()
|
|
|
|
}
|
|
|
|
|
2020-07-10 20:04:37 -05:00
|
|
|
#[must_use]
|
2020-04-10 13:46:42 -05:00
|
|
|
pub fn timeit(label: &'static str) -> impl Drop {
|
2021-04-26 11:30:50 -05:00
|
|
|
let start = Instant::now();
|
|
|
|
defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
|
2020-04-10 13:46:42 -05:00
|
|
|
}
|
2020-05-03 13:35:21 -05:00
|
|
|
|
2021-01-14 05:55:13 -06:00
|
|
|
/// Prints backtrace to stderr, useful for debugging.
|
|
|
|
pub fn print_backtrace() {
|
2021-09-11 10:42:38 -05:00
|
|
|
#[cfg(feature = "backtrace")]
|
|
|
|
eprintln!("{:?}", backtrace::Backtrace::new());
|
|
|
|
|
|
|
|
#[cfg(not(feature = "backtrace"))]
|
2021-01-14 05:55:13 -06:00
|
|
|
eprintln!(
|
|
|
|
r#"Enable the backtrace feature.
|
|
|
|
Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
|
|
|
|
"#
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_lower_snake_case(s: &str) -> String {
|
|
|
|
to_snake_case(s, char::to_ascii_lowercase)
|
|
|
|
}
|
|
|
|
pub fn to_upper_snake_case(s: &str) -> String {
|
|
|
|
to_snake_case(s, char::to_ascii_uppercase)
|
|
|
|
}
|
2021-10-06 16:24:47 -05:00
|
|
|
|
|
|
|
// Code partially taken from rust/compiler/rustc_lint/src/nonstandard_style.rs
|
|
|
|
// commit: 9626f2b
|
|
|
|
fn to_snake_case<F: Fn(&char) -> char>(mut s: &str, change_case: F) -> String {
|
|
|
|
let mut words = vec![];
|
|
|
|
|
|
|
|
// Preserve leading underscores
|
|
|
|
s = s.trim_start_matches(|c: char| {
|
|
|
|
if c == '_' {
|
|
|
|
words.push(String::new());
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for s in s.split('_') {
|
|
|
|
let mut last_upper = false;
|
|
|
|
let mut buf = String::new();
|
|
|
|
|
|
|
|
if s.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ch in s.chars() {
|
|
|
|
if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper {
|
|
|
|
words.push(buf);
|
|
|
|
buf = String::new();
|
2020-10-03 08:45:16 -05:00
|
|
|
}
|
2021-10-06 16:24:47 -05:00
|
|
|
|
|
|
|
last_upper = ch.is_uppercase();
|
|
|
|
buf.extend(iter::once(change_case(&ch)));
|
2020-05-03 13:35:21 -05:00
|
|
|
}
|
|
|
|
|
2021-10-06 16:24:47 -05:00
|
|
|
words.push(buf);
|
2020-05-03 13:35:21 -05:00
|
|
|
}
|
2021-10-06 16:24:47 -05:00
|
|
|
|
|
|
|
words.join("_")
|
2020-05-03 13:35:21 -05:00
|
|
|
}
|
2020-05-19 15:25:07 -05:00
|
|
|
|
|
|
|
pub fn replace(buf: &mut String, from: char, to: &str) {
|
|
|
|
if !buf.contains(from) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// FIXME: do this in place.
|
2021-10-03 07:39:43 -05:00
|
|
|
*buf = buf.replace(from, to);
|
2020-05-19 15:25:07 -05:00
|
|
|
}
|
2020-06-03 06:26:15 -05:00
|
|
|
|
2020-06-23 15:27:24 -05:00
|
|
|
pub fn trim_indent(mut text: &str) -> String {
|
|
|
|
if text.starts_with('\n') {
|
|
|
|
text = &text[1..];
|
|
|
|
}
|
|
|
|
let indent = text
|
|
|
|
.lines()
|
|
|
|
.filter(|it| !it.trim().is_empty())
|
|
|
|
.map(|it| it.len() - it.trim_start().len())
|
|
|
|
.min()
|
|
|
|
.unwrap_or(0);
|
2021-05-04 06:10:49 -05:00
|
|
|
text.split_inclusive('\n')
|
2020-06-23 15:27:24 -05:00
|
|
|
.map(
|
|
|
|
|line| {
|
|
|
|
if line.len() <= indent {
|
|
|
|
line.trim_start_matches(' ')
|
|
|
|
} else {
|
|
|
|
&line[indent..]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2021-01-10 05:36:15 -06:00
|
|
|
pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
|
2021-01-07 16:39:02 -06:00
|
|
|
where
|
|
|
|
F: FnMut(&T) -> Ordering,
|
|
|
|
{
|
2021-05-04 06:10:49 -05:00
|
|
|
let start = slice.partition_point(|it| key(it) == Ordering::Less);
|
|
|
|
let len = slice[start..].partition_point(|it| key(it) == Ordering::Equal);
|
2021-01-10 05:36:15 -06:00
|
|
|
start..start + len
|
2021-01-07 16:39:02 -06:00
|
|
|
}
|
|
|
|
|
2021-04-26 11:30:50 -05:00
|
|
|
#[must_use]
|
2021-04-20 14:54:05 -05:00
|
|
|
pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
|
|
|
|
struct D<F: FnOnce()>(Option<F>);
|
|
|
|
impl<F: FnOnce()> Drop for D<F> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if let Some(f) = self.0.take() {
|
2021-10-03 07:39:43 -05:00
|
|
|
f();
|
2021-04-20 14:54:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
D(Some(f))
|
|
|
|
}
|
|
|
|
|
2022-06-13 06:34:07 -05:00
|
|
|
/// A [`std::process::Child`] wrapper that will kill the child on drop.
|
2021-06-22 14:51:57 -05:00
|
|
|
#[cfg_attr(not(target_arch = "wasm32"), repr(transparent))]
|
2021-07-08 09:40:14 -05:00
|
|
|
#[derive(Debug)]
|
2021-04-20 08:06:20 -05:00
|
|
|
pub struct JodChild(pub std::process::Child);
|
2021-01-07 11:08:34 -06:00
|
|
|
|
|
|
|
impl ops::Deref for JodChild {
|
2021-04-20 08:06:20 -05:00
|
|
|
type Target = std::process::Child;
|
|
|
|
fn deref(&self) -> &std::process::Child {
|
2021-01-07 11:08:34 -06:00
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::DerefMut for JodChild {
|
2021-04-20 08:06:20 -05:00
|
|
|
fn deref_mut(&mut self) -> &mut std::process::Child {
|
2021-01-07 11:08:34 -06:00
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for JodChild {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let _ = self.0.kill();
|
2021-01-22 10:50:56 -06:00
|
|
|
let _ = self.0.wait();
|
2021-01-07 11:08:34 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-06 10:08:05 -05:00
|
|
|
impl JodChild {
|
2022-06-15 11:35:48 -05:00
|
|
|
pub fn spawn(mut command: Command) -> sio::Result<Self> {
|
2022-06-13 06:34:07 -05:00
|
|
|
command.spawn().map(Self)
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:06:20 -05:00
|
|
|
pub fn into_inner(self) -> std::process::Child {
|
2021-06-22 14:51:57 -05:00
|
|
|
if cfg!(target_arch = "wasm32") {
|
|
|
|
panic!("no processes on wasm");
|
|
|
|
}
|
|
|
|
// SAFETY: repr transparent, except on WASM
|
2021-04-20 08:06:20 -05:00
|
|
|
unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
|
2021-04-06 10:08:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-20 10:27:51 -05:00
|
|
|
// feature: iter_order_by
|
|
|
|
// Iterator::eq_by
|
|
|
|
pub fn iter_eq_by<I, I2, F>(this: I2, other: I, mut eq: F) -> bool
|
|
|
|
where
|
|
|
|
I: IntoIterator,
|
|
|
|
I2: IntoIterator,
|
|
|
|
F: FnMut(I2::Item, I::Item) -> bool,
|
|
|
|
{
|
|
|
|
let mut other = other.into_iter();
|
|
|
|
let mut this = this.into_iter();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let x = match this.next() {
|
|
|
|
None => return other.next().is_none(),
|
|
|
|
Some(val) => val,
|
|
|
|
};
|
|
|
|
|
|
|
|
let y = match other.next() {
|
|
|
|
None => return false,
|
|
|
|
Some(val) => val,
|
|
|
|
};
|
|
|
|
|
|
|
|
if !eq(x, y) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-10 13:01:24 -06:00
|
|
|
/// Returns all final segments of the argument, longest first.
|
|
|
|
pub fn slice_tails<T>(this: &[T]) -> impl Iterator<Item = &[T]> {
|
|
|
|
(0..this.len()).map(|i| &this[i..])
|
|
|
|
}
|
|
|
|
|
2020-06-23 15:27:24 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_trim_indent() {
|
|
|
|
assert_eq!(trim_indent(""), "");
|
|
|
|
assert_eq!(
|
|
|
|
trim_indent(
|
|
|
|
"
|
|
|
|
hello
|
|
|
|
world
|
|
|
|
"
|
|
|
|
),
|
|
|
|
"hello\nworld\n"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
trim_indent(
|
|
|
|
"
|
|
|
|
hello
|
|
|
|
world"
|
|
|
|
),
|
|
|
|
"hello\nworld"
|
|
|
|
);
|
|
|
|
assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n");
|
|
|
|
assert_eq!(
|
|
|
|
trim_indent(
|
|
|
|
"
|
|
|
|
fn main() {
|
|
|
|
return 92;
|
|
|
|
}
|
|
|
|
"
|
|
|
|
),
|
|
|
|
"fn main() {\n return 92;\n}\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|