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