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