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
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

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

            
57
pub mod error_sources;
58
pub mod intern;
59
pub mod iter;
60
pub mod n_key_list;
61
pub mod n_key_set;
62
pub mod onionperf_types;
63
pub mod rand_hostname;
64
pub mod rangebounds;
65
pub mod retry;
66
pub mod test_rng;
67
pub mod token_bucket;
68

            
69
mod byte_qty;
70
pub use byte_qty::ByteQty;
71

            
72
pub use paste::paste;
73

            
74
#[doc(hidden)]
75
pub use derive_deftly;
76

            
77
use extend::ext;
78
use rand::Rng;
79

            
80
/// Sealed
81
mod sealed {
82
    /// Sealed
83
    pub trait Sealed {}
84
}
85
use sealed::Sealed;
86

            
87
// ----------------------------------------------------------------------
88

            
89
/// Function with the signature of `Debug::fmt` that just prints `".."`
90
///
91
/// ```
92
/// use educe::Educe;
93
/// use tor_basic_utils::skip_fmt;
94
///
95
/// #[derive(Educe, Default)]
96
/// #[educe(Debug)]
97
/// struct Wombat {
98
///     visible: usize,
99
///
100
///     #[educe(Debug(method = "skip_fmt"))]
101
///     invisible: [u8; 2],
102
/// }
103
///
104
/// assert_eq!( format!("{:?}", &Wombat::default()),
105
///             "Wombat { visible: 0, invisible: .. }" );
106
/// ```
107
16254
pub fn skip_fmt<T>(_: &T, f: &mut fmt::Formatter) -> fmt::Result {
108
    /// Inner function avoids code bloat due to generics
109
21414
    fn inner(f: &mut fmt::Formatter) -> fmt::Result {
110
21414
        write!(f, "..")
111
21414
    }
112
16254
    inner(f)
113
16254
}
114

            
115
// ----------------------------------------------------------------------
116

            
117
/// Formats an iterator as an object whose display implementation is a `separator`-separated string
118
/// of items from `iter`.
119
///
120
/// Performs a similar function to `Itertools::format`.  Differences:
121
///
122
///  * `Itertools::format` panics if the returned formatting helper is formatted twice;
123
///    conversely, `iter_join` requires that the iterator be `Clone`.
124
///  * `iter_join` only supports `Display`; `.format` supports all formatting traits.
125
///  * `iter_join` accepts an `IntoIterator` rather than requiring an `Iterator`.
126
//
127
// TODO maybe this should be an extension trait method?
128
727
pub fn iter_join(
129
727
    separator: &str,
130
727
    iter: impl IntoIterator<Item: fmt::Display> + Clone,
131
727
) -> impl fmt::Display {
132
    // TODO MSRV 1.93: Replace with `std::fmt::from_fn()`?
133
    struct Fmt<'a, I: IntoIterator<Item: fmt::Display> + Clone> {
134
        /// Separates items in `iter`.
135
        separator: &'a str,
136
        /// Iterator to join.
137
        iter: I,
138
    }
139
    impl<'a, I: IntoIterator<Item: fmt::Display> + Clone> fmt::Display for Fmt<'a, I> {
140
727
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141
727
            let Self { separator, iter } = self;
142
727
            let mut iter = iter.clone().into_iter();
143
727
            if let Some(first) = iter.next() {
144
721
                write!(f, "{first}")?;
145
6
            }
146
40795
            for x in iter {
147
40795
                write!(f, "{separator}{x}")?;
148
            }
149
727
            Ok(())
150
727
        }
151
    }
152
727
    Fmt { separator, iter }
