use crate::error::FormError;
use crate::multipart::parser::{PartHead, PartSummary};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Refusal {
pub status: u16,
pub code: &'static str,
}
impl Refusal {
#[must_use]
pub const fn new(status: u16, code: &'static str) -> Refusal {
Refusal { status, code }
}
}
#[must_use]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Verdict {
Continue,
Skip,
Refuse(Refusal),
}
pub trait Inspector {
fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
let _ = head;
Verdict::Continue
}
fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
let _ = (head, first);
Verdict::Continue
}
fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
let _ = (part, chunk);
Verdict::Continue
}
fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
let _ = (name, value);
Verdict::Continue
}
fn part_end(&mut self, summary: &PartSummary) -> Verdict {
let _ = summary;
Verdict::Continue
}
fn end(&mut self) -> Verdict {
Verdict::Continue
}
fn failed(&mut self, error: &FormError) {
let _ = error;
}
}
impl<I: Inspector + ?Sized> Inspector for &mut I {
fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
(**self).part_start(head)
}
fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
(**self).sniff(head, first)
}
fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
(**self).data(part, chunk)
}
fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
(**self).field(name, value)
}
fn part_end(&mut self, summary: &PartSummary) -> Verdict {
(**self).part_end(summary)
}
fn end(&mut self) -> Verdict {
(**self).end()
}
fn failed(&mut self, error: &FormError) {
(**self).failed(error);
}
}
impl<A: Inspector, B: Inspector> Inspector for (A, B) {
fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
both(self.0.part_start(head), || self.1.part_start(head))
}
fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
both(self.0.sniff(head, first), || self.1.sniff(head, first))
}
fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
both(self.0.data(part, chunk), || self.1.data(part, chunk))
}
fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
both(self.0.field(name, value), || self.1.field(name, value))
}
fn part_end(&mut self, summary: &PartSummary) -> Verdict {
both(self.0.part_end(summary), || self.1.part_end(summary))
}
fn end(&mut self) -> Verdict {
both(self.0.end(), || self.1.end())
}
fn failed(&mut self, error: &FormError) {
self.0.failed(error);
self.1.failed(error);
}
}
fn both(first: Verdict, second: impl FnOnce() -> Verdict) -> Verdict {
if matches!(first, Verdict::Refuse(_)) {
return first;
}
match second() {
Verdict::Refuse(r) => Verdict::Refuse(r),
Verdict::Skip => Verdict::Skip,
_ => first,
}
}
#[cfg(feature = "alloc")]
impl<I: Inspector + ?Sized> Inspector for alloc::boxed::Box<I> {
fn part_start(&mut self, head: &PartHead<'_>) -> Verdict {
(**self).part_start(head)
}
fn sniff(&mut self, head: &PartHead<'_>, first: &[u8]) -> Verdict {
(**self).sniff(head, first)
}
fn data(&mut self, part: u32, chunk: &[u8]) -> Verdict {
(**self).data(part, chunk)
}
fn field(&mut self, name: &str, value: &[u8]) -> Verdict {
(**self).field(name, value)
}
fn part_end(&mut self, summary: &PartSummary) -> Verdict {
(**self).part_end(summary)
}
fn end(&mut self) -> Verdict {
(**self).end()
}
fn failed(&mut self, error: &FormError) {
(**self).failed(error);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoInspector;
impl Inspector for NoInspector {}
pub trait Sink {
type Output;
fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError>;
fn write(&mut self, chunk: &[u8]) -> Result<(), FormError>;
fn close(&mut self) -> Result<Self::Output, FormError>;
fn abort(&mut self);
}
#[derive(Clone, Copy, Debug, Default)]
pub struct DiscardSink {
bytes: u64,
}
impl Sink for DiscardSink {
type Output = u64;
fn open(&mut self, _head: &PartHead<'_>) -> Result<(), FormError> {
self.bytes = 0;
Ok(())
}
fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
self.bytes = self.bytes.saturating_add(chunk.len() as u64);
Ok(())
}
fn close(&mut self) -> Result<u64, FormError> {
Ok(core::mem::take(&mut self.bytes))
}
fn abort(&mut self) {
self.bytes = 0;
}
}
#[cfg(feature = "alloc")]
mod memory {
use alloc::string::String;
use alloc::vec::Vec;
use super::Sink;
use crate::error::FormError;
use crate::extract::FileData;
use crate::multipart::parser::PartHead;
#[derive(Debug, Default)]
pub struct MemorySink {
current: Option<FileData>,
}
impl MemorySink {
#[must_use]
pub fn new() -> MemorySink {
MemorySink { current: None }
}
}
impl Sink for MemorySink {
type Output = FileData;
fn open(&mut self, head: &PartHead<'_>) -> Result<(), FormError> {
let filename = head
.filename()
.and_then(|f| core::str::from_utf8(f.raw()).ok().map(String::from));
let content_type = head
.content_type()
.and_then(|c| core::str::from_utf8(c.raw()).ok().map(String::from));
self.current = Some(FileData::new(Vec::new(), filename, content_type));
Ok(())
}
fn write(&mut self, chunk: &[u8]) -> Result<(), FormError> {
if let Some(f) = self.current.as_mut() {
f.append(chunk);
}
Ok(())
}
fn close(&mut self) -> Result<FileData, FormError> {
Ok(self.current.take().unwrap_or_default())
}
fn abort(&mut self) {
self.current = None;
}
}
}
#[cfg(feature = "alloc")]
pub use memory::MemorySink;