pipe_chain/parser/
pktline.rs

1//! git pktline parser
2use crate::{
3    byte::{take, TagByteError, TagBytesError},
4    tag, Incomplete, MapExt, Pipe, Result,
5};
6use fatal_error::FatalError;
7use std::str::from_utf8_unchecked;
8
9/// PktLine parsing error
10#[derive(Debug)]
11pub enum PktLineError {
12    /// more bytes needed
13    Incomplete(Incomplete),
14    /// invalid line size
15    InvalidSize,
16    /// invalid tag
17    TagByte(TagByteError),
18    /// invalid tag
19    TagBytes(TagBytesError),
20}
21
22impl From<Incomplete> for PktLineError {
23    fn from(value: Incomplete) -> Self { PktLineError::Incomplete(value) }
24}
25
26impl From<TagByteError> for PktLineError {
27    fn from(value: TagByteError) -> Self { PktLineError::TagByte(value) }
28}
29
30impl From<TagBytesError> for PktLineError {
31    fn from(value: TagBytesError) -> Self { PktLineError::TagBytes(value) }
32}
33
34impl std::fmt::Display for PktLineError {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            PktLineError::Incomplete(x) => write!(f, "{x}"),
38            PktLineError::InvalidSize => write!(f, "invalid size"),
39            PktLineError::TagByte(x) => write!(f, "PktLine Error: {x}"),
40            PktLineError::TagBytes(x) => write!(f, "PktLine Error: {x}"),
41        }
42    }
43}
44
45impl std::error::Error for PktLineError {
46    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
47        match self {
48            PktLineError::Incomplete(x) => Some(x),
49            PktLineError::TagByte(x) => Some(x),
50            PktLineError::TagBytes(x) => Some(x),
51            _ => None,
52        }
53    }
54}
55
56/// PktLike packet
57#[derive(Debug, PartialEq, Eq)]
58pub enum PktLine {
59    /// flush packed
60    Flush,
61    /// delim packet
62    Delim,
63    /// end packet
64    End,
65    /// data packet
66    Data(Vec<u8>),
67}
68
69impl PktLine {
70    /// creates a new string representing this packet
71    pub fn as_string(&self) -> String {
72        match self {
73            PktLine::Flush => "0000".to_string(),
74            PktLine::Delim => "0001".to_string(),
75            PktLine::End => "0002".to_string(),
76            PktLine::Data(x) => String::from_utf8_lossy(x).to_string(),
77        }
78    }
79
80    /// creates a new byte array representing this packet
81    pub fn into_bytes(self) -> Vec<u8> {
82        match self {
83            PktLine::Flush => b"0000".to_vec(),
84            PktLine::Delim => b"0001".to_vec(),
85            PktLine::End => b"0002".to_vec(),
86            PktLine::Data(x) => {
87                format!("{:04x}", x.len() + 4).into_bytes().into_iter().chain(x).collect()
88            }
89        }
90    }
91}
92
93/// Extracts a [PktLine] from bytes and returns it with the remaining bytes in case of failure an error is returned.
94pub fn pkt_line<'a>(data: &'a [u8]) -> Result<&'a [u8], (PktLine,), PktLineError> {
95    if data.len() < 4 {
96        return Err(FatalError::Error(Incomplete::Size(4 - data.len()).into()));
97    }
98    let (size, data) = data.split_at(4);
99    let size = match (size[0], size[1], size[2], size[3]) {
100        (
101            b'0'..=b'9' | b'a'..=b'f',
102            b'0'..=b'9' | b'a'..=b'f',
103            b'0'..=b'9' | b'a'..=b'f',
104            b'0'..=b'9' | b'a'..=b'f',
105        ) => Ok(unsafe {
106            u16::from_str_radix(from_utf8_unchecked(size), 16).unwrap_unchecked()
107        }),
108        _ => Err(FatalError::Fatal(PktLineError::InvalidSize)),
109    }?;
110    match size {
111        0 => Ok((data, (PktLine::Flush,))),
112        1 => Ok((data, (PktLine::Delim,))),
113        2 => Ok((data, (PktLine::End,))),
114        x @ 5..=65520 => take((x - 4) as usize)
115            .map1(|x: &'a [u8]| PktLine::Data(x.to_vec()))
116            .apply(data),
117        _ => Err(FatalError::Fatal(PktLineError::InvalidSize)),
118    }
119}
120
121/// Extracts [PktLine::Flush] from bytes
122pub fn pkt_flush(data: &'_ [u8]) -> Result<&'_ [u8], (PktLine,), PktLineError> {
123    tag(b"0000").map1(|_| PktLine::Flush).apply(data)
124}
125
126/// Extracts [PktLine::Delim] from bytes
127pub fn pkt_delim(data: &'_ [u8]) -> Result<&'_ [u8], (PktLine,), PktLineError> {
128    tag(b"0001").map1(|_| PktLine::Delim).apply(data)
129}
130
131/// Extracts [PktLine::End] from bytes
132pub fn pkt_end(data: &'_ [u8]) -> Result<&'_ [u8], (PktLine,), PktLineError> {
133    tag(b"0002").map1(|_| PktLine::End).apply(data)
134}