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
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50

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

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

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

            
71
pub use paste::paste;
72

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

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

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

            
86
// ----------------------------------------------------------------------
87

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

            
114
// ----------------------------------------------------------------------
115

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

            
154
// ----------------------------------------------------------------------
155

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

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

            
173
// ----------------------------------------------------------------------
174

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

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

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

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

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

            
295
// ----------------------------------------------------------------------
296

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

            
317
// ----------------------------------------------------------------------
318

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

            
398
// ----------------------------------------------------------------------
399

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

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

            
444
// ----------------------------------------------------------------------
445

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

            
526
// ----------------------------------------------------------------------
527

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

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

            
615
// ----------------------------------------------------------------------
616

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

            
691
// ----------------------------------------------------------------------
692

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

            
710
// ----------------------------------------------------------------------
711

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

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