Skip to main content

eml_codec/
raw_input.rs

1#[cfg(feature = "arbitrary")]
2use crate::fuzz_eq::FuzzEq;
3use crate::i18n::ContainsUtf8;
4#[cfg(feature = "arbitrary")]
5use arbitrary::Arbitrary;
6use bounded_static::{IntoBoundedStatic, ToBoundedStatic};
7use std::borrow::Cow;
8use std::fmt;
9
10#[derive(Clone, PartialEq)]
11pub struct RawInput<'a>(pub Option<&'a [u8]>);
12
13impl<'a> RawInput<'a> {
14    pub fn none() -> RawInput<'static> {
15        RawInput(None)
16    }
17
18    pub fn unwrap(&self) -> &'a [u8] {
19        match self.0 {
20            None => panic!("Called RawInput::unwrap() on a RawInput::none()"),
21            Some(s) => s,
22        }
23    }
24}
25
26impl<'a> From<&'a [u8]> for RawInput<'a> {
27    fn from(s: &'a [u8]) -> Self {
28        Self(Some(s))
29    }
30}
31
32impl<'a, const N: usize> From<&'a [u8; N]> for RawInput<'a> {
33    fn from(s: &'a [u8; N]) -> Self {
34        Self(Some(s))
35    }
36}
37
38impl<'a> fmt::Debug for RawInput<'a> {
39    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match &self.0 {
41            None => fmt.debug_tuple("None").finish(),
42            Some(s) => {
43                let maxlen = 100;
44                let disp: Cow<[u8]> = if s.len() <= maxlen {
45                    Cow::Borrowed(s)
46                } else {
47                    let mut disp = vec![];
48                    disp.extend_from_slice(&s[0..maxlen / 2]);
49                    disp.extend_from_slice(b"..");
50                    disp.extend_from_slice(&s[s.len() - (maxlen / 2)..s.len()]);
51                    Cow::Owned(disp)
52                };
53                fmt.debug_tuple("Some")
54                    .field(&String::from_utf8_lossy(&disp))
55                    .finish()
56            }
57        }
58    }
59}
60
61#[cfg(feature = "arbitrary")]
62impl<'a> Arbitrary<'a> for RawInput<'a> {
63    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
64        Ok(RawInput(None))
65    }
66}
67
68#[cfg(feature = "arbitrary")]
69impl<'a> FuzzEq for RawInput<'a> {
70    /// `RawInput` data is always informational and never part of the proper
71    /// "canonical" AST data. We thus ignore its content when comparing it with
72    /// fuzz_eq.
73    fn fuzz_eq(&self, _other: &Self) -> bool {
74        true
75    }
76}
77
78// Moving to an owned version discards the reference to the input.
79impl<'a> IntoBoundedStatic for RawInput<'a> {
80    type Static = RawInput<'static>;
81    fn into_static(self) -> Self::Static {
82        RawInput(None)
83    }
84}
85impl<'a> ToBoundedStatic for RawInput<'a> {
86    type Static = RawInput<'static>;
87    fn to_static(&self) -> Self::Static {
88        RawInput(None)
89    }
90}
91
92// Ignore RawInput values wrt ContainsUtf8
93impl<'a> ContainsUtf8 for RawInput<'a> {
94    fn contains_utf8(&self) -> bool {
95        false
96    }
97}
98
99#[cfg(test)]
100impl<'a> RawInput<'a> {
101    pub(crate) fn between(input: &'a [u8], prefix: &[u8], suffix: &[u8]) -> Self {
102        use memchr::memmem;
103        let prefix_matches: Vec<_> = memmem::find_iter(input, prefix).collect();
104        if prefix_matches.len() != 1 {
105            panic!("{} prefix matches (expected: 1)", prefix_matches.len());
106        }
107        let prefix_pos = prefix_matches[0];
108        let suffix_matches: Vec<_> =
109            memmem::find_iter(&input[prefix_pos + prefix.len()..], suffix).collect();
110        if suffix_matches.len() != 1 {
111            panic!("{} suffix matches (expected: 1)", suffix_matches.len());
112        }
113        let suffix_pos = suffix_matches[0] + prefix_pos + prefix.len();
114        RawInput(Some(&input[prefix_pos..suffix_pos + suffix.len()]))
115    }
116
117    pub(crate) fn between_excl(input: &'a [u8], before: &[u8], after: &[u8]) -> Self {
118        use memchr::memmem;
119        let before_matches: Vec<_> = memmem::find_iter(input, before).collect();
120        if before_matches.len() != 1 {
121            panic!("{} before matches (expected: 1)", before_matches.len());
122        }
123        let before_pos = before_matches[0];
124        let after_matches: Vec<_> =
125            memmem::find_iter(&input[before_pos + before.len()..], after).collect();
126        if after_matches.len() != 1 {
127            panic!("{} after matches (expected: 1)", after_matches.len());
128        }
129        let after_pos = after_matches[0] + before_pos + before.len();
130        RawInput(Some(&input[before_pos + before.len()..after_pos]))
131    }
132}