diff options
Diffstat (limited to 'crates/stdx')
-rw-r--r-- | crates/stdx/Cargo.toml | 5 | ||||
-rw-r--r-- | crates/stdx/src/lib.rs | 70 | ||||
-rw-r--r-- | crates/stdx/src/macros.rs | 52 |
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" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | backtrace = { 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. |
2 | use std::time::Instant; | 2 | use std::{cmp::Ordering, ops, process, time::Instant}; |
3 | 3 | ||
4 | mod macros; | 4 | mod macros; |
5 | pub mod panic_context; | 5 | pub mod panic_context; |
6 | 6 | ||
7 | pub use crate::macros::{on_assert_failure, set_assert_hook}; | ||
8 | |||
7 | #[inline(always)] | 9 | #[inline(always)] |
8 | pub fn is_ci() -> bool { | 10 | pub 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")] | ||
32 | pub fn print_backtrace() { | ||
33 | let bt = backtrace::Backtrace::new(); | ||
34 | eprintln!("{:?}", bt); | ||
35 | } | ||
36 | #[cfg(not(feature = "backtrace"))] | ||
37 | pub fn print_backtrace() { | ||
38 | eprintln!( | ||
39 | r#"Enable the backtrace feature. | ||
40 | Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`. | ||
41 | "# | ||
42 | ); | ||
43 | } | ||
44 | |||
45 | pub fn to_lower_snake_case(s: &str) -> String { | ||
46 | to_snake_case(s, char::to_ascii_lowercase) | ||
47 | } | ||
48 | pub fn to_upper_snake_case(s: &str) -> String { | ||
49 | to_snake_case(s, char::to_ascii_uppercase) | ||
50 | } | ||
28 | fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String { | 51 | fn 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 | ||
46 | pub fn to_lower_snake_case(s: &str) -> String { | ||
47 | to_snake_case(s, char::to_ascii_lowercase) | ||
48 | } | ||
49 | |||
50 | pub fn to_upper_snake_case(s: &str) -> String { | ||
51 | to_snake_case(s, char::to_ascii_uppercase) | ||
52 | } | ||
53 | |||
54 | pub fn replace(buf: &mut String, from: char, to: &str) { | 69 | pub 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 | ||
121 | pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize | 141 | pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize |
122 | where | 142 | where |
123 | P: FnMut(&T) -> bool, | 143 | P: FnMut(&T) -> bool, |
@@ -147,6 +167,36 @@ where | |||
147 | left | 167 | left |
148 | } | 168 | } |
149 | 169 | ||
170 | pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize> | ||
171 | where | ||
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 | |||
179 | pub struct JodChild(pub process::Child); | ||
180 | |||
181 | impl ops::Deref for JodChild { | ||
182 | type Target = process::Child; | ||
183 | fn deref(&self) -> &process::Child { | ||
184 | &self.0 | ||
185 | } | ||
186 | } | ||
187 | |||
188 | impl ops::DerefMut for JodChild { | ||
189 | fn deref_mut(&mut self) -> &mut process::Child { | ||
190 | &mut self.0 | ||
191 | } | ||
192 | } | ||
193 | |||
194 | impl Drop for JodChild { | ||
195 | fn drop(&mut self) { | ||
196 | let _ = self.0.kill(); | ||
197 | } | ||
198 | } | ||
199 | |||
150 | #[cfg(test)] | 200 | #[cfg(test)] |
151 | mod tests { | 201 | mod 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 | |||
3 | use std::{ | ||
4 | fmt, mem, panic, | ||
5 | sync::atomic::{AtomicUsize, Ordering::SeqCst}, | ||
6 | }; | ||
2 | #[macro_export] | 7 | #[macro_export] |
3 | macro_rules! eprintln { | 8 | macro_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] | ||
68 | macro_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 | |||
81 | type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>); | ||
82 | static HOOK: AtomicUsize = AtomicUsize::new(0); | ||
83 | |||
84 | pub fn set_assert_hook(hook: AssertHook) { | ||
85 | HOOK.store(hook as usize, SeqCst); | ||
86 | } | ||
87 | |||
88 | #[cold] | ||
89 | #[track_caller] | ||
90 | pub 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 | } | ||