use std::io::{self, Read};
use bytes::Bytes;
#[cfg(feature = "async")]
use futures_util::io::{AsyncRead, AsyncReadExt};
use crate::{model::IppVersion, payload::IppPayload, IppHeader};
#[cfg(feature = "async")]
pub struct AsyncIppReader<R> {
inner: R,
}
#[cfg(feature = "async")]
impl<R> AsyncIppReader<R>
where
R: 'static + AsyncRead + Send + Sync + Unpin,
{
pub fn new(inner: R) -> Self {
AsyncIppReader { inner }
}
async fn read_bytes(&mut self, len: usize) -> io::Result<Bytes> {
let mut buf = vec![0; len];
self.inner.read_exact(&mut buf).await?;
Ok(buf.into())
}
async fn read_string(&mut self, len: usize) -> io::Result<String> {
self.read_bytes(len)
.await
.map(|b| String::from_utf8_lossy(&b).into_owned())
}
async fn read_u16(&mut self) -> io::Result<u16> {
let mut buf = [0u8; 2];
self.inner.read_exact(&mut buf).await?;
Ok(u16::from_be_bytes(buf))
}
async fn read_u8(&mut self) -> io::Result<u8> {
let mut buf = [0u8; 1];
self.inner.read_exact(&mut buf).await?;
Ok(buf[0])
}
async fn read_u32(&mut self) -> io::Result<u32> {
let mut buf = [0u8; 4];
self.inner.read_exact(&mut buf).await?;
Ok(u32::from_be_bytes(buf))
}
pub async fn read_tag(&mut self) -> io::Result<u8> {
self.read_u8().await
}
pub async fn read_name(&mut self) -> io::Result<String> {
let name_len = self.read_u16().await?;
self.read_string(name_len as usize).await
}
pub async fn read_value(&mut self) -> io::Result<Bytes> {
let value_len = self.read_u16().await?;
self.read_bytes(value_len as usize).await
}
pub async fn read_header(&mut self) -> io::Result<IppHeader> {
let version = IppVersion(self.read_u16().await?);
let operation_status = self.read_u16().await?;
let request_id = self.read_u32().await?;
Ok(IppHeader::new(version, operation_status, request_id))
}
pub fn into_payload(self) -> IppPayload {
IppPayload::new_async(self.inner)
}
}
#[cfg(feature = "async")]
impl<R> From<R> for AsyncIppReader<R>
where
R: 'static + AsyncRead + Send + Sync + Unpin,
{
fn from(r: R) -> Self {
AsyncIppReader::new(r)
}
}
pub struct IppReader<R> {
inner: R,
}
impl<R> IppReader<R>
where
R: 'static + Read + Send + Sync,
{
pub fn new(inner: R) -> Self {
IppReader { inner }
}
fn read_bytes(&mut self, len: usize) -> io::Result<Bytes> {
let mut buf = vec![0; len];
self.inner.read_exact(&mut buf)?;
Ok(buf.into())
}
fn read_string(&mut self, len: usize) -> io::Result<String> {
self.read_bytes(len).map(|b| String::from_utf8_lossy(&b).into_owned())
}
fn read_u16(&mut self) -> io::Result<u16> {
let mut buf = [0u8; 2];
self.inner.read_exact(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}
fn read_u8(&mut self) -> io::Result<u8> {
let mut buf = [0u8; 1];
self.inner.read_exact(&mut buf)?;
Ok(buf[0])
}
fn read_u32(&mut self) -> io::Result<u32> {
let mut buf = [0u8; 4];
self.inner.read_exact(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}
pub fn read_tag(&mut self) -> io::Result<u8> {
self.read_u8()
}
pub fn read_name(&mut self) -> io::Result<String> {
let name_len = self.read_u16()?;
self.read_string(name_len as usize)
}
pub fn read_value(&mut self) -> io::Result<Bytes> {
let value_len = self.read_u16()?;
self.read_bytes(value_len as usize)
}
pub fn read_header(&mut self) -> io::Result<IppHeader> {
let version = IppVersion(self.read_u16()?);
let operation_status = self.read_u16()?;
let request_id = self.read_u32()?;
Ok(IppHeader::new(version, operation_status, request_id))
}
pub fn into_payload(self) -> IppPayload {
IppPayload::new(self.inner)
}
}
impl<R> From<R> for IppReader<R>
where
R: 'static + Read + Send + Sync,
{
fn from(r: R) -> Self {
IppReader::new(r)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::StatusCode;
#[test]
fn test_read_name() {
let data = io::Cursor::new(vec![0x00, 0x04, b't', b'e', b's', b't']);
let mut reader = IppReader::new(data);
let name = reader.read_name().unwrap();
assert_eq!(name, "test");
}
#[test]
fn test_read_value() {
let data = io::Cursor::new(vec![0x00, 0x04, b't', b'e', b's', b't']);
let mut reader = IppReader::new(data);
let value = reader.read_value().unwrap();
assert_eq!(value.as_ref(), b"test");
}
#[test]
fn test_read_header() {
let data = io::Cursor::new(vec![0x01, 0x01, 0x04, 0x01, 0x11, 0x22, 0x33, 0x44]);
let mut reader = IppReader::new(data);
let header = reader.read_header().unwrap();
assert_eq!(header.version, IppVersion::v1_1());
assert_eq!(header.operation_or_status, 0x401);
assert_eq!(header.request_id, 0x11223344);
assert_eq!(header.status_code(), StatusCode::ClientErrorForbidden);
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_async_read_name() {
let data = futures_util::io::Cursor::new(vec![0x00, 0x04, b't', b'e', b's', b't']);
let mut reader = AsyncIppReader::new(data);
let name = reader.read_name().await.unwrap();
assert_eq!(name, "test");
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_async_read_value() {
let data = futures_util::io::Cursor::new(vec![0x00, 0x04, b't', b'e', b's', b't']);
let mut reader = AsyncIppReader::new(data);
let value = reader.read_value().await.unwrap();
assert_eq!(value.as_ref(), b"test");
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_async_read_header() {
let data = futures_util::io::Cursor::new(vec![0x01, 0x01, 0x04, 0x01, 0x11, 0x22, 0x33, 0x44]);
let mut reader = AsyncIppReader::new(data);
let header = reader.read_header().await.unwrap();
assert_eq!(header.version, IppVersion::v1_1());
assert_eq!(header.operation_or_status, 0x401);
assert_eq!(header.request_id, 0x11223344);
assert_eq!(header.status_code(), StatusCode::ClientErrorForbidden);
}
}