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