pipe_chain/
byte.rs

1//! bytes related combinators
2use crate::{
3    take_while, Incomplete, InvalidRepetition, LenBytes, MapExt, Pipe, Repetition, Tag,
4    TakeAtom,
5};
6use fatal_error::FatalError;
7use std::error::Error as StdError;
8
9impl LenBytes for u8 {
10    fn len_bytes(&self) -> usize { 1 }
11}
12
13/// splits `&[u8]` in bytes
14pub struct ByteAtom<'a>(&'a [u8], usize);
15
16impl<'a> From<&'a [u8]> for ByteAtom<'a> {
17    fn from(value: &'a [u8]) -> Self { ByteAtom(value, 0) }
18}
19
20impl<'a> TakeAtom for ByteAtom<'a> {
21    type Atom = u8;
22    type Container = &'a [u8];
23
24    fn next(&mut self) -> Option<(usize, Self::Atom)> {
25        if let x @ Some(_) = self.0.get(self.1).map(|x| (self.1, *x)) {
26            self.1 += 1;
27            x
28        } else {
29            None
30        }
31    }
32
33    fn split_at(self, index: usize) -> (Self::Container, Self::Container) {
34        (&self.0[index..], &self.0[..index])
35    }
36}
37
38/// splits `&[u8; N]` in bytes
39pub struct ConstByteAtom<'a, const N: usize>(&'a [u8; N], usize);
40
41impl<'a, const N: usize> From<&'a [u8; N]> for ConstByteAtom<'a, N> {
42    fn from(value: &'a [u8; N]) -> Self { ConstByteAtom(value, 0) }
43}
44
45impl<'a, const N: usize> TakeAtom for ConstByteAtom<'a, N> {
46    type Atom = u8;
47    type Container = &'a [u8];
48
49    fn next(&mut self) -> Option<(usize, Self::Atom)> {
50        if self.1 < N {
51            let r = Some((self.1, self.0[self.1]));
52            self.1 += 1;
53            r
54        } else {
55            None
56        }
57    }
58
59    fn split_at(self, index: usize) -> (Self::Container, Self::Container) {
60        (&self.0[index..], &self.0[..index])
61    }
62}
63
64/// creates a pipe that gets a certain quantity of ascii digits
65///
66/// panics if the quantity is invalid
67#[inline]
68pub fn ascii_digits<'a, E>(
69    qty: impl TryInto<Repetition, Error = impl Into<InvalidRepetition>>,
70) -> impl Pipe<&'a [u8], (&'a [u8],), E>
71where
72    Incomplete: Into<E>,
73{
74    let qty = qty.try_into().map_err(Into::into).unwrap();
75    move |i: &'a [u8]| {
76        take_while(|x: u8| x.is_ascii_digit(), qty).apply(ByteAtom::from(i))
77    }
78}
79
80/// Take `n` bytes from a slice
81#[inline]
82pub fn take<'a, E>(n: usize) -> impl Pipe<&'a [u8], (&'a [u8],), E>
83where
84    Incomplete: Into<E>,
85{
86    #[inline]
87    move |x: &'a [u8]| {
88        if x.len() >= n {
89            Ok((&x[n..], (&x[..n],)))
90        } else {
91            Err(FatalError::Error(Incomplete::Size(n - x.len()).into()))
92        }
93    }
94}
95
96/// Take `N` bytes from a slice
97#[inline]
98pub fn const_take<'a, const N: usize, E>() -> impl Pipe<&'a [u8], ([u8; N],), E>
99where
100    Incomplete: Into<E>,
101{
102    #[inline]
103    move |x: &'a [u8]| {
104        if x.len() >= N {
105            Ok((
106                &x[N..],
107                (if let Ok(x) = x[..N].try_into() { x } else { unreachable!() },),
108            ))
109        } else {
110            Err(FatalError::Error(Incomplete::Size(N - x.len()).into()))
111        }
112    }
113}
114
115/// Take a big endian [u8] from a slice
116#[inline]
117pub fn be_u8<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u8,), E>
118where
119    Incomplete: Into<E>,
120{
121    const_take::<1, E>().map(|x: [u8; 1]| (u8::from_be_bytes(x),))
122}
123
124/// Take a big endian [u16] from a slice
125#[inline]
126pub fn be_u16<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u16,), E>
127where
128    Incomplete: Into<E>,
129{
130    const_take::<2, E>().map(|x: [u8; 2]| (u16::from_be_bytes(x),))
131}
132
133/// Take a big endian [u32] from a slice
134#[inline]
135pub fn be_u32<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u32,), E>
136where
137    Incomplete: Into<E>,
138{
139    const_take::<4, E>().map(|x: [u8; 4]| (u32::from_be_bytes(x),))
140}
141
142/// Take a big endian [u64] from a slice
143#[inline]
144pub fn be_u64<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u64,), E>
145where
146    Incomplete: Into<E>,
147{
148    const_take::<8, E>().map(|x: [u8; 8]| (u64::from_be_bytes(x),))
149}
150
151/// Take a little endian [u8] from a slice
152#[inline]
153pub fn le_u8<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u8,), E>
154where
155    Incomplete: Into<E>,
156{
157    const_take::<1, E>().map(|x: [u8; 1]| (u8::from_le_bytes(x),))
158}
159
160/// Take a little endian [u16] from a slice
161#[inline]
162pub fn le_u16<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u16,), E>
163where
164    Incomplete: Into<E>,
165{
166    const_take::<2, E>().map(|x: [u8; 2]| (u16::from_le_bytes(x),))
167}
168
169/// Take a little endian [u32] from a slice
170#[inline]
171pub fn le_u32<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u32,), E>
172where
173    Incomplete: Into<E>,
174{
175    const_take::<4, E>().map(|x: [u8; 4]| (u32::from_le_bytes(x),))
176}
177
178/// Take a little endian [u64] from a slice
179#[inline]
180pub fn le_u64<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (u64,), E>
181where
182    Incomplete: Into<E>,
183{
184    const_take::<8, E>().map(|x: [u8; 8]| (u64::from_le_bytes(x),))
185}
186
187/// Take a big endian [i8] from a slice
188#[inline]
189pub fn be_i8<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i8,), E>
190where
191    Incomplete: Into<E>,
192{
193    const_take::<1, E>().map(|x: [u8; 1]| (i8::from_be_bytes(x),))
194}
195
196/// Take a big endian [i16] from a slice
197#[inline]
198pub fn be_i16<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i16,), E>
199where
200    Incomplete: Into<E>,
201{
202    const_take::<2, E>().map(|x: [u8; 2]| (i16::from_be_bytes(x),))
203}
204
205/// Take a big endian [i32] from a slice
206#[inline]
207pub fn be_i32<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i32,), E>
208where
209    Incomplete: Into<E>,
210{
211    const_take::<4, E>().map(|x: [u8; 4]| (i32::from_be_bytes(x),))
212}
213
214/// Take a big endian [i64] from a slice
215#[inline]
216pub fn be_i64<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i64,), E>
217where
218    Incomplete: Into<E>,
219{
220    const_take::<8, E>().map(|x: [u8; 8]| (i64::from_be_bytes(x),))
221}
222
223/// Take a little endian [i8] from a slice
224#[inline]
225pub fn le_i8<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i8,), E>
226where
227    Incomplete: Into<E>,
228{
229    const_take::<1, E>().map(|x: [u8; 1]| (i8::from_le_bytes(x),))
230}
231
232/// Take a little endian [i16] from a slice
233#[inline]
234pub fn le_i16<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i16,), E>
235where
236    Incomplete: Into<E>,
237{
238    const_take::<2, E>().map(|x: [u8; 2]| (i16::from_le_bytes(x),))
239}
240
241/// Take a little endian [i32] from a slice
242#[inline]
243pub fn le_i32<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i32,), E>
244where
245    Incomplete: Into<E>,
246{
247    const_take::<4, E>().map(|x: [u8; 4]| (i32::from_le_bytes(x),))
248}
249
250/// Take a little endian [i64] from a slice
251#[inline]
252pub fn le_i64<'a, E: StdError + 'static>() -> impl Pipe<&'a [u8], (i64,), E>
253where
254    Incomplete: Into<E>,
255{
256    const_take::<8, E>().map(|x: [u8; 8]| (i64::from_le_bytes(x),))
257}
258
259/// Tag error contains expected and found bytes
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub struct TagBytesError(pub Vec<u8>, pub Vec<u8>);
262
263impl std::fmt::Display for TagBytesError {
264    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265        write!(f, "expected: '{:?}' got: '{:?}'", self.0, self.1)
266    }
267}
268
269impl std::error::Error for TagBytesError {}
270
271impl<'a, 'b, E> Tag<&'a [u8], E> for &'b [u8]
272where
273    E: StdError,
274    TagBytesError: Into<E>,
275    Incomplete: Into<E>,
276{
277    type Output = &'a [u8];
278
279    fn strip_from(&self, input: &'a [u8]) -> Result<(&'a [u8], (Self::Output,)), E> {
280        if let Some(x) = input.strip_prefix(*self) {
281            Ok((x, (&input[..self.len()],)))
282        } else {
283            Err(if self.starts_with(input) {
284                Incomplete::Size(self.len() - input.len()).into()
285            } else {
286                let end = if input.len() < self.len() { input.len() } else { self.len() };
287                TagBytesError(self.to_vec(), input[..end].to_vec()).into()
288            })
289        }
290    }
291}
292
293impl<'a, 'b, E, const N: usize> Tag<&'a [u8], E> for &'b [u8; N]
294where
295    E: StdError,
296    Incomplete: Into<E>,
297    TagBytesError: Into<E>,
298{
299    type Output = &'a [u8];
300
301    fn strip_from(&self, input: &'a [u8]) -> Result<(&'a [u8], (Self::Output,)), E> {
302        if let Some(x) = input.strip_prefix(*self) {
303            Ok((x, (&input[..self.len()],)))
304        } else {
305            Err(if self.starts_with(input) {
306                Incomplete::Size(N - input.len()).into()
307            } else {
308                let end = if input.len() < N { input.len() } else { N };
309                TagBytesError(self.to_vec(), input[..end].to_vec()).into()
310            })
311        }
312    }
313}
314
315/// Tag error contains expected and found bytes
316#[derive(Debug, Clone, PartialEq, Eq)]
317pub struct TagByteError(pub u8, pub u8);
318
319impl std::fmt::Display for TagByteError {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        write!(f, "Expected: '{}' got: '{}'", self.0, self.1)
322    }
323}
324
325impl std::error::Error for TagByteError {}
326
327impl<'a, E> Tag<&'a [u8], E> for u8
328where
329    E: StdError,
330    Incomplete: Into<E>,
331    TagByteError: Into<E>,
332{
333    type Output = u8;
334
335    fn strip_from(&self, input: &'a [u8]) -> Result<(&'a [u8], (Self::Output,)), E> {
336        if let Some(x) = input.strip_prefix(&[*self]) {
337            Ok((x, (*self,)))
338        } else {
339            Err(if input.is_empty() {
340                Incomplete::Size(1).into()
341            } else {
342                TagByteError(*self, input[0]).into()
343            })
344        }
345    }
346}
347
348/// Returns the consumed input instead of the pipe result
349/// ```rust
350/// # use fatal_error::FatalError;
351/// # use pipe_chain::{Pipe, AndExt, AndThenExt, tag, byte::consumed, Incomplete, byte::TagBytesError};
352/// # #[derive(Debug, PartialEq, Eq)]
353/// # enum Error {
354/// #     Incomplete(Incomplete),
355/// #     Tag(TagBytesError),
356/// # }
357/// #
358/// # impl std::fmt::Display for Error {
359/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360/// #         write!(f, "{self:?}")
361/// #     }
362/// # }
363/// #
364/// # impl From<Incomplete> for Error {
365/// #     fn from(value: Incomplete) -> Self { Error::Incomplete(value) }
366/// # }
367/// #
368/// # impl From<TagBytesError> for Error {
369/// #     fn from(value: TagBytesError) -> Self { Error::Tag(value) }
370/// # }
371/// #
372/// # impl std::error::Error for Error {}
373/// assert_eq!(
374///     consumed(tag::<Error, _, _>(b"foo").and(tag(b"bar"))).apply(b"foobarbaz"),
375///     Ok((&b"baz"[..], (&b"foobar"[..],)))
376/// );
377/// ```
378pub fn consumed<'a, O, E>(
379    mut p: impl Pipe<&'a [u8], O, E>,
380) -> impl Pipe<&'a [u8], (&'a [u8],), E> {
381    move |x: &'a [u8]| {
382        let (i, _) = p.apply(x)?;
383        Ok((i, (&x[..x.len() - i.len()],)))
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::TagByteError;
390    use crate::{
391        byte::{ascii_digits, take, TagBytesError},
392        tag, Incomplete, Pipe,
393    };
394    use fatal_error::FatalError;
395
396    #[derive(Debug, PartialEq, Eq)]
397    enum Error {
398        Incomplete(Incomplete),
399        TagByte(TagByteError),
400        TagBytes(TagBytesError),
401    }
402
403    impl std::fmt::Display for Error {
404        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405            write!(f, "{self:?}")
406        }
407    }
408
409    impl From<Incomplete> for Error {
410        fn from(value: Incomplete) -> Self { Error::Incomplete(value) }
411    }
412
413    impl From<TagByteError> for Error {
414        fn from(value: TagByteError) -> Self { Error::TagByte(value) }
415    }
416
417    impl From<TagBytesError> for Error {
418        fn from(value: TagBytesError) -> Self { Error::TagBytes(value) }
419    }
420
421    impl std::error::Error for Error {}
422
423    #[test]
424    fn test_tag() {
425        assert_eq!(
426            tag::<Error, _, _>(b"foo").apply(b"fooo"),
427            Ok((&b"o"[..], (&b"foo"[..],)))
428        );
429        assert_eq!(
430            tag::<Error, _, _>(b"foo").apply(b"fo"),
431            Err(FatalError::Error(Error::Incomplete(Incomplete::Size(1))))
432        );
433        assert_eq!(
434            tag::<Error, _, _>(b"foo").apply(b"faaa"),
435            Err(FatalError::Error(
436                TagBytesError(b"foo".to_vec(), b"faa".to_vec()).into(),
437            ))
438        );
439    }
440
441    #[test]
442    fn test_take() {
443        assert_eq!(take::<Incomplete>(1).apply(b"yolo"), Ok((&b"olo"[..], (&b"y"[..],))));
444        assert_eq!(
445            take::<Incomplete>(8).apply(b"yolo"),
446            Err(FatalError::Error(Incomplete::Size(4)))
447        );
448    }
449
450    #[derive(Debug, PartialEq, Eq)]
451    enum AsciiiDigitErr {
452        Incomplete(Incomplete),
453    }
454
455    impl From<Incomplete> for AsciiiDigitErr {
456        fn from(value: Incomplete) -> Self { AsciiiDigitErr::Incomplete(value) }
457    }
458
459    #[test]
460    fn test_digits() {
461        assert_eq!(
462            ascii_digits::<AsciiiDigitErr>(..).apply(b"0123"),
463            Ok((&b""[..], (&b"0123"[..],)))
464        );
465        assert_eq!(
466            ascii_digits::<AsciiiDigitErr>(..).apply(b""),
467            Ok((&b""[..], (&b""[..],)))
468        );
469        assert_eq!(
470            ascii_digits::<AsciiiDigitErr>(0..).apply(b""),
471            Ok((&b""[..], (&b""[..],)))
472        );
473        assert_eq!(
474            ascii_digits::<AsciiiDigitErr>(10..).apply(b""),
475            Err(FatalError::Error(Incomplete::Unknown.into()))
476        );
477        assert_eq!(
478            ascii_digits::<AsciiiDigitErr>(..=1).apply(b"012345"),
479            Ok((&b"12345"[..], (&b"0"[..],)))
480        );
481        assert_eq!(
482            ascii_digits::<AsciiiDigitErr>(..2).apply(b"012345"),
483            Ok((&b"12345"[..], (&b"0"[..],)))
484        );
485        assert_eq!(
486            ascii_digits::<AsciiiDigitErr>(2..3).apply(b"012345"),
487            Ok((&b"2345"[..], (&b"01"[..],)))
488        );
489        assert_eq!(
490            ascii_digits::<AsciiiDigitErr>(2..=2).apply(b"012345"),
491            Ok((&b"2345"[..], (&b"01"[..],)))
492        );
493        assert_eq!(
494            ascii_digits::<AsciiiDigitErr>(2..=3).apply(b"012345"),
495            Ok((&b"345"[..], (&b"012"[..],)))
496        );
497        assert_eq!(
498            ascii_digits::<AsciiiDigitErr>(2..=6).apply(b"012345abc"),
499            Ok((&b"abc"[..], (&b"012345"[..],)))
500        );
501        assert_eq!(
502            ascii_digits::<AsciiiDigitErr>(7..).apply(b"012345abc"),
503            Err(FatalError::Error(Incomplete::Unknown.into()))
504        );
505    }
506
507    #[test]
508    #[should_panic(
509        expected = "called `Result::unwrap()` on an `Err` value: InvalidRepetition(2, 1)"
510    )]
511    fn test_digits_invalid2() { ascii_digits::<AsciiiDigitErr>(2..2); }
512}