aboutsummaryrefslogtreecommitdiff
path: root/crates/stdx
diff options
context:
space:
mode:
Diffstat (limited to 'crates/stdx')
-rw-r--r--crates/stdx/Cargo.toml5
-rw-r--r--crates/stdx/src/lib.rs70
-rw-r--r--crates/stdx/src/macros.rs52
3 files changed, 117 insertions, 10 deletions
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index 8d7a51156..c47e8d0a8 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -10,4 +10,9 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13backtrace = { version = "0.3.44", optional = true }
13# Think twice before adding anything here 14# Think twice before adding anything here
15
16[features]
17# Uncomment to enable for the whole crate graph
18# default = [ "backtrace" ]
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 374ed5910..1ff2559bb 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,9 +1,11 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::time::Instant; 2use std::{cmp::Ordering, ops, process, time::Instant};
3 3
4mod macros; 4mod macros;
5pub mod panic_context; 5pub mod panic_context;
6 6
7pub use crate::macros::{on_assert_failure, set_assert_hook};
8
7#[inline(always)] 9#[inline(always)]
8pub fn is_ci() -> bool { 10pub fn is_ci() -> bool {
9 option_env!("CI").is_some() 11 option_env!("CI").is_some()
@@ -25,6 +27,27 @@ pub fn timeit(label: &'static str) -> impl Drop {
25 Guard { label, start: Instant::now() } 27 Guard { label, start: Instant::now() }
26} 28}
27 29
30/// Prints backtrace to stderr, useful for debugging.
31#[cfg(feature = "backtrace")]
32pub fn print_backtrace() {
33 let bt = backtrace::Backtrace::new();
34 eprintln!("{:?}", bt);
35}
36#[cfg(not(feature = "backtrace"))]
37pub fn print_backtrace() {
38 eprintln!(
39 r#"Enable the backtrace feature.
40Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
41"#
42 );
43}
44
45pub fn to_lower_snake_case(s: &str) -> String {
46 to_snake_case(s, char::to_ascii_lowercase)
47}
48pub fn to_upper_snake_case(s: &str) -> String {
49 to_snake_case(s, char::to_ascii_uppercase)
50}
28fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String { 51fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
29 let mut buf = String::with_capacity(s.len()); 52 let mut buf = String::with_capacity(s.len());
30 let mut prev = false; 53 let mut prev = false;
@@ -43,14 +66,6 @@ fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
43 buf 66 buf
44} 67}
45 68
46pub fn to_lower_snake_case(s: &str) -> String {
47 to_snake_case(s, char::to_ascii_lowercase)
48}
49
50pub fn to_upper_snake_case(s: &str) -> String {
51 to_snake_case(s, char::to_ascii_uppercase)
52}
53
54pub fn replace(buf: &mut String, from: char, to: &str) { 69pub fn replace(buf: &mut String, from: char, to: &str) {
55 if !buf.contains(from) { 70 if !buf.contains(from) {
56 return; 71 return;
@@ -117,7 +132,12 @@ impl<'a> Iterator for LinesWithEnds<'a> {
117 } 132 }
118} 133}
119 134
120// https://github.com/rust-lang/rust/issues/73831 135/// Returns `idx` such that:
136///
137/// ∀ x in slice[..idx]: pred(x)
138/// && ∀ x in slice[idx..]: !pred(x)
139///
140/// https://github.com/rust-lang/rust/issues/73831
121pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize 141pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
122where 142where
123 P: FnMut(&T) -> bool, 143 P: FnMut(&T) -> bool,
@@ -147,6 +167,36 @@ where
147 left 167 left
148} 168}
149 169
170pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
171where
172 F: FnMut(&T) -> Ordering,
173{
174 let start = partition_point(slice, |it| key(it) == Ordering::Less);
175 let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
176 start..start + len
177}
178
179pub struct JodChild(pub process::Child);
180
181impl ops::Deref for JodChild {
182 type Target = process::Child;
183 fn deref(&self) -> &process::Child {
184 &self.0
185 }
186}
187
188impl ops::DerefMut for JodChild {
189 fn deref_mut(&mut self) -> &mut process::Child {
190 &mut self.0
191 }
192}
193
194impl Drop for JodChild {
195 fn drop(&mut self) {
196 let _ = self.0.kill();
197 }
198}
199
150#[cfg(test)] 200#[cfg(test)]
151mod tests { 201mod tests {
152 use super::*; 202 use super::*;
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs
index f5ee3484b..4f5c6100d 100644
--- a/crates/stdx/src/macros.rs
+++ b/crates/stdx/src/macros.rs
@@ -1,4 +1,9 @@
1//! Convenience macros. 1//! Convenience macros.
2
3use std::{
4 fmt, mem, panic,
5 sync::atomic::{AtomicUsize, Ordering::SeqCst},
6};
2#[macro_export] 7#[macro_export]
3macro_rules! eprintln { 8macro_rules! eprintln {
4 ($($tt:tt)*) => {{ 9 ($($tt:tt)*) => {{
@@ -44,3 +49,50 @@ macro_rules! impl_from {
44 )* 49 )*
45 } 50 }
46} 51}
52
53/// A version of `assert!` macro which allows to handle an assertion failure.
54///
55/// In release mode, it returns the condition and logs an error.
56///
57/// ```
58/// if assert_never!(impossible) {
59/// // Heh, this shouldn't have happened, but lets try to soldier on...
60/// return None;
61/// }
62/// ```
63///
64/// Rust analyzer is a long-running process, and crashing really isn't an option.
65///
66/// Shamelessly stolen from: https://www.sqlite.org/assert.html
67#[macro_export]
68macro_rules! assert_never {
69 ($cond:expr) => { $crate::assert_never!($cond, "") };
70 ($cond:expr, $($fmt:tt)*) => {{
71 let value = $cond;
72 if value {
73 $crate::on_assert_failure(
74 format_args!($($fmt)*)
75 );
76 }
77 value
78 }};
79}
80
81type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>);
82static HOOK: AtomicUsize = AtomicUsize::new(0);
83
84pub fn set_assert_hook(hook: AssertHook) {
85 HOOK.store(hook as usize, SeqCst);
86}
87
88#[cold]
89#[track_caller]
90pub fn on_assert_failure(args: fmt::Arguments) {
91 let hook: usize = HOOK.load(SeqCst);
92 if hook == 0 {
93 panic!("\n assertion failed: {}\n", args);
94 }
95
96 let hook: AssertHook = unsafe { mem::transmute::<usize, AssertHook>(hook) };
97 hook(panic::Location::caller(), args)
98}