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