use crate::{Boundary, MultipartError, MultipartEvent, MultipartLimits, MultipartParser, PartHead};
use std::fmt;
use std::iter::FusedIterator;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Part<'a> {
head: PartHead<'a>,
content: &'a [u8],
}
impl<'a> Part<'a> {
pub fn head(&self) -> PartHead<'a> {
self.head
}
pub fn content(&self) -> &'a [u8] {
self.content
}
}
impl fmt::Debug for Part<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Part")
.field("head", &self.head)
.field("content", &format_args!("<{} bytes>", self.content.len()))
.finish()
}
}
#[derive(Clone)]
pub struct Parts<'a> {
parser: MultipartParser,
body: &'a [u8],
position: usize,
failed: bool,
}
pub fn parts<'a>(body: &'a [u8], boundary: &Boundary) -> Parts<'a> {
Parts::with_limits(body, boundary, MultipartLimits::default())
}
impl<'a> Parts<'a> {
pub fn with_limits(body: &'a [u8], boundary: &Boundary, limits: MultipartLimits) -> Self {
Self {
parser: MultipartParser::with_limits(boundary, limits),
body,
position: 0,
failed: false,
}
}
fn next_part(&mut self) -> Result<Option<Part<'a>>, MultipartError> {
let body = self.body;
let (mut head, mut content, mut start) = (None, self.position..self.position, None);
loop {
let parsed = self.parser.parse(&body[self.position..])?;
self.position += parsed.consumed;
match parsed.event {
MultipartEvent::Part(part) => {
head = Some(part);
content = self.position..self.position;
start = None;
}
MultipartEvent::Content(piece) => {
content = *start.get_or_insert(self.position - piece.len())..self.position
}
MultipartEvent::PartEnd => {
return Ok(head.map(|head| Part {
head,
content: &body[content],
}));
}
MultipartEvent::End => return Ok(None),
MultipartEvent::Incomplete => return self.parser.finish().and(Ok(None)),
}
}
}
}
impl fmt::Debug for Parts<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Parts")
.field("position", &self.position)
.field("length", &self.body.len())
.finish()
}
}
impl<'a> Iterator for Parts<'a> {
type Item = Result<Part<'a>, MultipartError>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed {
return None;
}
let result = self.next_part().transpose();
self.failed = !matches!(result, Some(Ok(_)));
result
}
}
impl FusedIterator for Parts<'_> {}