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

            
50
use std::fmt;
51
use std::ops::{RangeInclusive, RangeToInclusive};
52
use std::path::Path;
53
use std::time::Duration;
54

            
55
pub mod error_sources;
56
pub mod intern;
57
pub mod iter;
58
pub mod n_key_list;
59
pub mod n_key_set;
60
pub mod rand_hostname;
61
pub mod rangebounds;
62
pub mod retry;
63
pub mod test_rng;
64
pub mod token_bucket;
65

            
66
mod byte_qty;
67
pub use byte_qty::ByteQty;
68

            
69
pub use paste::paste;
70

            
71
#[doc(hidden)]
72
pub use derive_deftly;
73

            
74
use rand::Rng;
75

            
76
/// Sealed
77
mod sealed {
78
    /// Sealed
79
    pub trait Sealed {}
80
}
81
use 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
/// ```
103
15996
pub fn skip_fmt<T>(_: &T, f: &mut fmt::Formatter) -> fmt::Result {
104
    /// Inner function avoids code bloat due to generics
105
15996
    fn inner(f: &mut fmt::Formatter) -> fmt::Result {
106
15996
        write!(f, "..")
107
15996
    }
108
15996
    inner(f)
109
15996
}
110

            
111
// ----------------------------------------------------------------------
112

            
113
/// Formats an iterator as an object whose display implementation is a `separator`-separated string
114
/// of items from `iter`.
115
106
pub fn iter_join(
116
106
    separator: &str,
117
106
    iter: impl IntoIterator<Item: fmt::Display> + Clone,
118
106
) -> 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
106
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128
106
            let Self { separator, iter } = self;
129
106
            let mut iter = iter.clone().into_iter();
130
106
            if let Some(first) = iter.next() {
131
94
                write!(f, "{first}")?;
132
12
            }
133
106
            for x in iter {
134
98
                write!(f, "{separator}{x}")?;
135
            }
136
106
            Ok(())
137
106
        }
138
    }
139
106
    Fmt { separator, iter }
140
106
}
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.
146
pub trait StrExt: AsRef<str> {
147
    /// Like `str.strip_suffix()` but ASCII-case-insensitive
148
6719
    fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> {
149
6719
        let whole = self.as_ref();
150
6719
        let suffix_start = whole.len().checked_sub(suffix.len())?;
151
6650
        let (rest, possible_suffix) = whole.split_at_checked(suffix_start)?;
152
6650
        possible_suffix.eq_ignore_ascii_case(suffix).then_some(rest)
153
6719
    }
154

            
155
    /// Like `str.ends_with()` but ASCII-case-insensitive
156
82
    fn ends_with_ignore_ascii_case(&self, suffix: &str) -> bool {
157
82
        self.strip_suffix_ignore_ascii_case(suffix).is_some()
158
82
    }
159
}
160
impl StrExt for str {}
161

            
162
// ----------------------------------------------------------------------
163

            
164
/// Extension trait to provide `.gen_range_checked()`
165
pub 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
442232
    fn gen_range_checked<T, R>(&mut self, range: R) -> Option<T>
205
442232
    where
206
442232
        T: rand::distr::uniform::SampleUniform,
207
442232
        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
442232
        if range.is_empty() {
216
            None
217
        } else {
218
            use rand::RngExt;
219
            #[allow(clippy::disallowed_methods)]
220
442232
            Some(self.random_range(range))
221
        }
222
442232
    }
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
313609
    fn gen_range_infallible<T>(&mut self, range: RangeToInclusive<T>) -> T
245
313609
    where
246
313609
        T: GenRangeInfallible,
247
    {
248
313609
        self.gen_range_checked(T::lower_bound()..=range.end)
249
313609
            .expect("GenRangeInfallible type with an empty lower_bound()..=T range")
250
313609
    }
251
}
252
impl<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.
268
pub trait GenRangeInfallible: rand::distr::uniform::SampleUniform + Ord
269
where
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

            
278
impl GenRangeInfallible for Duration {
279
426666
    fn lower_bound() -> Self {
280
426666
        Duration::ZERO
281
426666
    }
282
}
283

            
284
// ----------------------------------------------------------------------
285

            
286
/// Renaming of `Path::display` as `display_lossy`
287
pub 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
}
300
impl Sealed for Path {}
301
impl PathExt for Path {
302
    #[allow(clippy::disallowed_methods)]
303
3528
    fn display_lossy(&self) -> std::path::Display<'_> {
304
3528
        self.display()
305
3528
    }
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]
367
macro_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
680
                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]
412
macro_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]
430
macro_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]
493
macro_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
559
        $crate::impl_debug_hex! { $type, |self_| self_ . $($accessor)* .as_ref() }
499
    };
500
    { $type:ty, $obtain:expr $(,)? } => {
501
        impl std::fmt::Debug for $type {
502
559
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
503
                use std::fmt::Write;
504
559
                let obtain: fn(&$type) -> &[u8] = $obtain;
505
559
                let bytes: &[u8] = obtain(self);
506
559
                write!(f, "{}(", stringify!($type))?;
507
19168
                for b in bytes {
508
19168
                    write!(f, "{:02x}", b)?;
509
                }
510
559
                write!(f, ")")?;
511
559
                Ok(())
512
559
            }
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]
588
macro_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]
663
macro_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]
694
macro_rules! assert_val_impl_trait {
695
    ($check:expr, $trait:path $(,)?) => {{
696
9516
        fn ensure_trait<T: $trait>(_s: &T) {}
697
        ensure_trait(&$check);
698
    }};
699
}
700

            
701
// ----------------------------------------------------------------------
702

            
703
#[cfg(test)]
704
mod 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
}