1
//! Misc helper functions and types for use in parsing network documents
2

            
3
use derive_deftly::define_derive_deftly;
4

            
5
pub(crate) mod rangemap_ext;
6
pub(crate) mod str;
7

            
8
pub mod batching_split_before;
9

            
10
use std::iter::Peekable;
11

            
12
#[cfg(test)]
13
use std::fmt::Display;
14

            
15
define_derive_deftly! {
16
    /// Implement `AsMut<Self>`
17
    ///
18
    /// For Reasons, Rust does not have a blanket:
19
    ///
20
    /// ```rust,ignore
21
    /// impl<T> AsMut<T> for T { .. }
22
    /// ```
23
    ///
24
    /// This derive macro expands to the obvious and trivial implementation,
25
    /// for the type that it's applied to.
26
    //
27
    // TODO move this somewhere lower in the stack, eg tor-basic-utils
28
    export AsMutSelf expect items:
29

            
30
    impl<$tgens> ::std::convert::AsMut<Self> for $ttype where $twheres {
31
2994
        fn as_mut(&mut self) -> &mut Self {
32
            self
33
        }
34
    }
35
}
36

            
37
#[cfg(test)]
38
/// Assert that `$a = $b`; if not, panic with a unidiff
39
//
40
// implementation is in fn assert_eq_or_diff, at the bottom of the file
41
macro_rules! assert_eq_or_diff {
42
    { $a:expr, $b:expr $(,)? } => {
43
        assert_eq_or_diff!($a, $b, "")
44
    };
45
    { $a:expr, $b:expr , $($message:tt)*} => {
46
        $crate::util::assert_eq_or_diff(
47
            &$a,
48
            stringify!($a),
49
            &$b,
50
            stringify!($b),
51
            &format_args!($($message)*),
52
        )
53
    };
54
}
55

            
56
/// An iterator with a `.peek()` method
57
///
58
/// We make this a trait to avoid entangling all the types with `Peekable`.
59
/// Ideally we would do this with `Itertools::PeekingNext`
60
/// but that was not implemented for `&mut PeekingNext`
61
/// when we wrote this code,
62
/// and we need that because we use a lot of `&mut NetdocReader`.
63
/// <https://github.com/rust-itertools/itertools/issues/678>
64
///
65
/// TODO: As of itertools 0.11.0, `PeekingNext` _is_ implemented for
66
/// `&'a mut I where I: PeekingNext`, so we can remove this type some time.
67
///
68
/// # **UNSTABLE**
69
///
70
/// This type is UNSTABLE and not part of the semver guarantees.
71
/// You'll only see it if you ran rustdoc with `--document-private-items`.
72
// This is needed because this is a trait bound for batching_split_before.
73
#[doc(hidden)]
74
pub trait PeekableIterator: Iterator {
75
    /// Inspect the next item, if there is one
76
    fn peek(&mut self) -> Option<&Self::Item>;
77
}
78

            
79
impl<I: Iterator> PeekableIterator for Peekable<I> {
80
    fn peek(&mut self) -> Option<&Self::Item> {
81
        self.peek()
82
    }
83
}
84

            
85
impl<I: PeekableIterator> PeekableIterator for &mut I {
86
19064
    fn peek(&mut self) -> Option<&Self::Item> {
87
19064
        <I as PeekableIterator>::peek(*self)
88
19064
    }
89
}
90

            
91
/// A Private module for declaring a "sealed" trait.
92
pub(crate) mod private {
93
    /// A non-exported trait, used to prevent others from implementing a trait.
94
    ///
95
    /// For more information on this pattern, see [the Rust API
96
    /// guidelines](https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed).
97
    #[expect(dead_code, unreachable_pub)] // TODO keep this Sealed trait in case we want it again?
98
    pub trait Sealed {}
99
}
100

            
101
#[cfg(test)]
102
#[allow(unused)]
103
fn test_as_mut_compiles() {
104
    use derive_deftly::Deftly;
105

            
106
    #[derive(Deftly)]
107
    #[derive_deftly(AsMutSelf)]
108
    struct S<T: Clone>
109
    where
110
        Option<T>: Clone,
111
    {
112
        t: T,
113
    }
114

            
115
    let _: &mut S<()> = S { t: () }.as_mut();
116
}
117

            
118
#[cfg(test)]
119
62
pub(crate) fn regsub(update: &mut String, re: &str, repl: impl regex::Replacer) {
120
62
    *update = regex::Regex::new(&format!("(?m){re}"))
121
62
        .expect(re)
122
62
        .replace_all(update, repl)
123
62
        .to_string();
124
62
}
125

            
126
#[cfg(test)]
127
34
pub(crate) fn assert_eq_or_diff(
128
34
    a: &str,
129
34
    a_what: &str,
130
34
    b: &str,
131
34
    b_what: &str,
132
34
    message: &dyn Display,
133
34
) {
134
    use imara_diff::{Algorithm, BasicLineDiffPrinter, Diff, InternedInput, UnifiedDiffConfig};
135

            
136
34
    if a == b {
137
34
        return;
138
    }
139
    let input = InternedInput::new(a, b);
140
    let mut diff = Diff::compute(Algorithm::Histogram, &input);
141
    diff.postprocess_lines(&input);
142
    panic!(
143
        // rustdoc insists on this unhelpful formatting
144
        "===== document {a_what} =====
145
{a}
146
===== document {b_what} =====
147
{b}
148
===== diff ====
149
{}
150
===== documents differ: {a_what} != {b_what} =====
151
{message}
152
",
153
        diff.unified_diff(
154
            &BasicLineDiffPrinter(&input.interner),
155
            UnifiedDiffConfig::default(),
156
            &input,
157
        ),
158
    );
159
34
}