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
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48

            
49
use std::collections::BinaryHeap;
50
use std::fmt;
51
use std::mem;
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 rand_hostname;
62
pub mod rangebounds;
63
pub mod retry;
64
pub mod test_rng;
65

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

            
69
pub use paste::paste;
70

            
71
use rand::Rng;
72

            
73
/// Sealed
74
mod sealed {
75
    /// Sealed
76
    pub trait Sealed {}
77
}
78
use sealed::Sealed;
79

            
80
// ----------------------------------------------------------------------
81

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

            
108
// ----------------------------------------------------------------------
109

            
110
/// Formats an iterator as an object whose display implementation is a `separator`-separated string
111
/// of items from `iter`.
112
6
pub fn iter_join(
113
6
    separator: &str,
114
6
    iter: impl Iterator<Item: fmt::Display> + Clone,
115
6
) -> impl fmt::Display {
116
    // TODO: This can be replaced with `std::fmt::from_fn()` once stabilised and within our MSRV.
117
    struct Fmt<'a, I: Iterator<Item: fmt::Display> + Clone> {
118
        /// Separates items in `iter`.
119
        separator: &'a str,
120
        /// Iterator to join.
121
        iter: I,
122
    }
123
    impl<'a, I: Iterator<Item: fmt::Display> + Clone> fmt::Display for Fmt<'a, I> {
124
6
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125
6
            let Self { separator, iter } = self;
126
6
            let mut iter = iter.clone();
127
6
            if let Some(first) = iter.next() {
128
6
                write!(f, "{first}")?;
129
            }
130
12
            for x in iter {
131
6
                write!(f, "{separator}{x}")?;
132
            }
133
6
            Ok(())
134
6
        }
135
    }
136
6
    Fmt { separator, iter }
137
6
}
138

            
139
// ----------------------------------------------------------------------
140

            
141
/// Extension trait to provide `.strip_suffix_ignore_ascii_case()` etc.
142
// Using `.as_ref()` as a supertrait lets us make the method a provided one.
143
pub trait StrExt: AsRef<str> {
144
    /// Like `str.strip_suffix()` but ASCII-case-insensitive
145
6450
    fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> {
146
6450
        let whole = self.as_ref();
147
6450
        let suffix_start = whole.len().checked_sub(suffix.len())?;
148
6383
        whole[suffix_start..]
149
6383
            .eq_ignore_ascii_case(suffix)
150
6383
            .then(|| &whole[..suffix_start])
151
6450
    }
152

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

            
160
// ----------------------------------------------------------------------
161

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

            
215
    /// Generate a random value in the given upper-bounded-only range.
216
    ///
217
    /// For use with an inclusive upper-bounded-only range,
218
    /// with types that implement `GenRangeInfallible`
219
    /// (that necessarily then implement the appropriate `rand` traits).
220
    ///
221
    /// 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.
222
    ///
223
    /// ### Example
224
    ///
225
    /// ```
226
    /// use std::time::Duration;
227
    /// use tor_basic_utils::RngExt as _;
228
    ///
229
    /// fn stochastic_sleep(max: Duration) {
230
    ///     let chosen_delay = rand::rng()
231
    ///         .gen_range_infallible(..=max);
232
    ///     std::thread::sleep(chosen_delay);
233
    /// }
234
    /// ```
235
306738
    fn gen_range_infallible<T>(&mut self, range: RangeToInclusive<T>) -> T
236
306738
    where
237
306738
        T: GenRangeInfallible,
238
    {
239
306738
        self.gen_range_checked(T::lower_bound()..=range.end)
240
306738
            .expect("GenRangeInfallible type with an empty lower_bound()..=T range")
241
306738
    }
242
}
243
impl<T: Rng> RngExt for T {}
244

            
245
/// Types that can be infallibly sampled using `gen_range_infallible`
246
///
247
/// In addition to the supertraits, the implementor of this trait must guarantee that:
248
///
249
/// `<Self as GenRangeInfallible>::lower_bound() ..= UPPER`
250
/// is a nonempty range for every value of `UPPER`.
251
//
252
// One might think that this trait is wrong because we might want to be able to
253
// implement gen_range_infallible for arguments other than RangeToInclusive<T>.
254
// However, double-ended ranges are inherently fallible because the actual values
255
// might be in the wrong order.  Non-inclusive ranges are fallible because the
256
// upper bound might be zero, unless a NonZero type is used, which seems like a further
257
// complication that we probably don't want to introduce here.  That leaves lower-bounded
258
// ranges, but those are very rare.
259
pub trait GenRangeInfallible: rand::distr::uniform::SampleUniform + Ord
260
where
261
    RangeInclusive<Self>: rand::distr::uniform::SampleRange<Self>,
