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