#![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] // @@ begin lint list maintained by maint/add_warning @@ #![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable) #![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly) #![warn(missing_docs)] #![warn(noop_method_call)] #![warn(unreachable_pub)] #![warn(clippy::all)] #![deny(clippy::await_holding_lock)] #![deny(clippy::cargo_common_metadata)] #![deny(clippy::cast_lossless)] #![deny(clippy::checked_conversions)] #![warn(clippy::cognitive_complexity)] #![deny(clippy::debug_assert_with_mut_call)] #![deny(clippy::exhaustive_enums)] #![deny(clippy::exhaustive_structs)] #![deny(clippy::expl_impl_clone_on_copy)] #![deny(clippy::fallible_impl_from)] #![deny(clippy::implicit_clone)] #![deny(clippy::large_stack_arrays)] #![warn(clippy::manual_ok_or)] #![deny(clippy::missing_docs_in_private_items)] #![warn(clippy::needless_borrow)] #![warn(clippy::needless_pass_by_value)] #![warn(clippy::option_option)] #![deny(clippy::print_stderr)] #![deny(clippy::print_stdout)] #![warn(clippy::rc_buffer)] #![deny(clippy::ref_option_ref)] #![warn(clippy::semicolon_if_nothing_returned)] #![warn(clippy::trait_duplication_in_bounds)] #![deny(clippy::unchecked_time_subtraction)] #![deny(clippy::unnecessary_wraps)] #![warn(clippy::unseparated_literal_suffix)] #![deny(clippy::unwrap_used)] #![deny(clippy::mod_module_files)] #![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness #![allow(clippy::uninlined_format_args)] #![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945 #![allow(clippy::result_large_err)] // temporary workaround for arti#587 #![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best #![allow(clippy::needless_lifetimes)] // See arti#1765 #![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060 //! use std::collections::BinaryHeap; use std::fmt; use std::mem; use std::ops::{RangeInclusive, RangeToInclusive}; use std::path::Path; use std::time::Duration; pub mod intern; pub mod iter; pub mod n_key_list; pub mod n_key_set; pub mod rand_hostname; pub mod rangebounds; pub mod retry; pub mod test_rng; mod byte_qty; pub use byte_qty::ByteQty; pub use paste::paste; use rand::Rng; /// Sealed mod sealed { /// Sealed pub trait Sealed {} } use sealed::Sealed; // ---------------------------------------------------------------------- /// Function with the signature of `Debug::fmt` that just prints `".."` /// /// ``` /// use educe::Educe; /// use tor_basic_utils::skip_fmt; /// /// #[derive(Educe, Default)] /// #[educe(Debug)] /// struct Wombat { /// visible: usize, /// /// #[educe(Debug(method = "skip_fmt"))] /// invisible: [u8; 2], /// } /// /// assert_eq!( format!("{:?}", &Wombat::default()), /// "Wombat { visible: 0, invisible: .. }" ); /// ``` pub fn skip_fmt(_: &T, f: &mut fmt::Formatter) -> fmt::Result { /// Inner function avoids code bloat due to generics fn inner(f: &mut fmt::Formatter) -> fmt::Result { write!(f, "..") } inner(f) } // ---------------------------------------------------------------------- /// Extension trait to provide `.strip_suffix_ignore_ascii_case()` etc. // Using `.as_ref()` as a supertrait lets us make the method a provided one. pub trait StrExt: AsRef { /// Like `str.strip_suffix()` but ASCII-case-insensitive fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> { let whole = self.as_ref(); let suffix_start = whole.len().checked_sub(suffix.len())?; whole[suffix_start..] .eq_ignore_ascii_case(suffix) .then(|| &whole[..suffix_start]) } /// Like `str.ends_with()` but ASCII-case-insensitive fn ends_with_ignore_ascii_case(&self, suffix: &str) -> bool { self.strip_suffix_ignore_ascii_case(suffix).is_some() } } impl StrExt for str {} // ---------------------------------------------------------------------- /// Extension trait to provide `.gen_range_checked()` pub trait RngExt: Rng { /// Generate a random value in the given range. /// /// 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. /// /// If the supplied range is empty, returns `None`. /// /// (This is a non-panicking version of [`Rng::gen_range`].) /// /// ### Example /// /// ``` /// use tor_basic_utils::RngExt as _; // // Fake plastic imitation tor_error, since that's actually higher up the stack /// # #[macro_use] /// # mod tor_error { /// # #[derive(Debug)] /// # pub struct Bug; /// # pub fn internal() {} // makes `use` work /// # } /// # macro_rules! internal { { $x:expr } => { Bug } } // /// use tor_error::{Bug, internal}; /// /// fn choose(slice: &[i32]) -> Result { /// let index = rand::rng() /// .gen_range_checked(0..slice.len()) /// .ok_or_else(|| internal!("empty slice"))?; /// Ok(slice[index]) /// } /// /// assert_eq!(choose(&[42]).unwrap(), 42); /// let _: Bug = choose(&[]).unwrap_err(); /// ``` // // TODO: We may someday wish to rename this function to random_range_checked, // since gen_range was renamed to random_range in rand 0.9. // Or we might decide to leave it alone. fn gen_range_checked(&mut self, range: R) -> Option where T: rand::distr::uniform::SampleUniform, R: rand::distr::uniform::SampleRange, { if range.is_empty() { None } else { #[allow(clippy::disallowed_methods)] Some(Rng::random_range(self, range)) } } /// Generate a random value in the given upper-bounded-only range. /// /// For use with an inclusive upper-bounded-only range, /// with types that implement `GenRangeInfallible` /// (that necessarily then implement the appropriate `rand` traits). /// /// 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. /// /// ### Example /// /// ``` /// use std::time::Duration; /// use tor_basic_utils::RngExt as _; /// /// fn stochastic_sleep(max: Duration) { /// let chosen_delay = rand::rng() /// .gen_range_infallible(..=max); /// std::thread::sleep(chosen_delay); /// } /// ``` fn gen_range_infallible(&mut self, range: RangeToInclusive) -> T where T: GenRangeInfallible, { self.gen_range_checked(T::lower_bound()..=range.end) .expect("GenRangeInfallible type with an empty lower_bound()..=T range") } } impl RngExt for T {} /// Types that can be infallibly sampled using `gen_range_infallible` /// /// In addition to the supertraits, the implementor of this trait must guarantee that: /// /// `::lower_bound() ..= UPPER` /// is a nonempty range for every value of `UPPER`. // // One might think that this trait is wrong because we might want to be able to // implement gen_range_infallible for arguments other than RangeToInclusive. // However, double-ended ranges are inherently fallible because the actual values // might be in the wrong order. Non-inclusive ranges are fallible because the // upper bound might be zero, unless a NonZero type is used, which seems like a further // complication that we probably don't want to introduce here. That leaves lower-bounded // ranges, but those are very rare. pub trait GenRangeInfallible: rand::distr::uniform::SampleUniform + Ord where RangeInclusive: rand::distr::uniform::SampleRange, { /// The usual lower bound, for converting a `RangeToInclusive` to a `RangeInclusive` /// /// Only makes sense with types with a sensible lower bound, such as zero. fn lower_bound() -> Self; } impl GenRangeInfallible for Duration { fn lower_bound() -> Self { Duration::ZERO } } // ---------------------------------------------------------------------- /// Implementation of `ErrorKind::NotADirectory` that doesn't require Nightly pub trait IoErrorExt: Sealed { /// Is this `io::ErrorKind::NotADirectory` ? fn is_not_a_directory(&self) -> bool; } impl Sealed for std::io::Error {} impl IoErrorExt for std::io::Error { fn is_not_a_directory(&self) -> bool { self.raw_os_error() == Some( #[cfg(target_family = "unix")] libc::ENOTDIR, #[cfg(target_family = "windows")] { /// Obtained from Rust stdlib source code /// See also: /// /// (although the documentation is anaemic) and /// const ERROR_DIRECTORY: i32 = 267; ERROR_DIRECTORY }, ) } } // ---------------------------------------------------------------------- /// Implementation of `BinaryHeap::retain` that doesn't require Nightly pub trait BinaryHeapExt { /// Remove all elements for which `f` returns `false` /// /// Performance is not great right now - the algorithm is `O(n*log(n))` /// where `n` is the number of elements in the heap (not the number removed). /// /// The name is `retain_ext` to avoid a name collision with the unstable function, /// which would require the use of UFCS and make this unergonomic. fn retain_ext bool>(&mut self, f: F); } impl BinaryHeapExt for BinaryHeap { fn retain_ext bool>(&mut self, f: F) { let items = mem::take(self).into_iter(); *self = items.filter(f).collect(); } } // ---------------------------------------------------------------------- /// Renaming of `Path::display` as `display_lossy` pub trait PathExt: Sealed { /// Display this `Path` as an approximate string, for human consumption in messages /// /// Operating system paths cannot always be faithfully represented as Rust strings, /// because they might not be valid Unicode. /// /// This helper method provides a way to display a string for human users. /// **This may lose information** so should only be used for error messages etc. /// /// This method is exactly the same as [`std::path::Path::display`], /// but with a different and more discouraging name. fn display_lossy(&self) -> std::path::Display<'_>; } impl Sealed for Path {} impl PathExt for Path { #[allow(clippy::disallowed_methods)] fn display_lossy(&self) -> std::path::Display<'_> { self.display() } } // ---------------------------------------------------------------------- /// Define an "accessor trait", which describes structs that have fields of certain types /// /// This can be useful if a large struct, living high up in the dependency graph, /// contains fields that lower-lever crates want to be able to use without having /// to copy the data about etc. /// /// ``` /// // imagine this in the lower-level module /// pub trait Supertrait {} /// use tor_basic_utils::define_accessor_trait; /// define_accessor_trait! { /// pub trait View: Supertrait { /// lorem: String, /// ipsum: usize, /// + /// fn other_accessor(&self) -> bool; /// // any other trait items can go here /// } /// } /// /// fn test_view(v: &V) { /// assert_eq!(v.lorem(), "sit"); /// assert_eq!(v.ipsum(), &42); /// } /// /// // imagine this in the higher-level module /// use derive_more::AsRef; /// #[derive(AsRef)] /// struct Everything { /// #[as_ref] lorem: String, /// #[as_ref] ipsum: usize, /// dolor: Vec<()>, /// } /// impl Supertrait for Everything { } /// impl View for Everything { /// fn other_accessor(&self) -> bool { false } /// } /// /// let everything = Everything { /// lorem: "sit".into(), /// ipsum: 42, /// dolor: vec![()], /// }; /// /// test_view(&everything); /// ``` /// /// ### Generated code /// /// ``` /// # pub trait Supertrait { } /// pub trait View: AsRef + AsRef + Supertrait { /// fn lorem(&self) -> &String { self.as_ref() } /// fn ipsum(&self) -> &usize { self.as_ref() } /// } /// ``` #[macro_export] macro_rules! define_accessor_trait { { $( #[ $attr:meta ])* $vis:vis trait $Trait:ident $( : $( $Super:path )* )? { $( $accessor:ident: $type:ty, )* $( + $( $rest:tt )* )? } } => { $( #[ $attr ])* $vis trait $Trait: $( core::convert::AsRef<$type> + )* $( $( $Super + )* )? { $( /// Access the field fn $accessor(&self) -> &$type { core::convert::AsRef::as_ref(self) } )* $( $( $rest )* )? } } } // ---------------------------------------------------------------------- /// Helper for assisting with macro "argument" defaulting /// /// ```ignore /// macro_coalesce_args!{ [ something ] ... } // => something /// macro_coalesce_args!{ [ ], [ other ] ... } // => other /// // etc. /// ``` /// /// ### Usage note /// /// It is generally possible to avoid use of `macro_coalesce_args`, at the cost of /// providing many alternative matcher patterns. Using `macro_coalesce_args` can make /// it possible to provide a single pattern with the optional items in `$( )?`. /// /// This is valuable because a single pattern with some optional items /// makes much better documentation than several patterns which the reader must compare /// by eye - and it also simplifies the implementation. /// /// `macro_coalesce_args` takes each of its possible expansions in `[ ]` and returns /// the first nonempty one. #[macro_export] macro_rules! macro_first_nonempty { { [ $($yes:tt)+ ] $($rhs:tt)* } => { $($yes)* }; { [ ]$(,)? [ $($otherwise:tt)* ] $($rhs:tt)* } => { $crate::macro_first_nonempty!{ [ $($otherwise)* ] $($rhs)* } }; } // ---------------------------------------------------------------------- /// Define `Debug` to print as hex /// /// # Usage /// /// ```ignore /// impl_debug_hex! { $type } /// impl_debug_hex! { $type . $field_accessor } /// impl_debug_hex! { $type , $accessor_fn } /// ``` /// /// By default, this expects `$type` to implement `AsRef<[u8]>`. /// /// Or, you can supply a series of tokens `$field_accessor`, /// which will be used like this: `self.$field_accessor.as_ref()` /// to get a `&[u8]`. /// /// Or, you can supply `$accessor: fn(&$type) -> &[u8]`. /// /// # Examples /// /// ``` /// use tor_basic_utils::impl_debug_hex; /// #[derive(Default)] /// struct FourBytes([u8; 4]); /// impl AsRef<[u8]> for FourBytes { fn as_ref(&self) -> &[u8] { &self.0 } } /// impl_debug_hex! { FourBytes } /// /// assert_eq!( /// format!("{:?}", FourBytes::default()), /// "FourBytes(00000000)", /// ); /// ``` /// /// ``` /// use tor_basic_utils::impl_debug_hex; /// #[derive(Default)] /// struct FourBytes([u8; 4]); /// impl_debug_hex! { FourBytes .0 } /// /// assert_eq!( /// format!("{:?}", FourBytes::default()), /// "FourBytes(00000000)", /// ); /// ``` /// /// ``` /// use tor_basic_utils::impl_debug_hex; /// struct FourBytes([u8; 4]); /// impl_debug_hex! { FourBytes, |self_| &self_.0 } /// /// assert_eq!( /// format!("{:?}", FourBytes([1,2,3,4])), /// "FourBytes(01020304)", /// ) /// ``` #[macro_export] macro_rules! impl_debug_hex { { $type:ty $(,)? } => { $crate::impl_debug_hex! { $type, |self_| <$type as AsRef<[u8]>>::as_ref(&self_) } }; { $type:ident . $($accessor:tt)+ } => { $crate::impl_debug_hex! { $type, |self_| self_ . $($accessor)* .as_ref() } }; { $type:ty, $obtain:expr $(,)? } => { impl std::fmt::Debug for $type { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use std::fmt::Write; let obtain: fn(&$type) -> &[u8] = $obtain; let bytes: &[u8] = obtain(self); write!(f, "{}(", stringify!($type))?; for b in bytes { write!(f, "{:02x}", b)?; } write!(f, ")")?; Ok(()) } } }; } // ---------------------------------------------------------------------- /// Helper for defining a struct which can be (de)serialized several ways, including "natively" /// /// Ideally we would have /// ```rust ignore /// #[derive(Deserialize)] /// #[serde(try_from=Possibilities)] /// struct Main { /* principal definition */ } /// /// #[derive(Deserialize)] /// #[serde(untagged)] /// enum Possibilities { Main(Main), Other(OtherRepr) } /// /// #[derive(Deserialize)] /// struct OtherRepr { /* other representation we still want to read */ } /// /// impl TryFrom for Main { /* ... */ } /// ``` /// /// But the impl for `Possibilities` ends up honouring the `try_from` on `Main` /// so is recursive. /// /// We solve that (ab)using serde's remote feature, /// on a second copy of the struct definition. /// /// See the Example for instructions. /// It is important to **add test cases** /// for all the representations you expect to parse and serialise, /// since there are easy-to-write bugs, /// for example omitting some of the necessary attributes. /// /// # Generated output: /// /// * The original struct definition, unmodified /// * `#[derive(Serialize, Deserialize)] struct $main_Raw { }` /// /// The `$main_Raw` struct ought not normally be to constructed anywhere, /// and *isn't* convertible to or from the near-identical `$main` struct. /// It exists only as a thing to feed to the serde remove derive, /// and name in `with=`. /// /// # Example /// /// ``` /// use serde::{Deserialize, Serialize}; /// use tor_basic_utils::derive_serde_raw; /// /// derive_serde_raw! { /// #[derive(Deserialize, Serialize, Default, Clone, Debug)] /// #[serde(try_from="BridgeConfigBuilderSerde", into="BridgeConfigBuilderSerde")] /// pub struct BridgeConfigBuilder = "BridgeConfigBuilder" { /// transport: Option, /// //... /// } /// } /// /// #[derive(Serialize,Deserialize)] /// #[serde(untagged)] /// enum BridgeConfigBuilderSerde { /// BridgeLine(String), /// Dict(#[serde(with="BridgeConfigBuilder_Raw")] BridgeConfigBuilder), /// } /// /// impl TryFrom for BridgeConfigBuilder { //... /// # type Error = std::io::Error; /// # fn try_from(_: BridgeConfigBuilderSerde) -> Result { todo!() } } /// impl From for BridgeConfigBuilderSerde { //... /// # fn from(_: BridgeConfigBuilder) -> BridgeConfigBuilderSerde { todo!() } } /// ``` #[macro_export] macro_rules! derive_serde_raw { { $( #[ $($attrs:meta)* ] )* $vis:vis struct $main:ident=$main_s:literal $($body:tt)* } => { $(#[ $($attrs)* ])* $vis struct $main $($body)* $crate::paste! { #[allow(non_camel_case_types)] #[derive(Serialize, Deserialize)] #[serde(remote=$main_s)] struct [< $main _Raw >] $($body)* } } } // ---------------------------------------------------------------------- /// Flatten a `Result, E>` into a `Result`. /// /// See [`Result::flatten`], which is not available /// at our current MSRV. // TODO MSRV 1.89: When our MSRV is at least 1.89, // remove this function and replace uses with `Result::flatten`. pub fn flatten(x: Result, E>) -> Result { match x { Ok(Ok(x)) => Ok(x), Err(e) | Ok(Err(e)) => Err(e), } } // ---------------------------------------------------------------------- /// Asserts that the type of the expression implements the given trait. /// /// Example: /// /// ``` /// # use tor_basic_utils::assert_val_impl_trait; /// let x: u32 = 0; /// assert_val_impl_trait!(x, Clone); /// ``` #[macro_export] macro_rules! assert_val_impl_trait { ($check:expr, $trait:path $(,)?) => {{ fn ensure_trait(_s: &T) {} ensure_trait(&$check); }}; } // ---------------------------------------------------------------------- #[cfg(test)] mod test { // @@ begin test lint list maintained by maint/add_warning @@ #![allow(clippy::bool_assert_comparison)] #![allow(clippy::clone_on_copy)] #![allow(clippy::dbg_macro)] #![allow(clippy::mixed_attributes_style)] #![allow(clippy::print_stderr)] #![allow(clippy::print_stdout)] #![allow(clippy::single_char_pattern)] #![allow(clippy::unwrap_used)] #![allow(clippy::unchecked_time_subtraction)] #![allow(clippy::useless_vec)] #![allow(clippy::needless_pass_by_value)] //! use super::*; #[test] fn test_strip_suffix_ignore_ascii_case() { assert_eq!( "hi there".strip_suffix_ignore_ascii_case("THERE"), Some("hi ") ); assert_eq!("hi here".strip_suffix_ignore_ascii_case("THERE"), None); assert_eq!("THERE".strip_suffix_ignore_ascii_case("there"), Some("")); assert_eq!("hi".strip_suffix_ignore_ascii_case("THERE"), None); } }