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}