use bytes::Bytes;
use digest::Digest;
use crate::{
errors::{ErrorKind, HttpError},
storage::StorageError,
};
pub trait UploadInspector<E> {
fn inspect(&mut self, bytes: &Bytes) -> Result<(), E>;
}
#[derive(Debug, thiserror::Error)]
pub enum UploadInspectorError {
#[error("File size too large")]
FileSizeTooLarge,
#[error(transparent)]
IO(#[from] StorageError),
#[error(transparent)]
Read(#[from] axum::Error),
}
impl HttpError for UploadInspectorError {
type Detail = ();
fn status_code(&self) -> http::StatusCode {
match self {
UploadInspectorError::FileSizeTooLarge => http::StatusCode::PAYLOAD_TOO_LARGE,
UploadInspectorError::IO(_) => http::StatusCode::INTERNAL_SERVER_ERROR,
UploadInspectorError::Read(_) => http::StatusCode::BAD_REQUEST,
}
}
fn error_kind(&self) -> &'static str {
match self {
UploadInspectorError::FileSizeTooLarge => ErrorKind::UploadTooLarge,
UploadInspectorError::IO(_) => ErrorKind::IO,
UploadInspectorError::Read(_) => ErrorKind::RequestRead,
}
.as_str()
}
fn error_detail(&self) -> Self::Detail {
()
}
}
pub struct UploadSize {
size: usize,
limit: Option<usize>,
}
impl UploadSize {
pub fn new(limit: Option<usize>) -> Self {
Self { size: 0, limit }
}
pub fn finish(self) -> usize {
self.size
}
}
impl UploadInspector<UploadInspectorError> for UploadSize {
fn inspect(&mut self, bytes: &Bytes) -> Result<(), UploadInspectorError> {
self.size += bytes.len();
let too_large = self.limit.map(|l| self.size > l).unwrap_or(false);
if too_large {
return Err(UploadInspectorError::FileSizeTooLarge);
}
Ok(())
}
}
pub struct UploadHasher<D: Digest> {
hasher: D,
}
impl<D: Digest> UploadHasher<D> {
pub fn new() -> Self {
Self { hasher: D::new() }
}
pub fn finish(self) -> digest::Output<D> {
self.hasher.finalize()
}
}
impl<D: Digest> UploadInspector<UploadInspectorError> for UploadHasher<D> {
fn inspect(&mut self, bytes: &Bytes) -> Result<(), UploadInspectorError> {
self.hasher.update(bytes);
Ok(())
}
}