]> git.lizzy.rs Git - rust.git/blobdiff - crates/stdx/src/lib.rs
Remove proc macro management thread
[rust.git] / crates / stdx / src / lib.rs
index 13aab1451ec0b22d332e12082c7b69ef2bf4acbb..e83d5db437d1d218d5fe3c3696e23790366b9de1 100644 (file)
@@ -1,9 +1,12 @@
 //! Missing batteries for standard libraries.
-use std::{cmp::Ordering, ops, process, time::Instant};
+use std::{cmp::Ordering, ops, time::Instant};
 
 mod macros;
+pub mod process;
 pub mod panic_context;
 
+pub use always_assert::{always, never};
+
 #[inline(always)]
 pub fn is_ci() -> bool {
     option_env!("CI").is_some()
@@ -11,20 +14,31 @@ pub fn is_ci() -> bool {
 
 #[must_use]
 pub fn timeit(label: &'static str) -> impl Drop {
-    struct Guard {
-        label: &'static str,
-        start: Instant,
-    }
-
-    impl Drop for Guard {
-        fn drop(&mut self) {
-            eprintln!("{}: {:.2?}", self.label, self.start.elapsed())
-        }
-    }
+    let start = Instant::now();
+    defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
+}
 
-    Guard { label, start: Instant::now() }
+/// Prints backtrace to stderr, useful for debugging.
+#[cfg(feature = "backtrace")]
+pub fn print_backtrace() {
+    let bt = backtrace::Backtrace::new();
+    eprintln!("{:?}", bt);
+}
+#[cfg(not(feature = "backtrace"))]
+pub fn print_backtrace() {
+    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)
+}
 fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
     let mut buf = String::with_capacity(s.len());
     let mut prev = false;
@@ -43,14 +57,6 @@ fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
     buf
 }
 
-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)
-}
-
 pub fn replace(buf: &mut String, from: char, to: &str) {
     if !buf.contains(from) {
         return;
@@ -59,20 +65,6 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
     *buf = buf.replace(from, to)
 }
 
-// https://github.com/rust-lang/rust/issues/74773
-pub fn split_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
-    let mut split = haystack.splitn(2, delim);
-    let prefix = split.next()?;
-    let suffix = split.next()?;
-    Some((prefix, suffix))
-}
-pub fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
-    let mut split = haystack.rsplitn(2, delim);
-    let suffix = split.next()?;
-    let prefix = split.next()?;
-    Some((prefix, suffix))
-}
-
 pub fn trim_indent(mut text: &str) -> String {
     if text.starts_with('\n') {
         text = &text[1..];
@@ -83,7 +75,7 @@ pub fn trim_indent(mut text: &str) -> String {
         .map(|it| it.len() - it.trim_start().len())
         .min()
         .unwrap_or(0);
-    lines_with_ends(text)
+    text.split_inclusive('\n')
         .map(
             |line| {
                 if line.len() <= indent {
@@ -96,82 +88,41 @@ pub fn trim_indent(mut text: &str) -> String {
         .collect()
 }
 
-pub fn lines_with_ends(text: &str) -> LinesWithEnds {
-    LinesWithEnds { text }
-}
-
-pub struct LinesWithEnds<'a> {
-    text: &'a str,
-}
-
-impl<'a> Iterator for LinesWithEnds<'a> {
-    type Item = &'a str;
-    fn next(&mut self) -> Option<&'a str> {
-        if self.text.is_empty() {
-            return None;
-        }
-        let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
-        let (res, next) = self.text.split_at(idx);
-        self.text = next;
-        Some(res)
-    }
-}
-
-/// Returns `idx` such that:
-///
-///     ∀ x in slice[..idx]:  pred(x)
-///  && ∀ x in slice[idx..]: !pred(x)
-///
-/// https://github.com/rust-lang/rust/issues/73831
-pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
-where
-    P: FnMut(&T) -> bool,
-{
-    let mut left = 0;
-    let mut right = slice.len();
-
-    while left != right {
-        let mid = left + (right - left) / 2;
-        // SAFETY:
-        // When left < right, left <= mid < right.
-        // Therefore left always increases and right always decreases,
-        // and either of them is selected.
-        // In both cases left <= right is satisfied.
-        // Therefore if left < right in a step,
-        // left <= right is satisfied in the next step.
-        // Therefore as long as left != right, 0 <= left < right <= len is satisfied
-        // and if this case 0 <= mid < len is satisfied too.
-        let value = unsafe { slice.get_unchecked(mid) };
-        if pred(value) {
-            left = mid + 1;
-        } else {
-            right = mid;
-        }
-    }
-
-    left
-}
-
 pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
 where
     F: FnMut(&T) -> Ordering,
 {
-    let start = partition_point(slice, |it| key(it) == Ordering::Less);
-    let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
+    let start = slice.partition_point(|it| key(it) == Ordering::Less);
+    let len = slice[start..].partition_point(|it| key(it) == Ordering::Equal);
     start..start + len
 }
 
-pub struct JodChild(pub process::Child);
+#[must_use]
+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() {
+                f()
+            }
+        }
+    }
+    D(Some(f))
+}
+
+#[cfg_attr(not(target_arch = "wasm32"), repr(transparent))]
+#[derive(Debug)]
+pub struct JodChild(pub std::process::Child);
 
 impl ops::Deref for JodChild {
-    type Target = process::Child;
-    fn deref(&self) -> &process::Child {
+    type Target = std::process::Child;
+    fn deref(&self) -> &std::process::Child {
         &self.0
     }
 }
 
 impl ops::DerefMut for JodChild {
-    fn deref_mut(&mut self) -> &mut process::Child {
+    fn deref_mut(&mut self) -> &mut std::process::Child {
         &mut self.0
     }
 }
@@ -179,6 +130,45 @@ fn deref_mut(&mut self) -> &mut process::Child {
 impl Drop for JodChild {
     fn drop(&mut self) {
         let _ = self.0.kill();
+        let _ = self.0.wait();
+    }
+}
+
+impl JodChild {
+    pub fn into_inner(self) -> std::process::Child {
+        if cfg!(target_arch = "wasm32") {
+            panic!("no processes on wasm");
+        }
+        // SAFETY: repr transparent, except on WASM
+        unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
+    }
+}
+
+// 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;
+        }
     }
 }