use std::borrow::Cow;
use std::str::{from_utf8, from_utf8_unchecked};
use crate::error::Error;
use crate::types::response_code::ResponseCode;
#[derive(Clone, Debug)]
pub struct RawResponse {
pub(crate) code: ResponseCode,
pub(crate) first_line: Vec<u8>,
pub(crate) data_blocks: Option<DataBlocks>,
}
impl RawResponse {
pub fn code(&self) -> ResponseCode {
self.code
}
pub fn has_data_blocks(&self) -> bool {
match self.data_blocks {
Some(_) => true,
_ => false,
}
}
pub fn data_blocks(&self) -> Option<&DataBlocks> {
self.data_blocks.as_ref()
}
pub fn first_line(&self) -> &[u8] {
&self.first_line
}
pub fn first_line_without_code(&self) -> &[u8] {
&self.first_line[4..]
}
pub fn fail_unless(self, desired: impl Into<ResponseCode>) -> Result<RawResponse, Error> {
if self.code() != desired.into() {
Err(Error::failure(self))
} else {
Ok(self)
}
}
pub fn first_line_to_utf8_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(&self.first_line)
}
pub unsafe fn first_line_as_utf8_unchecked(&self) -> &str {
from_utf8_unchecked(&self.first_line)
}
}
#[derive(Clone, Debug)]
pub struct DataBlocks {
pub(crate) payload: Vec<u8>,
pub(crate) line_boundaries: Vec<(usize, usize)>,
}
impl DataBlocks {
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn payload_as_utf8(&self) -> Result<&str, std::str::Utf8Error> {
from_utf8(&self.payload)
}
pub fn lines(&self) -> Lines<'_> {
Lines {
data_blocks: self,
inner: self.line_boundaries.iter(),
}
}
pub fn unterminated(&self) -> Unterminated<'_> {
Unterminated {
inner: self.lines(),
}
}
pub fn lines_len(&self) -> usize {
self.line_boundaries.len()
}
pub fn payload_len(&self) -> usize {
self.payload.len()
}
pub fn is_empty(&self) -> bool {
self.line_boundaries.is_empty()
}
}
#[derive(Clone, Debug)]
pub struct Lines<'a> {
data_blocks: &'a DataBlocks,
inner: std::slice::Iter<'a, (usize, usize)>,
}
impl<'a> Iterator for Lines<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
if let Some((start, end)) = self.inner.next() {
Some(&self.data_blocks.payload[*start..*end])
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct Unterminated<'a> {
inner: Lines<'a>,
}
impl<'a> Iterator for Unterminated<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
Some(line) if line == b".\r\n" => None,
Some(line) => Some(&line[..line.len() - 2]),
None => None,
}
}
}