153
727
}
154

            
155
// ----------------------------------------------------------------------
156

            
157
/// Extension trait to provide `.strip_suffix_ignore_ascii_case()` etc.
158
#[ext(name = StrExt)]
159
pub impl str {
160
    /// Like `str.strip_suffix()` but ASCII-case-insensitive
161
10213
    fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> {
162
10213
        let whole = self;
163
10213
        let suffix_start = whole.len().checked_sub(suffix.len())?;
164
9963
        let (rest, possible_suffix) = whole.split_at_checked(suffix_start)?;
165
9963
        possible_suffix.eq_ignore_ascii_case(suffix).then_some(rest)
166
10213
    }
167

            
168
    /// Like `str.ends_with()` but ASCII-case-insensitive
169
4399
    fn ends_with_ignore_ascii_case(&self, suffix: &str) -> bool {
170
4399
        self.strip_suffix_ignore_ascii_case(suffix).is_some()
171
4399
    }
172
}
173

            
174
// ----------------------------------------------------------------------
175

            
176
/// Extension trait to provide `.gen_range_checked()`
177
pub trait RngExt: Rng {
178
    /// Generate a random value in the given range.
179
    ///
180
    /// 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.
181
    ///
182
    /// If the supplied range is empty, returns `None`.
183
    ///
184
    /// (This is a non-panicking version of [`rand::RngExt::random_range`].)
185
    ///
186
    /// ### Example
187
    ///
188
    /// ```
189
    /// use tor_basic_utils::RngExt as _;
190
    //
191
    // Fake plastic imitation tor_error, since that's actually higher up the stack
192
    /// # #[macro_use]
193
    /// # mod tor_error {
194
    /// #     #[derive(Debug)]
195
    /// #     pub struct Bug;
196
    /// #     pub fn internal() {} // makes `use` work
197
    /// # }
198
    /// # macro_rules! internal { { $x:expr } => { Bug } }
199
    //
200
    /// use tor_error::{Bug, internal};
201
    ///
202
    /// fn choose(slice: &[i32]) -> Result<i32, Bug> {
203
    ///     let index = rand::rng()
204
    ///         .gen_range_checked(0..slice.len())
205
    ///         .ok_or_else(|| internal!("empty slice"))?;
206
    ///     Ok(slice[index])
207
    /// }
208
    ///
209
    /// assert_eq!(choose(&[42]).unwrap(), 42);
210
    /// let _: Bug = choose(&[]).unwrap_err();
211
    /// ```
212
    //
213
    // TODO: We may someday wish to rename this function to random_range_checked,
214
    // since gen_range was renamed to random_range in rand 0.9.
215
    // Or we might decide to leave it alone.
216
378104
    fn gen_range_checked<T, R>(&mut self, range: R) -> Option<T>
217
378104
    where
218
378104
        T: rand::distr::uniform::SampleUniform,
219
378104
        R: rand::distr::uniform::SampleRange<T>,
220
    {
221
        #[allow(clippy::disallowed_methods)]
222
        {
223
            // Prove that rand::RngExt::random_range exists.  See arti.git/clippy.toml.
224
            let _ = |r: &mut rand::rngs::ThreadRng| rand::RngExt::random_range::<u8, _>(r, 0..10);
225
        }
226

            
227
378104
        if range.is_empty() {
228
            None
229
        } else {
230
            use rand::RngExt;
231
            #[allow(clippy::disallowed_methods)]
232
378104
            Some(self.random_range(range))
233
        }
234
378104
    }
235

            
236
    /// Generate a random value in the given upper-bounded-only range.
237
    ///
238
    /// For use with an inclusive upper-bounded-only range,
239
    /// with types that implement `GenRangeInfallible`
240
    /// (that necessarily then implement the appropriate `rand` traits).
241
    ///
242
    /// 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.
243
    ///
244
    /// ### Example
245
    ///
246
    /// ```
247
    /// use std::time::Duration;
248
    /// use tor_basic_utils::RngExt as _;
249
    ///
250
    /// fn stochastic_sleep(max: Duration) {
251
    ///     let chosen_delay = rand::rng()
252
    ///         .gen_range_infallible(..=max);
253
    ///     std::thread::sleep(chosen_delay);
254
    /// }
255
    /// ```
256
304529
    fn gen_range_infallible<T>(&mut self, range: RangeToInclusive<T>) -> T
257
304529
    where
258
304529
        T: GenRangeInfallible,
259
    {
260
304529
        self.gen_range_checked(T::lower_bound()..=range.end)
261
304529
            .expect("GenRangeInfallible type with an empty lower_bound()..=T range")
262
304529
    }
263
}
264
impl<T: Rng> RngExt for T {}
265

            
266
/// Types that can be infallibly sampled using `gen_range_infallible`
267
///
268
/// In addition to the supertraits, the implementor of this trait must guarantee that:
269
///
270
/// `<Self as GenRangeInfallible>::lower_bound() ..= UPPER`
271
/// is a nonempty range for every value of `UPPER`.
272
//
273
// One might think that this trait is wrong because we might want to be able to
274
// implement gen_range_infallible for arguments other than RangeToInclusive<T>.
275
// However, double-ended ranges are inherently fallible because the actual values
276
// might be in the wrong order.  Non-inclusive ranges are fallible because the
277
// upper bound might be zero, unless a NonZero type is used, which seems like a further
278
// complication that we probably don't want to introduce here.  That leaves lower-bounded
279
// ranges, but those are very rare.
280
pub trait GenRangeInfallible: rand::distr::uniform::SampleUniform + Ord
281
where
282
    RangeInclusive<Self>: rand::distr::uniform::SampleRange<Self>,
