use crate::Error;
use actix_web::web::Bytes;
use futures_util::Stream;
use mime::Mime;
use std::{
collections::{HashMap, VecDeque},
fmt,
future::Future,
pin::Pin,
sync::Arc,
};
use tracing::trace;
#[derive(Debug)]
pub struct FileMeta<T> {
pub filename: String,
pub content_type: Mime,
pub result: T,
}
#[derive(Debug)]
pub enum Value<T> {
Map(HashMap<String, Value<T>>),
Array(Vec<Value<T>>),
File(FileMeta<T>),
Bytes(Bytes),
Text(String),
Int(i64),
Float(f64),
}
impl<T> Value<T> {
pub(crate) fn merge(&mut self, rhs: Self) {
match self {
Value::Map(ref mut hm) => {
if let Value::Map(other) = rhs {
other.into_iter().fold(hm, |hm, (key, value)| {
if let Some(v) = hm.get_mut(&key) {
v.merge(value);
} else {
hm.insert(key.to_owned(), value);
}
hm
});
}
}
Value::Array(ref mut v) => {
if let Value::Array(other) = rhs {
v.extend(other);
}
}
_ => (),
}
}
pub fn map(self) -> Option<HashMap<String, Value<T>>> {
match self {
Value::Map(map) => Some(map),
_ => None,
}
}
pub fn array(self) -> Option<Vec<Value<T>>> {
match self {
Value::Array(vec) => Some(vec),
_ => None,
}
}
pub fn file(self) -> Option<FileMeta<T>> {
match self {
Value::File(file_meta) => Some(file_meta),
_ => None,
}
}
pub fn bytes(self) -> Option<Bytes> {
match self {
Value::Bytes(bytes) => Some(bytes),
_ => None,
}
}
pub fn text(self) -> Option<String> {
match self {
Value::Text(text) => Some(text),
_ => None,
}
}
pub fn int(self) -> Option<i64> {
match self {
Value::Int(int) => Some(int),
_ => None,
}
}
pub fn float(self) -> Option<f64> {
match self {
Value::Float(float) => Some(float),
_ => None,
}
}
}
impl<T> From<MultipartContent<T>> for Value<T> {
fn from(mc: MultipartContent<T>) -> Self {
match mc {
MultipartContent::File(file_meta) => Value::File(file_meta),
MultipartContent::Bytes(bytes) => Value::Bytes(bytes),
MultipartContent::Text(string) => Value::Text(string),
MultipartContent::Int(i) => Value::Int(i),
MultipartContent::Float(f) => Value::Float(f),
}
}
}
pub type FileFn<T, E> = Arc<
dyn Fn(
String,
Mime,
Pin<Box<dyn Stream<Item = Result<Bytes, Error>>>>,
) -> Pin<Box<dyn Future<Output = Result<T, E>>>>
+ Send
+ Sync,
>;
pub enum Field<T, E> {
Array(Array<T, E>),
Map(Map<T, E>),
File(FileFn<T, E>),
Bytes,
Int,
Float,
Text,
}
impl<T, E> Clone for Field<T, E> {
fn clone(&self) -> Self {
match self {
Self::Array(a) => Self::Array(a.clone()),
Self::Map(m) => Self::Map(m.clone()),
Self::File(file_fn) => Self::File(Arc::clone(file_fn)),
Self::Bytes => Self::Bytes,
Self::Int => Self::Int,
Self::Float => Self::Float,
Self::Text => Self::Text,
}
}
}
impl<T, E> fmt::Debug for Field<T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Field::Array(ref arr) => f.debug_tuple("Array").field(arr).finish(),
Field::Map(ref map) => f.debug_tuple("Map").field(map).finish(),
Field::File(_) => write!(f, "File"),
Field::Bytes => write!(f, "Bytes"),
Field::Int => write!(f, "Int"),
Field::Float => write!(f, "Float"),
Field::Text => write!(f, "Text"),
}
}
}
impl<T, E> Field<T, E> {
pub fn file<F, Fut>(f: F) -> Self
where
F: Fn(String, Mime, Pin<Box<dyn Stream<Item = Result<Bytes, Error>>>>) -> Fut
+ Send
+ Sync
+ Clone
+ 'static,
Fut: Future<Output = Result<T, E>> + 'static,
E: 'static,
{
Field::File(Arc::new(move |filename, mime, stream| {
let f = f.clone();
Box::pin(async move { (f)(filename, mime, stream).await })
}))
}
pub fn bytes() -> Self {
Field::Bytes
}
pub fn text() -> Self {
Field::Text
}
pub fn int() -> Self {
Field::Int
}
pub fn float() -> Self {
Field::Float
}
pub fn array(field: Field<T, E>) -> Self {
Field::Array(Array::new(field))
}
pub fn map() -> Map<T, E> {
Map::new()
}
fn valid_field(&self, name: VecDeque<&NamePart>) -> Option<FieldTerminator<T, E>> {
trace!("Checking {:?} and {:?}", self, name);
match *self {
Field::Array(ref arr) => arr.valid_field(name),
Field::Map(ref map) => map.valid_field(name),
Field::File(ref file_fn) => {
if name.is_empty() {
Some(FieldTerminator::File(file_fn.clone()))
} else {
None
}
}
Field::Bytes => {
if name.is_empty() {
Some(FieldTerminator::Bytes)
} else {
None
}
}
Field::Int => {
if name.is_empty() {
Some(FieldTerminator::Int)
} else {
None
}
}
Field::Float => {
if name.is_empty() {
Some(FieldTerminator::Float)
} else {
None
}
}
Field::Text => {
if name.is_empty() {
Some(FieldTerminator::Text)
} else {
None
}
}
}
}
}
pub struct Array<T, E> {
inner: Box<Field<T, E>>,
}
impl<T, E> Clone for Array<T, E> {
fn clone(&self) -> Self {
Array {
inner: Box::new((*self.inner).clone()),
}
}
}
impl<T, E> fmt::Debug for Array<T, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Array").field("inner", &self.inner).finish()
}
}
impl<T, E> Array<T, E> {
fn new(field: Field<T, E>) -> Self {
Array {
inner: Box::new(field),
}
}
fn valid_field(&self, mut name: VecDeque<&NamePart>) -> Option<FieldTerminator<T, E>> {
trace!("Checking {:?} and {:?}", self, name);
match name.pop_front() {
Some(NamePart::Array) => self.inner.valid_field(name),
_ => None,
}
}
}
pub struct Map<T, E> {
inner: Vec<(String, Field<T, E>)>,
}
impl<T, E> Clone for Map<T, E> {
fn clone(&self) -> Self {
Map {
inner: self.inner.clone(),
}
}
}
impl<T, E> fmt::Debug for Map<T, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Map").field("inner", &self.inner).finish()
}
}
impl<T, E> Map<T, E> {
fn new() -> Self {
Map { inner: Vec::new() }
}
pub fn field(mut self, key: &str, value: Field<T, E>) -> Self {
self.inner.push((key.to_owned(), value));
self
}
pub fn finalize(self) -> Field<T, E> {
Field::Map(self)
}
fn valid_field(&self, mut name: VecDeque<&NamePart>) -> Option<FieldTerminator<T, E>> {
trace!("Checking {:?} and {:?}", self, name);
match name.pop_front() {
Some(NamePart::Map(name_part)) => self
.inner
.iter()
.find(|&&(ref item, _)| *item == *name_part)
.and_then(|&(_, ref field)| field.valid_field(name)),
_ => None,
}
}
}
pub struct Form<T, E> {
pub(crate) max_fields: u32,
pub(crate) max_field_size: usize,
pub(crate) max_files: u32,
pub(crate) max_file_size: usize,
pub(crate) transform_error: Option<Arc<dyn Fn(Error) -> actix_web::Error + Send + Sync>>,
inner: Map<T, E>,
}
impl<T, E> Clone for Form<T, E> {
fn clone(&self) -> Self {
Form {
max_fields: self.max_fields,
max_field_size: self.max_field_size,
max_files: self.max_files,
max_file_size: self.max_file_size,
transform_error: None,
inner: self.inner.clone(),
}
}
}
impl<T, E> Default for Form<T, E> {
fn default() -> Self {
Self::new()
}
}
impl<T, E> Form<T, E> {
pub fn new() -> Self {
Form {
max_fields: 100,
max_field_size: 10_000,
max_files: 20,
max_file_size: 10_000_000,
transform_error: None,
inner: Map::new(),
}
}
pub fn transform_error(
mut self,
f: impl Fn(Error) -> actix_web::Error + Send + Sync + 'static,
) -> Self {
self.transform_error = Some(Arc::new(f));
self
}
pub fn max_fields(mut self, max: u32) -> Self {
self.max_fields = max;
self
}
pub fn max_field_size(mut self, max: usize) -> Self {
self.max_field_size = max;
self
}
pub fn max_files(mut self, max: u32) -> Self {
self.max_files = max;
self
}
pub fn max_file_size(mut self, max: usize) -> Self {
self.max_file_size = max;
self
}
pub fn field(mut self, name: &str, field: Field<T, E>) -> Self {
self.inner = self.inner.field(name, field);
self
}
pub(crate) fn valid_field(&self, name: VecDeque<&NamePart>) -> Option<FieldTerminator<T, E>> {
self.inner.valid_field(name.clone())
}
}
impl<T, E> fmt::Debug for Form<T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Form").field("inner", &self.inner).finish()
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct ContentDisposition {
pub name: Option<String>,
pub filename: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum NamePart {
Map(String),
Array,
}
impl NamePart {
pub fn is_map(&self) -> bool {
matches!(self, NamePart::Map(_))
}
}
#[derive(Clone)]
pub(crate) enum FieldTerminator<T, E> {
File(FileFn<T, E>),
Bytes,
Int,
Float,
Text,
}
impl<T, E> fmt::Debug for FieldTerminator<T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FieldTerminator::File(_) => write!(f, "File"),
FieldTerminator::Bytes => write!(f, "Bytes"),
FieldTerminator::Int => write!(f, "Int"),
FieldTerminator::Float => write!(f, "Float"),
FieldTerminator::Text => write!(f, "Text"),
}
}
}
pub(crate) type MultipartHash<T> = (Vec<NamePart>, MultipartContent<T>);
pub(crate) type MultipartForm<T> = Vec<MultipartHash<T>>;
#[derive(Debug)]
pub(crate) enum MultipartContent<T> {
File(FileMeta<T>),
Bytes(Bytes),
Text(String),
Int(i64),
Float(f64),
}