tor_basic_utils/
lib.rs

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