283
{
284
    /// The usual lower bound, for converting a `RangeToInclusive` to a `RangeInclusive`
285
    ///
286
    /// Only makes sense with types with a sensible lower bound, such as zero.
287
    fn lower_bound() -> Self;
288
}
289

            
290
impl GenRangeInfallible for Duration {
291
753308
    fn lower_bound() -> Self {
292
753308
        Duration::ZERO
293
753308
    }
294
}
295

            
296
// ----------------------------------------------------------------------
297

            
298
/// Renaming of `Path::display` as `display_lossy`
299
#[ext(supertraits = Sealed)]
300
pub impl Path {
301
    /// Display this `Path` as an approximate string, for human consumption in messages
302
    ///
303
    /// Operating system paths cannot always be faithfully represented as Rust strings,
304
    /// because they might not be valid Unicode.
305
    ///
306
    /// This helper method provides a way to display a string for human users.
307
    /// **This may lose information** so should only be used for error messages etc.
308
    ///
309
    /// This method is exactly the same as [`std::path::Path::display`],
310
    /// but with a different and more discouraging name.
311
    #[allow(clippy::disallowed_methods)]
312
7138
    fn display_lossy(&self) -> std::path::Display<'_> {
313
7138
        self.display()
314
7138
    }
315
}
316
impl Sealed for Path {}
317

            
318
// ----------------------------------------------------------------------
319

            
320
/// Define an "accessor trait", which describes structs that have fields of certain types
321
///
322
/// This can be useful if a large struct, living high up in the dependency graph,
323
/// contains fields that lower-lever crates want to be able to use without having
324
/// to copy the data about etc.
325
///
326
/// ```
327
/// // imagine this in the lower-level module
328
/// pub trait Supertrait {}
329
/// use tor_basic_utils::define_accessor_trait;
330
/// define_accessor_trait! {
331
///     pub trait View: Supertrait {
332
///         lorem: String,
333
///         ipsum: usize,
334
///         +
335
///         fn other_accessor(&self) -> bool;
336
///         // any other trait items can go here
337
///    }
338
/// }
339
///
340
/// fn test_view<V: View>(v: &V) {
341
///     assert_eq!(v.lorem(), "sit");
342
///     assert_eq!(v.ipsum(), &42);
343
/// }
344
///
345
/// // imagine this in the higher-level module
346
/// use derive_more::AsRef;
347
/// #[derive(AsRef)]
348
/// struct Everything {
349
///     #[as_ref] lorem: String,
350
///     #[as_ref] ipsum: usize,
351
///     dolor: Vec<()>,
352
/// }
353
/// impl Supertrait for Everything { }
354
/// impl View for Everything {
355
///     fn other_accessor(&self) -> bool { false }
356
/// }
357
///
358
/// let everything = Everything {
359
///     lorem: "sit".into(),
360
///     ipsum: 42,
361
///     dolor: vec![()],
362
/// };
363
///
364
/// test_view(&everything);
365
/// ```
366
///
367
/// ### Generated code
368
///
369
/// ```
370
/// # pub trait Supertrait { }
371
/// pub trait View: AsRef<String> + AsRef<usize> + Supertrait {
372
///     fn lorem(&self) -> &String { self.as_ref() }
373
///     fn ipsum(&self) -> &usize { self.as_ref() }
374
/// }
375
/// ```
376
#[macro_export]
377
macro_rules! define_accessor_trait {
378
    {
379
        $( #[ $attr:meta ])*
380
        $vis:vis trait $Trait:ident $( : $( $Super:path )* )? {
381
            $( $accessor:ident: $type:ty, )*
382
            $( + $( $rest:tt )* )?
383
        }
384
    } => {
385
        $( #[ $attr ])*
386
        $vis trait $Trait: $( core::convert::AsRef<$type> + )* $( $( $Super + )* )?
387
        {
388
            $(
389
                /// Access the field
390
340
                fn $accessor(&self) -> &$type { core::convert::AsRef::as_ref(self) }
391
            )*
392
            $(
393
                $( $rest )*
394
            )?
395
        }
396
    }
397
}
398

            
399
// ----------------------------------------------------------------------
400

            
401
/// Helper for assisting with macro "argument" defaulting
402
///
403
/// ```ignore
404
/// macro_first_nonempty!{ [ something ]  ... }  // =>   something
405
/// macro_first_nonempty!{ [ ], [ other ] ... }  // =>   other
406
/// // etc.
407
/// ```
408
///
409
/// ### Usage note
410
///
411
/// It is generally possible to avoid use of `macro_first_nonempty`, at the cost of
412
/// providing many alternative matcher patterns.  Using `macro_first_nonempty` can make
413
/// it possible to provide a single pattern with the optional items in `$( )?`.
414
///
415
/// This is valuable because a single pattern with some optional items
416
/// makes much better documentation than several patterns which the reader must compare
417
/// by eye - and it also simplifies the implementation.
418
///
419
/// `macro_first_nonempty` takes each of its possible expansions in `[ ]` and returns
420
/// the first nonempty one.
421
#[macro_export]
422
macro_rules! macro_first_nonempty {
423
    { [ $($yes:tt)+ ] $($rhs:tt)* } => { $($yes)* };
424
    { [ ]$(,)? [ $($otherwise:tt)* ] $($rhs:tt)* } => {
425
        $crate::macro_first_nonempty!{ [ $($otherwise)* ] $($rhs)* }
426
    };
427
}
428

            
429
/// Helper for assisting with defining macros that need to expand
430
/// conditionally when an argument is empty.
431
///
432
/// ```ignore
433
/// if_empty!{ {   } { x } { y } } // => x
434
/// if_empty!{ { z } { x } { y } } // => y
435
/// // etc.
436
/// ```
437
///
438
/// Note: The `{ y }` argument may be omitted.
439
#[macro_export]
440
macro_rules! if_empty {
441
    { { }                  { $($x:tt)* } $({ $($y:tt)* })? } => { $($x)* };
442
    { { $($nonempty:tt)+ } { $($x:tt)* } $({ $($y:tt)* })? } => { $($($y)*)? };
443
}
444

            
445
// ----------------------------------------------------------------------
446

            
447
/// Define `Debug` to print as hex
448
///
449
/// # Usage
450
///
451
/// ```ignore
452
/// impl_debug_hex! { $type }
453
/// impl_debug_hex! { $type . $field_accessor }
454
/// impl_debug_hex! { $type , $accessor_fn }
455
/// ```
456
///
457
/// By default, this expects `$type` to implement `AsRef<[u8]>`.
458
///
459
/// Or, you can supply a series of tokens `$field_accessor`,
460
/// which will be used like this: `self.$field_accessor.as_ref()`
461
/// to get a `&[u8]`.
462
///
463
/// Or, you can supply `$accessor: fn(&$type) -> &[u8]`.
464
///
465
/// # Examples
466
///
467
/// ```
468
/// use tor_basic_utils::impl_debug_hex;
469
/// #[derive(Default)]
470
/// struct FourBytes([u8; 4]);
471
/// impl AsRef<[u8]> for FourBytes { fn as_ref(&self) -> &[u8] { &self.0 } }
472
/// impl_debug_hex! { FourBytes }
473
///
474
/// assert_eq!(
475
///     format!("{:?}", FourBytes::default()),
476
///     "FourBytes(00000000)",
477
/// );
478
/// ```
479
///
480
/// ```
481
/// use tor_basic_utils::impl_debug_hex;
482
/// #[derive(Default)]
483
/// struct FourBytes([u8; 4]);
484
/// impl_debug_hex! { FourBytes .0 }
485
///
486
/// assert_eq!(
487
///     format!("{:?}", FourBytes::default()),
488
///     "FourBytes(00000000)",
489
/// );
490
/// ```
491
///
492
/// ```
493
/// use tor_basic_utils::impl_debug_hex;
494
/// struct FourBytes([u8; 4]);
495
/// impl_debug_hex! { FourBytes, |self_| &self_.0 }
496
///
497
/// assert_eq!(
498
///     format!("{:?}", FourBytes([1,2,3,4])),
499
///     "FourBytes(01020304)",
500
/// )
501
/// ```
502
#[macro_export]
503
macro_rules! impl_debug_hex {
504
    { $type:ty $(,)? } => {
505
        $crate::impl_debug_hex! { $type, |self_| <$type as AsRef<[u8]>>::as_ref(&self_) }
506
    };
507
    { $type:ident . $($accessor:tt)+ } => {
508
514
        $crate::impl_debug_hex! { $type, |self_| self_ . $($accessor)* .as_ref() }
509
    };
510
    { $type:ty, $obtain:expr $(,)? } => {
511
        impl std::fmt::Debug for $type {
512
514
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
513
                use std::fmt::Write;
514
514
                let obtain: fn(&$type) -> &[u8] = $obtain;
515
514
                let bytes: &[u8] = obtain(self);
516
514
                write!(f, "{}(", stringify!($type))?;
517
17088
                for b in bytes {
518
17088
                    write!(f, "{:02x}", b)?;
519
                }
520
514
                write!(f, ")")?;
521
514
                Ok(())
522
514
            }
523
        }
524
    };
525
}
526

            
527
// ----------------------------------------------------------------------
528

            
529
/// Helper for defining a struct which can be (de)serialized several ways, including "natively"
530
///
531
/// Ideally we would have
532
/// ```rust ignore
533
/// #[derive(Deserialize)]
534
/// #[serde(try_from=Possibilities)]
535
/// struct Main { /* principal definition */ }
536
///
537
/// #[derive(Deserialize)]
538
/// #[serde(untagged)]
539
/// enum Possibilities { Main(Main), Other(OtherRepr) }
540
///
541
/// #[derive(Deserialize)]
542
/// struct OtherRepr { /* other representation we still want to read */ }
543
///
544
/// impl TryFrom<Possibilities> for Main { /* ... */ }
545
/// ```
546
///
547
/// But the impl for `Possibilities` ends up honouring the `try_from` on `Main`
548
/// so is recursive.
549
///
550
/// We solve that (ab)using serde's remote feature,
551
/// on a second copy of the struct definition.
552
///
553
/// See the Example for instructions.
554
/// It is important to **add test cases**
555
/// for all the representations you expect to parse and serialise,
556
/// since there are easy-to-write bugs,
557
/// for example omitting some of the necessary attributes.
558
///
559
/// # Generated output:
560
///
561
///  * The original struct definition, unmodified
562
///  * `#[derive(Serialize, Deserialize)] struct $main_Raw { }`
563
///
564
/// The `$main_Raw` struct ought not normally be to constructed anywhere,
565
/// and *isn't* convertible to or from the near-identical `$main` struct.
566
/// It exists only as a thing to feed to the serde remove derive,
567
/// and name in `with=`.
568
///
569
/// # Example
570
///
571
/// ```
572
/// use serde::{Deserialize, Serialize};
573
/// use tor_basic_utils::derive_serde_raw;
574
///
575
/// derive_serde_raw! {
576
///     #[derive(Deserialize, Serialize, Default, Clone, Debug)]
577
///     #[serde(try_from="BridgeConfigBuilderSerde", into="BridgeConfigBuilderSerde")]
578
///     pub struct BridgeConfigBuilder = "BridgeConfigBuilder" {
579
///         transport: Option<String>,
580
///         //...
581
///     }
582
/// }
583
///
584
/// #[derive(Serialize,Deserialize)]
585
/// #[serde(untagged)]
586
/// enum BridgeConfigBuilderSerde {
587
///     BridgeLine(String),
588
///     Dict(#[serde(with="BridgeConfigBuilder_Raw")] BridgeConfigBuilder),
589
/// }
590
///
591
/// impl TryFrom<BridgeConfigBuilderSerde> for BridgeConfigBuilder { //...
592
/// #    type Error = std::io::Error;
593
/// #    fn try_from(_: BridgeConfigBuilderSerde) -> Result<Self, Self::Error> { todo!() } }
594
/// impl From<BridgeConfigBuilder> for BridgeConfigBuilderSerde { //...
595
/// #    fn from(_: BridgeConfigBuilder) -> BridgeConfigBuilderSerde { todo!() } }
596
/// ```
597
#[macro_export]
598
macro_rules! derive_serde_raw { {
599
    $( #[ $($attrs:meta)* ] )*
600
    $vis:vis struct $main:ident=$main_s:literal
601
    $($body:tt)*
602
} => {
603
    $(#[ $($attrs)* ])*
604
    $vis struct $main
605
    $($body)*
606

            
607
    $crate::paste! {
608
        #[allow(non_camel_case_types)]
609
        #[derive(Serialize, Deserialize)]
610
        #[serde(remote=$main_s)]
611
        struct [< $main _Raw >]
612
        $($body)*
613
    }
614
} }
615

            
616
// ----------------------------------------------------------------------
617

            
618
/// Give a compile time error if TYPE implements TRAIT
619
///
620
/// Includes the identifier $rule in the error message, to help the user diagnose
621
/// the problem (unlike the similar macro in `static_assertions`.
622
///
623
/// Supports generics (also, unlike the one in static_assertions`).
624
///
625
/// # Input syntaxes
626
///
627
/// ```
628
// With a fair amount of trickery, we can get the compiler to (mostly) syntax-check this!
629
/// # #![allow(nonstandard_style)]
630
/// # use tor_basic_utils::assert_not_impl;
631
/// # use std::cell::Cell;
632
/// # type TYPE = Cell<u32>;
633
/// # use Sync as TRAIT;
634
/// assert_not_impl! { [RULE_IDENTIFIER] TYPE: TRAIT }
635
//
636
// We can't get the compiler to syntax check this one:
637
// error[E0207]: the type parameter `TYPE_GENERICS` is not constrained ...
638
// Instead, we hide it from the compiler and write a very similar test, hidden from the reader.
639
/// # let _ = r#"
640
/// assert_not_impl! { [RULE_IDENTIFIER <TYPE_GENERICS>] TYPE: TRAIT }
641
/// # "#;
642
/// # assert_not_impl! { [RULE_IDENTIFIER <TYPE_GENERICS>] Cell<TYPE_GENERICS>: TRAIT }
643
/// ```
644
///
645
///  * `RULE_IDENTIFIER` is an arbitrary identifier; it will appear in the error message.
646
///    (There is no way to include arbitrary explanatory text.)
647
///  * `TYPE_GENERICS` are generic bindings needed for `TYPE`.
648
///    (Generics on the trait are not supported.)
649
///
650
/// # Examples
651
///
652
/// ```
653
/// use std::cell::Cell;
654
/// use tor_basic_utils::assert_not_impl;
655
///
656
/// // No error will occur; Cell is not Sync
657
/// assert_not_impl! {
658
///     [cell_must_not_be_sync] Cell<u32>: Sync
659
/// }
660
/// assert_not_impl! {
661
///     [cell_must_not_be_sync <T: Copy>]
662
///     Cell<T>: Sync
663
/// }
664
/// ```
665
///
666
/// ```compile_fail
667
/// // Compile-time error _is_ given; String implements Clone.
668
/// assert_not_impl! {
669
///     [clone_is_forbidden_here] String: Clone
670
/// }
671
/// ```
672
#[macro_export]
673
macro_rules! assert_not_impl {
674
    // we can't match the trailing > of generics - only the leading <
675
    {[$rule:ident $( < $($gens:tt)* )? ] $t:ty : $trait:path } => {
676
        const _ : () = {
677
            #[allow(dead_code, non_camel_case_types)]
678
            trait $rule<X> {
679
                fn item();
680
            }
681
            impl$( < $($gens)* )? $rule<()> for $t {
682
                fn item() {
683
                    let _ = Self::item;
684
                }
685
            }
686
            struct Invalid;
687
            impl<T : $trait + ?Sized> $rule<Invalid> for T { fn item() {} }
688
        };
689
    }
690
}
691

            
692
// ----------------------------------------------------------------------
693

            
694
/// Asserts that the type of the expression implements the given trait.
695
///
696
/// Example:
697
///
698
/// ```
699
/// # use tor_basic_utils::assert_val_impl_trait;
700
/// let x: u32 = 0;
701
/// assert_val_impl_trait!(x, Clone);
702
/// ```
703
#[macro_export]
704
macro_rules! assert_val_impl_trait {
705
    ($check:expr, $trait:path $(,)?) => {{
706
4828
        fn ensure_trait<T: $trait>(_s: &T) {}
707
        ensure_trait(&$check);
708
    }};
709
}
710

            
711
// ----------------------------------------------------------------------
712

            
713
#[cfg(test)]
714
mod test {
715
    // @@ begin test lint list maintained by maint/add_warning @@
716
    #![allow(clippy::bool_assert_comparison)]
717
    #![allow(clippy::clone_on_copy)]
718
    #![allow(clippy::dbg_macro)]
719
    #![allow(clippy::mixed_attributes_style)]
720
    #![allow(clippy::print_stderr)]
721
    #![allow(clippy::print_stdout)]
722
    #![allow(clippy::single_char_pattern)]
723
    #![allow(clippy::unwrap_used)]
724
    #![allow(clippy::unchecked_time_subtraction)]
725
    #![allow(clippy::useless_vec)]
726
    #![allow(clippy::needless_pass_by_value)]
727
    #![allow(clippy::string_slice)] // See arti#2571
728
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
729
    use super::*;
730

            
731
    #[test]
732
    fn test_strip_suffix_ignore_ascii_case() {
733
        assert_eq!(
734
            "hi there".strip_suffix_ignore_ascii_case("THERE"),
735
            Some("hi ")
736
        );
737
        assert_eq!("hi here".strip_suffix_ignore_ascii_case("THERE"), None);
738
        assert_eq!("THERE".strip_suffix_ignore_ascii_case("there"), Some(""));
739
        assert_eq!("hi".strip_suffix_ignore_ascii_case("THERE"), None);
740
    }
741
}