Skip to main content

tor_netdoc/
util.rs

1//! Misc helper functions and types for use in parsing network documents
2
3use derive_deftly::define_derive_deftly;
4
5pub(crate) mod str;
6
7pub mod batching_split_before;
8
9use std::iter::Peekable;
10
11#[cfg(test)]
12use std::fmt::Display;
13
14define_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        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
40macro_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)]
73pub trait PeekableIterator: Iterator {
74    /// Inspect the next item, if there is one
75    fn peek(&mut self) -> Option<&Self::Item>;
76}
77
78impl<I: Iterator> PeekableIterator for Peekable<I> {
79    fn peek(&mut self) -> Option<&Self::Item> {
80        self.peek()
81    }
82}
83
84impl<I: PeekableIterator> PeekableIterator for &mut I {
85    fn peek(&mut self) -> Option<&Self::Item> {
86        <I as PeekableIterator>::peek(*self)
87    }
88}
89
90/// A Private module for declaring a "sealed" trait.
91pub(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)]
102fn 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)]
118pub(crate) fn regsub(update: &mut String, re: &str, repl: impl regex::Replacer) {
119    *update = regex::Regex::new(&format!("(?m){re}"))
120        .expect(re)
121        .replace_all(update, repl)
122        .to_string();
123}
124
125#[cfg(test)]
126pub(crate) fn assert_eq_or_diff(
127    a: &str,
128    a_what: &str,
129    b: &str,
130    b_what: &str,
131    message: &dyn Display,
132) {
133    use imara_diff::{Algorithm, BasicLineDiffPrinter, Diff, InternedInput, UnifiedDiffConfig};
134
135    if a == b {
136        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}