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