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 fn fuzz_eq(&self, _other: &Self) -> bool {
74 true
75 }
76}
77
78impl<'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
92impl<'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}