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 str;
6

            
7
pub mod batching_split_before;
8

            
9
use std::iter::Peekable;
10

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

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

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

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

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

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

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

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

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

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

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

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

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

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