Skip to main content

libaprs_engine/
transport.rs

1use std::io::{self, Read};
2
3use crate::MAX_PACKET_LEN;
4
5/// Default maximum byte batch accepted by transport helper readers.
6pub const DEFAULT_TRANSPORT_READ_LIMIT: usize = 1024 * 1024;
7
8/// Stable transport diagnostic categories for logs and external systems.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum TransportErrorCode {
11    /// Input exceeded the configured byte limit.
12    OversizedInput,
13    /// Input framing was invalid for the transport.
14    InvalidFrame,
15    /// Transport reached EOF before a complete packet/frame was available.
16    UnexpectedEof,
17    /// Transport operation timed out.
18    Timeout,
19    /// Underlying I/O failed.
20    Io,
21}
22
23impl TransportErrorCode {
24    /// Returns a stable machine-readable diagnostic code.
25    #[must_use]
26    pub const fn code(self) -> &'static str {
27        match self {
28            Self::OversizedInput => "transport.oversized_input",
29            Self::InvalidFrame => "transport.invalid_frame",
30            Self::UnexpectedEof => "transport.unexpected_eof",
31            Self::Timeout => "transport.timeout",
32            Self::Io => "transport.io",
33        }
34    }
35}
36
37/// Common packet-source contract for transport adapters.
38pub trait PacketSource {
39    /// Source-specific error type.
40    type Error;
41
42    /// Reads a bounded batch of packet byte vectors.
43    fn recv_packets(&mut self) -> Result<Vec<Vec<u8>>, Self::Error>;
44}
45
46/// Common packet-sink contract for transport adapters.
47pub trait PacketSink {
48    /// Sink-specific error type.
49    type Error;
50
51    /// Sends one packet byte slice without mutating or normalizing it.
52    fn send_packet(&mut self, packet: &[u8]) -> Result<(), Self::Error>;
53}
54
55/// Reads all available bytes from a reader while enforcing a hard limit.
56pub fn read_all_with_limit(mut reader: impl Read, max_bytes: usize) -> io::Result<Vec<u8>> {
57    let mut input = Vec::new();
58    let mut limited = (&mut reader).take(max_bytes.saturating_add(1) as u64);
59    limited.read_to_end(&mut input)?;
60    if input.len() > max_bytes {
61        return Err(oversized_input_error());
62    }
63    Ok(input)
64}
65
66/// Creates the standard oversized-input transport error.
67#[must_use]
68pub fn oversized_input_error() -> io::Error {
69    io::Error::new(
70        io::ErrorKind::InvalidData,
71        TransportErrorCode::OversizedInput.code(),
72    )
73}
74
75/// Line-oriented packet source for file/stdin style transports.
76#[derive(Clone, Debug, Eq, PartialEq)]
77pub struct LineTransport<'a> {
78    input: &'a [u8],
79}
80
81impl<'a> LineTransport<'a> {
82    /// Creates a transport over newline-separated packet bytes.
83    #[must_use]
84    pub fn new(input: &'a [u8]) -> Self {
85        Self { input }
86    }
87
88    /// Iterates packet lines without trailing CR/LF bytes.
89    #[must_use]
90    pub fn packets(&self) -> Vec<&'a [u8]> {
91        self.input
92            .split(|byte| *byte == b'\n')
93            .map(trim_trailing_carriage_return)
94            .filter(|line| !line.is_empty())
95            .collect()
96    }
97
98    /// Iterates packet lines while enforcing a per-packet byte limit.
99    ///
100    /// The limit is applied after removing one trailing carriage return from
101    /// CRLF-framed lines and before allocating owned packet copies.
102    pub fn packets_with_limit(&self, max_packet_len: usize) -> io::Result<Vec<&'a [u8]>> {
103        let mut packets = Vec::new();
104        for line in self
105            .input
106            .split(|byte| *byte == b'\n')
107            .map(trim_trailing_carriage_return)
108            .filter(|line| !line.is_empty())
109        {
110            if line.len() > max_packet_len {
111                return Err(oversized_input_error());
112            }
113            packets.push(line);
114        }
115        Ok(packets)
116    }
117}
118
119impl PacketSource for LineTransport<'_> {
120    type Error = io::Error;
121
122    fn recv_packets(&mut self) -> Result<Vec<Vec<u8>>, Self::Error> {
123        Ok(self
124            .packets_with_limit(MAX_PACKET_LEN)?
125            .into_iter()
126            .map(<[u8]>::to_vec)
127            .collect())
128    }
129}
130
131impl PacketSink for Vec<Vec<u8>> {
132    type Error = io::Error;
133
134    fn send_packet(&mut self, packet: &[u8]) -> Result<(), Self::Error> {
135        self.push(packet.to_vec());
136        Ok(())
137    }
138}
139
140fn trim_trailing_carriage_return(line: &[u8]) -> &[u8] {
141    line.strip_suffix(b"\r").unwrap_or(line)
142}