262
{
263
    /// The usual lower bound, for converting a `RangeToInclusive` to a `RangeInclusive`
264
    ///
265
    /// Only makes sense with types with a sensible lower bound, such as zero.
266
    fn lower_bound() -> Self;
267
}
268

            
269
impl GenRangeInfallible for Duration {
270
417588
    fn lower_bound() -> Self {
271
417588
        Duration::ZERO
272
417588
    }
273
}
274

            
275
// ----------------------------------------------------------------------
276

            
277
/// Implementation of `ErrorKind::NotADirectory` that doesn't require Nightly
278
pub trait IoErrorExt: Sealed {
279
    /// Is this `io::ErrorKind::NotADirectory` ?
280
    fn is_not_a_directory(&self) -> bool;
281
}
282
impl Sealed for std::io::Error {}
283
impl IoErrorExt for std::io::Error {
284
    fn is_not_a_directory(&self) -> bool {
285
        self.raw_os_error()
286
            == Some(
287
                #[cfg(target_family = "unix")]
288
                libc::ENOTDIR,
289
                #[cfg(target_family = "windows")]
290
                {
291
                    /// Obtained from Rust stdlib source code
292
                    /// See also:
293
                    ///   <https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499->
294
                    /// (although the documentation is anaemic) and
295
                    /// <https://github.com/rust-lang/rust/pull/79965>
296
                    const ERROR_DIRECTORY: i32 = 267;
297
                    ERROR_DIRECTORY
298
                },
299
            )
300
    }
301
}
302

            
303
// ----------------------------------------------------------------------
304

            
305
/// Implementation of `BinaryHeap::retain` that doesn't require Nightly
306
pub trait BinaryHeapExt<T> {
307
    /// Remove all elements for which `f` returns `false`
308
    ///
309
    /// Performance is not great right now - the algorithm is `O(n*log(n))`
310
    /// where `n` is the number of elements in the heap (not the number removed).
311
    ///
312
    /// The name is `retain_ext` to avoid a name collision with the unstable function,
313
    /// which would require the use of UFCS and make this unergonomic.
314
    fn retain_ext<F: FnMut(&T) -> bool>(&mut self, f: F);
315
}
316
impl<T: Ord> BinaryHeapExt<T> for BinaryHeap<T> {
317
88
    fn retain_ext<F: FnMut(&T) -> bool>(&mut self, f: F) {
318
88
        let items = mem::take(self).into_iter();
319
88
        *self = items.filter(f).collect();
320
88
    }
321
}
322

            
323
// ----------------------------------------------------------------------
324

            
325
/// Renaming of `Path::display` as `display_lossy`
326
pub trait PathExt: Sealed {
327
    /// Display this `Path` as an approximate string, for human consumption in messages
328
    ///
329
    /// Operating system paths cannot always be faithfully represented as Rust strings,
330
    /// because they might not be valid Unicode.
331
    ///
332
    /// This helper method provides a way to display a string for human users.
333
    /// **This may lose information** so should only be used for error messages etc.
334
    ///
335
    /// This method is exactly the same as [`std::path::Path::display`],
336
    /// but with a different and more discouraging name.
337
    fn display_lossy(&self) -> std::path::Display<'_>;
338
}
339
impl Sealed for Path {}
340
impl PathExt for Path {
341
    #[allow(clippy::disallowed_methods)]
342
2623
    fn display_lossy(&self) -> std::path::Display<'_> {
343
2623
        self.display()
344
2623
    }
