tor_basic_utils/lib.rs
1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3// @@ begin lint list maintained by maint/add_warning @@
4#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6#![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_duration_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39#![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43#![allow(clippy::needless_lifetimes)] // See arti#1765
44#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
46
47use std::collections::BinaryHeap;
48use std::fmt;
49use std::mem;
50use std::ops::{RangeInclusive, RangeToInclusive};
51use std::path::Path;
52use std::time::Duration;
53
54pub mod iter;
55pub mod n_key_list;
56pub mod n_key_set;
57pub mod rangebounds;
58pub mod retry;
59pub mod test_rng;
60
61mod byte_qty;
62pub use byte_qty::ByteQty;
63
64pub use paste::paste;
65
66use rand::Rng;
67
68/// Sealed
69mod sealed {
70 /// Sealed
71 pub trait Sealed {}
72}
73use sealed::Sealed;
74
75// ----------------------------------------------------------------------
76
77/// Function with the signature of `Debug::fmt` that just prints `".."`
78///
79/// ```
80/// use educe::Educe;
81/// use tor_basic_utils::skip_fmt;
82///
83/// #[derive(Educe, Default)]
84/// #[educe(Debug)]
85/// struct Wombat {
86/// visible: usize,
87///
88/// #[educe(Debug(method = "skip_fmt"))]
89/// invisible: [u8; 2],
90/// }
91///
92/// assert_eq!( format!("{:?}", &Wombat::default()),
93/// "Wombat { visible: 0, invisible: .. }" );
94/// ```
95pub fn skip_fmt<T>(_: &T, f: &mut fmt::Formatter) -> fmt::Result {
96 /// Inner function avoids code bloat due to generics
97 fn inner(f: &mut fmt::Formatter) -> fmt::Result {
98 write!(f, "..")
99 }
100 inner(f)
101}
102
103// ----------------------------------------------------------------------
104
105/// Extension trait to provide `.strip_suffix_ignore_ascii_case()` etc.
106// Using `.as_ref()` as a supertrait lets us make the method a provided one.
107pub trait StrExt: AsRef<str> {
108 /// Like `str.strip_suffix()` but ASCII-case-insensitive
109 fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> {
110 let whole = self.as_ref();
111 let suffix_start = whole.len().checked_sub(suffix.len())?;
112 whole[suffix_start..]
113 .eq_ignore_ascii_case(suffix)
114 .then(|| &whole[..suffix_start])
115 }
116
117 /// Like `str.ends_with()` but ASCII-case-insensitive
118 fn ends_with_ignore_ascii_case(&self, suffix: &str) -> bool {
119 self.strip_suffix_ignore_ascii_case(suffix).is_some()
120 }
121}
122impl StrExt for str {}
123
124// ----------------------------------------------------------------------
125
126/// Extension trait to provide `.gen_range_checked()`
127pub trait RngExt: Rng {
128 /// Generate a random value in the given range.
129 ///
130 /// This function is optimised for the case that only a single sample is made from the given range. See also the [`Uniform`](rand::distr::uniform::Uniform) distribution type which may be faster if sampling from the same range repeatedly.
131 ///
132 /// If the supplied range is empty, returns `None`.
133 ///
134 /// (This is a non-panicking version of [`Rng::gen_range`].)
135 ///
136 /// ### Example
137 ///
138 /// ```
139 /// use tor_basic_utils::RngExt as _;
140 //
141 // Fake plastic imitation tor_error, since that's actually higher up the stack
142 /// # #[macro_use]
143 /// # mod tor_error {
144 /// # #[derive(Debug)]
145 /// # pub struct Bug;
146 /// # pub fn internal() {} // makes `use` work
147 /// # }
148 /// # macro_rules! internal { { $x:expr } => { Bug } }
149 //
150 /// use tor_error::{Bug, internal};
151 ///
152 /// fn choose(slice: &[i32]) -> Result<i32, Bug> {
153 /// let index = rand::rng()
154 /// .gen_range_checked(0..slice.len())
155 /// .ok_or_else(|| internal!("empty slice"))?;
156 /// Ok(slice[index])
157 /// }
158 ///
159 /// assert_eq!(choose(&[42]).unwrap(), 42);
160 /// let _: Bug = choose(&[]).unwrap_err();
161 /// ```
162 //
163 // TODO: We may someday wish to rename this function to random_range_checked,
164 // since gen_range was renamed to random_range in rand 0.9.
165 // Or we might decide to leave it alone.
166 fn gen_range_checked<T, R>(&mut self, range: R) -> Option<T>
167 where
168 T: rand::distr::uniform::SampleUniform,
169 R: rand::distr::uniform::SampleRange<T>,
170 {
171 if range.is_empty() {
172 None
173 } else {
174 #[allow(clippy::disallowed_methods)]
175 Some(Rng::random_range(self, range))
176 }
177 }
178
179 /// Generate a random value in the given upper-bounded-only range.
180 ///
181 /// For use with an inclusive upper-bounded-only range,
182 /// with types that implement `GenRangeInfallible`
183 /// (that necessarily then implement the appropriate `rand` traits).
184 ///
185 /// This function is optimised for the case that only a single sample is made from the given range. See also the [`Uniform`](rand::distr::uniform::Uniform) distribution type which may be faster if sampling from the same range repeatedly.
186 ///
187 /// ### Example
188 ///
189 /// ```
190 /// use std::time::Duration;
191 /// use tor_basic_utils::RngExt as _;
192 ///
193 /// fn stochastic_sleep(max: Duration) {
194 /// let chosen_delay = rand::rng()
195 /// .gen_range_infallible(..=max);
196 /// std::thread::sleep(chosen_delay);
197 /// }
198 /// ```
199 fn gen_range_infallible<T>(&mut self, range: RangeToInclusive<T>) -> T
200 where
201 T: GenRangeInfallible,
202 {
203 self.gen_range_checked(T::lower_bound()..=range.end)
204 .expect("GenRangeInfallible type with an empty lower_bound()..=T range")
205 }
206}
207impl<T: Rng> RngExt for T {}
208
209/// Types that can be infallibly sampled using `gen_range_infallible`
210///
211/// In addition to the supertraits, the implementor of this trait must guarantee that:
212///
213/// `<Self as GenRangeInfallible>::lower_bound() ..= UPPER`
214/// is a nonempty range for every value of `UPPER`.
215//
216// One might think that this trait is wrong because we might want to be able to
217// implement gen_range_infallible for arguments other than RangeToInclusive<T>.
218// However, double-ended ranges are inherently fallible because the actual values
219// might be in the wrong order. Non-inclusive ranges are fallible because the
220// upper bound might be zero, unless a NonZero type is used, which seems like a further
221// complication that we probably don't want to introduce here. That leaves lower-bounded
222// ranges, but those are very rare.
223pub trait GenRangeInfallible: rand::distr::uniform::SampleUniform + Ord
224where
225 RangeInclusive<Self>: rand::distr::uniform::SampleRange<Self>,
226{
227 /// The usual lower bound, for converting a `RangeToInclusive` to a `RangeInclusive`
228 ///
229 /// Only makes sense with types with a sensible lower bound, such as zero.
230 fn lower_bound() -> Self;
231}
232
233impl GenRangeInfallible for Duration {
234 fn lower_bound() -> Self {
235 Duration::ZERO
236 }
237}
238
239// ----------------------------------------------------------------------
240
241/// Implementation of `ErrorKind::NotADirectory` that doesn't require Nightly
242pub trait IoErrorExt: Sealed {
243 /// Is this `io::ErrorKind::NotADirectory` ?
244 fn is_not_a_directory(&self) -> bool;
245}
246impl Sealed for std::io::Error {}
247impl IoErrorExt for std::io::Error {
248 fn is_not_a_directory(&self) -> bool {
249 self.raw_os_error()
250 == Some(
251 #[cfg(target_family = "unix")]
252 libc::ENOTDIR,
253 #[cfg(target_family = "windows")]
254 {
255 /// Obtained from Rust stdlib source code
256 /// See also:
257 /// <https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499->
258 /// (although the documentation is anaemic) and
259 /// <https://github.com/rust-lang/rust/pull/79965>
260 const ERROR_DIRECTORY: i32 = 267;
261 ERROR_DIRECTORY
262 },
263 )
264 }
265}
266
267// ----------------------------------------------------------------------
268
269/// Implementation of `BinaryHeap::retain` that doesn't require Nightly
270pub trait BinaryHeapExt<T> {
271 /// Remove all elements for which `f` returns `false`
272 ///
273 /// Performance is not great right now - the algorithm is `O(n*log(n))`
274 /// where `n` is the number of elements in the heap (not the number removed).
275 ///
276 /// The name is `retain_ext` to avoid a name collision with the unstable function,
277 /// which would require the use of UFCS and make this unergonomic.
278 fn retain_ext<F: FnMut(&T) -> bool>(&mut self, f: F);
279}
280impl<T: Ord> BinaryHeapExt<T> for BinaryHeap<T> {
281 fn retain_ext<F: FnMut(&T) -> bool>(&mut self, f: F) {
282 let items = mem::take(self).into_iter();
283 *self = items.filter(f).collect();
284 }
285}
286
287// ----------------------------------------------------------------------
288
289/// Renaming of `Path::display` as `display_lossy`
290pub trait PathExt: Sealed {
291 /// Display this `Path` as an approximate string, for human consumption in messages
292 ///
293 /// Operating system paths cannot always be faithfully represented as Rust strings,
294 /// because they might not be valid Unicode.
295 ///
296 /// This helper method provides a way to display a string for human users.
297 /// **This may lose information** so should only be used for error messages etc.
298 ///
299 /// This method is exactly the same as [`std::path::Path::display`],
300 /// but with a different and more discouraging name.
301 fn display_lossy(&self) -> std::path::Display<'_>;
302}
303impl Sealed for Path {}
304impl PathExt for Path {
305 #[allow(clippy::disallowed_methods)]
306 fn display_lossy(&self) -> std::path::Display<'_> {
307 self.display()
308 }
309}
310
311// ----------------------------------------------------------------------
312
313/// Define an "accessor trait", which describes structs that have fields of certain types
314///
315/// This can be useful if a large struct, living high up in the dependency graph,
316/// contains fields that lower-lever crates want to be able to use without having
317/// to copy the data about etc.
318///
319/// ```
320/// // imagine this in the lower-level module
321/// pub trait Supertrait {}
322/// use tor_basic_utils::define_accessor_trait;
323/// define_accessor_trait! {
324/// pub trait View: Supertrait {
325/// lorem: String,
326/// ipsum: usize,
327/// +
328/// fn other_accessor(&self) -> bool;
329/// // any other trait items can go here
330/// }
331/// }
332///
333/// fn test_view<V: View>(v: &V) {
334/// assert_eq!(v.lorem(), "sit");
335/// assert_eq!(v.ipsum(), &42);
336/// }
337///
338/// // imagine this in the higher-level module
339/// use derive_more::AsRef;
340/// #[derive(AsRef)]
341/// struct Everything {
342/// #[as_ref] lorem: String,
343/// #[as_ref] ipsum: usize,
344/// dolor: Vec<()>,
345/// }
346/// impl Supertrait for Everything { }
347/// impl View for Everything {
348/// fn other_accessor(&self) -> bool { false }
349/// }
350///
351/// let everything = Everything {
352/// lorem: "sit".into(),
353/// ipsum: 42,
354/// dolor: vec![()],
355/// };
356///
357/// test_view(&everything);
358/// ```
359///
360/// ### Generated code
361///
362/// ```
363/// # pub trait Supertrait { }
364/// pub trait View: AsRef<String> + AsRef<usize> + Supertrait {
365/// fn lorem(&self) -> &String { self.as_ref() }
366/// fn ipsum(&self) -> &usize { self.as_ref() }
367/// }
368/// ```
369#[macro_export]
370macro_rules! define_accessor_trait {
371 {
372 $( #[ $attr:meta ])*
373 $vis:vis trait $Trait:ident $( : $( $Super:path )* )? {
374 $( $accessor:ident: $type:ty, )*
375 $( + $( $rest:tt )* )?
376 }
377 } => {
378 $( #[ $attr ])*
379 $vis trait $Trait: $( core::convert::AsRef<$type> + )* $( $( $Super + )* )?
380 {
381 $(
382 /// Access the field
383 fn $accessor(&self) -> &$type { core::convert::AsRef::as_ref(self) }
384 )*
385 $(
386 $( $rest )*
387 )?
388 }
389 }
390}
391
392// ----------------------------------------------------------------------
393
394/// Helper for assisting with macro "argument" defaulting
395///
396/// ```ignore
397/// macro_coalesce_args!{ [ something ] ... } // => something
398/// macro_coalesce_args!{ [ ], [ other ] ... } // => other
399/// // etc.
400/// ```
401///
402/// ### Usage note
403///
404/// It is generally possible to avoid use of `macro_coalesce_args`, at the cost of
405/// providing many alternative matcher patterns. Using `macro_coalesce_args` can make
406/// it possible to provide a single pattern with the optional items in `$( )?`.
407///
408/// This is valuable because a single pattern with some optional items
409/// makes much better documentation than several patterns which the reader must compare
410/// by eye - and it also simplifies the implementation.
411///
412/// `macro_coalesce_args` takes each of its possible expansions in `[ ]` and returns
413/// the first nonempty one.
414#[macro_export]
415macro_rules! macro_first_nonempty {
416 { [ $($yes:tt)+ ] $($rhs:tt)* } => { $($yes)* };
417 { [ ]$(,)? [ $($otherwise:tt)* ] $($rhs:tt)* } => {
418 $crate::macro_first_nonempty!{ [ $($otherwise)* ] $($rhs)* }
419 };
420}
421
422// ----------------------------------------------------------------------
423
424/// Define `Debug` to print as hex
425///
426/// # Usage
427///
428/// ```ignore
429/// impl_debug_hex! { $type }
430/// impl_debug_hex! { $type . $field_accessor }
431/// impl_debug_hex! { $type , $accessor_fn }
432/// ```
433///
434/// By default, this expects `$type` to implement `AsRef<[u8]>`.
435///
436/// Or, you can supply a series of tokens `$field_accessor`,
437/// which will be used like this: `self.$field_accessor.as_ref()`
438/// to get a `&[u8]`.
439///
440/// Or, you can supply `$accessor: fn(&$type) -> &[u8]`.
441///
442/// # Examples
443///
444/// ```
445/// use tor_basic_utils::impl_debug_hex;
446/// #[derive(Default)]
447/// struct FourBytes([u8; 4]);
448/// impl AsRef<[u8]> for FourBytes { fn as_ref(&self) -> &[u8] { &self.0 } }
449/// impl_debug_hex! { FourBytes }
450///
451/// assert_eq!(
452/// format!("{:?}", FourBytes::default()),
453/// "FourBytes(00000000)",
454/// );
455/// ```
456///
457/// ```
458/// use tor_basic_utils::impl_debug_hex;
459/// #[derive(Default)]
460/// struct FourBytes([u8; 4]);
461/// impl_debug_hex! { FourBytes .0 }
462///
463/// assert_eq!(
464/// format!("{:?}", FourBytes::default()),
465/// "FourBytes(00000000)",
466/// );
467/// ```
468///
469/// ```
470/// use tor_basic_utils::impl_debug_hex;
471/// struct FourBytes([u8; 4]);
472/// impl_debug_hex! { FourBytes, |self_| &self_.0 }
473///
474/// assert_eq!(
475/// format!("{:?}", FourBytes([1,2,3,4])),
476/// "FourBytes(01020304)",
477/// )
478/// ```
479#[macro_export]
480macro_rules! impl_debug_hex {
481 { $type:ty $(,)? } => {
482 $crate::impl_debug_hex! { $type, |self_| <$type as AsRef<[u8]>>::as_ref(&self_) }
483 };
484 { $type:ident . $($accessor:tt)+ } => {
485 $crate::impl_debug_hex! { $type, |self_| self_ . $($accessor)* .as_ref() }
486 };
487 { $type:ty, $obtain:expr $(,)? } => {
488 impl std::fmt::Debug for $type {
489 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
490 use std::fmt::Write;
491 let obtain: fn(&$type) -> &[u8] = $obtain;
492 let bytes: &[u8] = obtain(self);
493 write!(f, "{}(", stringify!($type))?;
494 for b in bytes {
495 write!(f, "{:02x}", b)?;
496 }
497 write!(f, ")")?;
498 Ok(())
499 }
500 }
501 };
502}
503
504// ----------------------------------------------------------------------
505
506/// Helper for defining a struct which can be (de)serialized several ways, including "natively"
507///
508/// Ideally we would have
509/// ```rust ignore
510/// #[derive(Deserialize)]
511/// #[serde(try_from=Possibilities)]
512/// struct Main { /* principal definition */ }
513///
514/// #[derive(Deserialize)]
515/// #[serde(untagged)]
516/// enum Possibilities { Main(Main), Other(OtherRepr) }
517///
518/// #[derive(Deserialize)]
519/// struct OtherRepr { /* other representation we still want to read */ }
520///
521/// impl TryFrom<Possibilities> for Main { /* ... */ }
522/// ```
523///
524/// But the impl for `Possibilities` ends up honouring the `try_from` on `Main`
525/// so is recursive.
526///
527/// We solve that (ab)using serde's remote feature,
528/// on a second copy of the struct definition.
529///
530/// See the Example for instructions.
531/// It is important to **add test cases**
532/// for all the representations you expect to parse and serialise,
533/// since there are easy-to-write bugs,
534/// for example omitting some of the necessary attributes.
535///
536/// # Generated output:
537///
538/// * The original struct definition, unmodified
539/// * `#[derive(Serialize, Deserialize)] struct $main_Raw { }`
540///
541/// The `$main_Raw` struct ought not normally be to constructed anywhere,
542/// and *isn't* convertible to or from the near-identical `$main` struct.
543/// It exists only as a thing to feed to the serde remove derive,
544/// and name in `with=`.
545///
546/// # Example
547///
548/// ```
549/// use serde::{Deserialize, Serialize};
550/// use tor_basic_utils::derive_serde_raw;
551///
552/// derive_serde_raw! {
553/// #[derive(Deserialize, Serialize, Default, Clone, Debug)]
554/// #[serde(try_from="BridgeConfigBuilderSerde", into="BridgeConfigBuilderSerde")]
555/// pub struct BridgeConfigBuilder = "BridgeConfigBuilder" {
556/// transport: Option<String>,
557/// //...
558/// }
559/// }
560///
561/// #[derive(Serialize,Deserialize)]
562/// #[serde(untagged)]
563/// enum BridgeConfigBuilderSerde {
564/// BridgeLine(String),
565/// Dict(#[serde(with="BridgeConfigBuilder_Raw")] BridgeConfigBuilder),
566/// }
567///
568/// impl TryFrom<BridgeConfigBuilderSerde> for BridgeConfigBuilder { //...
569/// # type Error = std::io::Error;
570/// # fn try_from(_: BridgeConfigBuilderSerde) -> Result<Self, Self::Error> { todo!() } }
571/// impl From<BridgeConfigBuilder> for BridgeConfigBuilderSerde { //...
572/// # fn from(_: BridgeConfigBuilder) -> BridgeConfigBuilderSerde { todo!() } }
573/// ```
574#[macro_export]
575macro_rules! derive_serde_raw { {
576 $( #[ $($attrs:meta)* ] )*
577 $vis:vis struct $main:ident=$main_s:literal
578 $($body:tt)*
579} => {
580 $(#[ $($attrs)* ])*
581 $vis struct $main
582 $($body)*
583
584 $crate::paste! {
585 #[allow(non_camel_case_types)]
586 #[derive(Serialize, Deserialize)]
587 #[serde(remote=$main_s)]
588 struct [< $main _Raw >]
589 $($body)*
590 }
591} }
592
593// ----------------------------------------------------------------------
594
595/// Flatten a `Result<Result<T, E>, E>` into a `Result<T, E>`.
596///
597/// See the nightly [`Result::flatten`].
598// TODO MSRV TBD: When `Result::flatten` is stable and our MSRV allows,
599// remove this function and replace uses with `Result::flatten`.
600pub fn flatten<T, E>(x: Result<Result<T, E>, E>) -> Result<T, E> {
601 match x {
602 Ok(Ok(x)) => Ok(x),
603 Err(e) | Ok(Err(e)) => Err(e),
604 }
605}
606
607// ----------------------------------------------------------------------
608
609/// Asserts that the type of the expression implements the given trait.
610///
611/// Example:
612///
613/// ```
614/// # use tor_basic_utils::assert_val_impl_trait;
615/// let x: u32 = 0;
616/// assert_val_impl_trait!(x, Clone);
617/// ```
618#[macro_export]
619macro_rules! assert_val_impl_trait {
620 ($check:expr, $trait:path $(,)?) => {{
621 fn ensure_trait<T: $trait>(_s: &T) {}
622 ensure_trait(&$check);
623 }};
624}
625
626// ----------------------------------------------------------------------
627
628#[cfg(test)]
629mod test {
630 // @@ begin test lint list maintained by maint/add_warning @@
631 #![allow(clippy::bool_assert_comparison)]
632 #![allow(clippy::clone_on_copy)]
633 #![allow(clippy::dbg_macro)]
634 #![allow(clippy::mixed_attributes_style)]
635 #![allow(clippy::print_stderr)]
636 #![allow(clippy::print_stdout)]
637 #![allow(clippy::single_char_pattern)]
638 #![allow(clippy::unwrap_used)]
639 #![allow(clippy::unchecked_duration_subtraction)]
640 #![allow(clippy::useless_vec)]
641 #![allow(clippy::needless_pass_by_value)]
642 //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
643 use super::*;
644
645 #[test]
646 fn test_strip_suffix_ignore_ascii_case() {
647 assert_eq!(
648 "hi there".strip_suffix_ignore_ascii_case("THERE"),
649 Some("hi ")
650 );
651 assert_eq!("hi here".strip_suffix_ignore_ascii_case("THERE"), None);
652 assert_eq!("THERE".strip_suffix_ignore_ascii_case("there"), Some(""));
653 assert_eq!("hi".strip_suffix_ignore_ascii_case("THERE"), None);
654 }
655}