345
}
346

            
347
// ----------------------------------------------------------------------
348

            
349
/// Define an "accessor trait", which describes structs that have fields of certain types
350
///
351
/// This can be useful if a large struct, living high up in the dependency graph,
352
/// contains fields that lower-lever crates want to be able to use without having
353
/// to copy the data about etc.
354
///
355
/// ```
356
/// // imagine this in the lower-level module
357
/// pub trait Supertrait {}
358
/// use tor_basic_utils::define_accessor_trait;
359
/// define_accessor_trait! {
360
///     pub trait View: Supertrait {
361
///         lorem: String,
362
///         ipsum: usize,
363
///         +
364
///         fn other_accessor(&self) -> bool;
365
///         // any other trait items can go here
366
///    }
367
/// }
368
///
369
/// fn test_view<V: View>(v: &V) {
370
///     assert_eq!(v.lorem(), "sit");
371
///     assert_eq!(v.ipsum(), &42);
372
/// }
373
///
374
/// // imagine this in the higher-level module
375
/// use derive_more::AsRef;
376
/// #[derive(AsRef)]
377
/// struct Everything {
378
///     #[as_ref] lorem: String,
379
///     #[as_ref] ipsum: usize,
380
///     dolor: Vec<()>,
381
/// }
382
/// impl Supertrait for Everything { }
383
/// impl View for Everything {
384
///     fn other_accessor(&self) -> bool { false }
385
/// }
386
///
387
/// let everything = Everything {
388
///     lorem: "sit".into(),
389
///     ipsum: 42,
390
///     dolor: vec![()],
391
/// };
392
///
393
/// test_view(&everything);
394
/// ```
395
///
396
/// ### Generated code
397
///
398
/// ```
399
/// # pub trait Supertrait { }
400
/// pub trait View: AsRef<String> + AsRef<usize> + Supertrait {
401
///     fn lorem(&self) -> &String { self.as_ref() }
402
///     fn ipsum(&self) -> &usize { self.as_ref() }
403
/// }
404
/// ```
405
#[macro_export]
406
macro_rules! define_accessor_trait {
407
    {
408
        $( #[ $attr:meta ])*
409
        $vis:vis trait $Trait:ident $( : $( $Super:path )* )? {
410
            $( $accessor:ident: $type:ty, )*
411
            $( + $( $rest:tt )* )?
412
        }
413
    } => {
414
        $( #[ $attr ])*
415
        $vis trait $Trait: $( core::convert::AsRef<$type> + )* $( $( $Super + )* )?
416
        {
417
            $(
418
                /// Access the field
419
848
                fn $accessor(&self) -> &$type { core::convert::AsRef::as_ref(self) }
420
            )*
421
            $(
422
                $( $rest )*
423
            )?
424
        }
425
    }
426
}
427

            
428
// ----------------------------------------------------------------------
429

            
430
/// Helper for assisting with macro "argument" defaulting
431
///
432
/// ```ignore
433
/// macro_coalesce_args!{ [ something ]  ... }  // =>   something
434
/// macro_coalesce_args!{ [ ], [ other ] ... }  // =>   other
435
/// // etc.
436
/// ```
437
///
438
/// ### Usage note
439
///
440
/// It is generally possible to avoid use of `macro_coalesce_args`, at the cost of
441
/// providing many alternative matcher patterns.  Using `macro_coalesce_args` can make
442
/// it possible to provide a single pattern with the optional items in `$( )?`.
443
///
444
/// This is valuable because a single pattern with some optional items
445
/// makes much better documentation than several patterns which the reader must compare
446
/// by eye - and it also simplifies the implementation.
447
///
448
/// `macro_coalesce_args` takes each of its possible expansions in `[ ]` and returns
449
/// the first nonempty one.
450
#[macro_export]
451
macro_rules! macro_first_nonempty {
452
    { [ $($yes:tt)+ ] $($rhs:tt)* } => { $($yes)* };
453
    { [ ]$(,)? [ $($otherwise:tt)* ] $($rhs:tt)* } => {
454
        $crate::macro_first_nonempty!{ [ $($otherwise)* ] $($rhs)* }
455
    };
456
}
457

            
458
// ----------------------------------------------------------------------
459

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

            
540
// ----------------------------------------------------------------------
541

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

            
620
    $crate::paste! {
621
        #[allow(non_camel_case_types)]
622
        #[derive(Serialize, Deserialize)]
623
        #[serde(remote=$main_s)]
624
        struct [< $main _Raw >]
625
        $($body)*
626
    }
627
} }
628

            
629
// ----------------------------------------------------------------------
630

            
631
/// Flatten a `Result<Result<T, E>, E>` into a `Result<T, E>`.
632
///
633
/// See [`Result::flatten`], which is not available
634
/// at our current MSRV.
635
// TODO MSRV 1.89: When our MSRV is at least 1.89,
636
// remove this function and replace uses with `Result::flatten`.
637
pub fn flatten<T, E>(x: Result<Result<T, E>, E>) -> Result<T, E> {
638
    match x {
639
        Ok(Ok(x)) => Ok(x),
640
        Err(e) | Ok(Err(e)) => Err(e),
641
    }
642
}
643

            
644
// ----------------------------------------------------------------------
645

            
646
/// Asserts that the type of the expression implements the given trait.
647
///
648
/// Example:
649
///
650
/// ```
651
/// # use tor_basic_utils::assert_val_impl_trait;
652
/// let x: u32 = 0;
653
/// assert_val_impl_trait!(x, Clone);
654
/// ```
655
#[macro_export]
656
macro_rules! assert_val_impl_trait {
657
    ($check:expr, $trait:path $(,)?) => {{
658
234
        fn ensure_trait<T: $trait>(_s: &T) {}
659
        ensure_trait(&$check);
660
    }};
661
}
662

            
663
// ----------------------------------------------------------------------
664

            
665
#[cfg(test)]
666
mod test {
667
    // @@ begin test lint list maintained by maint/add_warning @@
668
    #![allow(clippy::bool_assert_comparison)]
669
    #![allow(clippy::clone_on_copy)]
670
    #![allow(clippy::dbg_macro)]
671
    #![allow(clippy::mixed_attributes_style)]
672
    #![allow(clippy::print_stderr)]
673
    #![allow(clippy::print_stdout)]
674
    #![allow(clippy::single_char_pattern)]
675
    #![allow(clippy::unwrap_used)]
676
    #![allow(clippy::unchecked_time_subtraction)]
677
    #![allow(clippy::useless_vec)]
678
    #![allow(clippy::needless_pass_by_value)]
679
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
680
    use super::*;
681

            
682
    #[test]
683
    fn test_strip_suffix_ignore_ascii_case() {
684
        assert_eq!(
685
            "hi there".strip_suffix_ignore_ascii_case("THERE"),
686
            Some("hi ")
687
        );
688
        assert_eq!("hi here".strip_suffix_ignore_ascii_case("THERE"), None);
689
        assert_eq!("THERE".strip_suffix_ignore_ascii_case("there"), Some(""));
690
        assert_eq!("hi".strip_suffix_ignore_ascii_case("THERE"), None);
691
    }
692
}