Allow non-path states from upload
This commit is contained in:
parent
bdad3f5145
commit
0c231119ac
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "actix-form-data"
|
||||
description = "Multipart Form Data for Actix Web"
|
||||
version = "0.6.0-beta.4"
|
||||
version = "0.6.0-beta.5"
|
||||
license = "GPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/Aardwolf/actix-form-data.git"
|
||||
|
|
|
@ -27,7 +27,7 @@ use form_data::{Field, Form, Value};
|
|||
#### Overview
|
||||
First, you'd create a form structure you want to parse from the multipart stream.
|
||||
```rust
|
||||
let form = Form::new().field("field-name", Field::text());
|
||||
let form = Form::<()>::new().field("field-name", Field::text());
|
||||
```
|
||||
This creates a form with one required field named "field-name" that will be parsed as text.
|
||||
|
||||
|
@ -69,7 +69,7 @@ use actix_web::{
|
|||
};
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
async fn upload(uploaded_content: Value) -> HttpResponse {
|
||||
async fn upload(uploaded_content: Value<()>) -> HttpResponse {
|
||||
println!("Uploaded Content: {:#?}", uploaded_content);
|
||||
HttpResponse::Created().finish()
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use actix_web::{
|
|||
};
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
async fn upload(uploaded_content: Value) -> HttpResponse {
|
||||
async fn upload(uploaded_content: Value<()>) -> HttpResponse {
|
||||
println!("Uploaded Content: {:#?}", uploaded_content);
|
||||
HttpResponse::Created().finish()
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
while let Some(res) = stream.next().await {
|
||||
res?;
|
||||
}
|
||||
Ok(None) as Result<_, Error>
|
||||
Ok(()) as Result<(), Error>
|
||||
})),
|
||||
);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ use actix_web::{
|
|||
use futures::stream::{Stream, StreamExt, TryStreamExt};
|
||||
use std::{
|
||||
env,
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
|
@ -18,7 +19,7 @@ use tracing::info;
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
struct AppState {
|
||||
form: Form,
|
||||
form: Form<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
|
@ -59,7 +60,7 @@ impl ResponseError for Errors {
|
|||
}
|
||||
}
|
||||
|
||||
async fn upload(uploaded_content: Value) -> HttpResponse {
|
||||
async fn upload(uploaded_content: Value<PathBuf>) -> HttpResponse {
|
||||
info!("Uploaded Content: {:#?}", uploaded_content);
|
||||
|
||||
HttpResponse::Created().finish()
|
||||
|
@ -111,8 +112,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
async move {
|
||||
save_file(stream, count)
|
||||
.await
|
||||
.map(Into::into)
|
||||
.map(Some)
|
||||
.map(PathBuf::from)
|
||||
.map_err(Errors::from)
|
||||
}
|
||||
})),
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
//! };
|
||||
//! use futures::stream::StreamExt;
|
||||
//!
|
||||
//! async fn upload(uploaded_content: Value) -> HttpResponse {
|
||||
//! async fn upload(uploaded_content: Value<()>) -> HttpResponse {
|
||||
//! println!("Uploaded Content: {:#?}", uploaded_content);
|
||||
//! HttpResponse::Created().finish()
|
||||
//! }
|
||||
|
@ -54,7 +54,7 @@
|
|||
//! while let Some(_) = stream.next().await {
|
||||
//! // do something
|
||||
//! }
|
||||
//! Ok(None) as Result<_, std::convert::Infallible>
|
||||
//! Ok(()) as Result<(), std::convert::Infallible>
|
||||
//! })),
|
||||
//! );
|
||||
//!
|
||||
|
|
|
@ -34,22 +34,25 @@ use std::{
|
|||
};
|
||||
use tokio::sync::oneshot::{channel, Receiver};
|
||||
|
||||
struct Uploaded {
|
||||
rx: Receiver<Value>,
|
||||
struct Uploaded<T> {
|
||||
rx: Receiver<Value<T>>,
|
||||
}
|
||||
|
||||
pub struct MultipartMiddleware<S> {
|
||||
form: Form,
|
||||
pub struct MultipartMiddleware<S, T> {
|
||||
form: Form<T>,
|
||||
service: S,
|
||||
}
|
||||
|
||||
impl FromRequest for Value {
|
||||
impl<T> FromRequest for Value<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
type Error = Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
let opt = req.extensions_mut().remove::<Uploaded>();
|
||||
let opt = req.extensions_mut().remove::<Uploaded<T>>();
|
||||
Box::pin(async move {
|
||||
let fut = opt.ok_or(Error::MissingMiddleware)?;
|
||||
|
||||
|
@ -58,15 +61,16 @@ impl FromRequest for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S> Transform<S, ServiceRequest> for Form
|
||||
impl<S, T> Transform<S, ServiceRequest> for Form<T>
|
||||
where
|
||||
S: Service<ServiceRequest, Error = actix_web::Error>,
|
||||
S::Future: 'static,
|
||||
T: 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type InitError = ();
|
||||
type Transform = MultipartMiddleware<S>;
|
||||
type Transform = MultipartMiddleware<S, T>;
|
||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
|
@ -77,10 +81,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S> Service<ServiceRequest> for MultipartMiddleware<S>
|
||||
impl<S, T> Service<ServiceRequest> for MultipartMiddleware<S, T>
|
||||
where
|
||||
S: Service<ServiceRequest, Error = actix_web::Error>,
|
||||
S::Future: 'static,
|
||||
T: 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
|
|
182
src/types.rs
182
src/types.rs
|
@ -25,17 +25,16 @@ use std::{
|
|||
collections::{HashMap, VecDeque},
|
||||
fmt,
|
||||
future::Future,
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
use tracing::trace;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileMeta {
|
||||
pub struct FileMeta<T = ()> {
|
||||
pub filename: String,
|
||||
pub content_type: Mime,
|
||||
pub saved_as: Option<PathBuf>,
|
||||
pub result: T,
|
||||
}
|
||||
|
||||
/// The result of a succesfull parse through a given multipart stream.
|
||||
|
@ -49,7 +48,7 @@ pub struct FileMeta {
|
|||
/// # use std::collections::HashMap;
|
||||
/// # let mut hm = HashMap::new();
|
||||
/// # hm.insert("field-name".to_owned(), Value::Int(32));
|
||||
/// # let value = Value::Map(hm);
|
||||
/// # let value = Value::<()>::Map(hm);
|
||||
/// match value {
|
||||
/// Value::Map(mut hashmap) => {
|
||||
/// match hashmap.remove("field-name") {
|
||||
|
@ -64,17 +63,17 @@ pub struct FileMeta {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub enum Value {
|
||||
Map(HashMap<String, Value>),
|
||||
Array(Vec<Value>),
|
||||
File(FileMeta),
|
||||
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 Value {
|
||||
impl<T> Value<T> {
|
||||
pub(crate) fn merge(&mut self, rhs: Self) {
|
||||
match self {
|
||||
Value::Map(ref mut hm) => {
|
||||
|
@ -99,21 +98,21 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map(self) -> Option<HashMap<String, Value>> {
|
||||
pub fn map(self) -> Option<HashMap<String, Value<T>>> {
|
||||
match self {
|
||||
Value::Map(map) => Some(map),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn array(self) -> Option<Vec<Value>> {
|
||||
pub fn array(self) -> Option<Vec<Value<T>>> {
|
||||
match self {
|
||||
Value::Array(vec) => Some(vec),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(self) -> Option<FileMeta> {
|
||||
pub fn file(self) -> Option<FileMeta<T>> {
|
||||
match self {
|
||||
Value::File(file_meta) => Some(file_meta),
|
||||
_ => None,
|
||||
|
@ -149,8 +148,8 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<MultipartContent> for Value {
|
||||
fn from(mc: MultipartContent) -> Self {
|
||||
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),
|
||||
|
@ -161,29 +160,42 @@ impl From<MultipartContent> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub type FileFn = Arc<
|
||||
pub type FileFn<T> = Arc<
|
||||
dyn Fn(
|
||||
String,
|
||||
Mime,
|
||||
Pin<Box<dyn Stream<Item = Result<Bytes, Error>>>>,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Option<PathBuf>, actix_web::Error>>>>
|
||||
) -> Pin<Box<dyn Future<Output = Result<T, actix_web::Error>>>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
/// The field type represents a field in the form-data that is allowed to be parsed.
|
||||
#[derive(Clone)]
|
||||
pub enum Field {
|
||||
Array(Array),
|
||||
Map(Map),
|
||||
File(FileFn),
|
||||
pub enum Field<T = ()> {
|
||||
Array(Array<T>),
|
||||
Map(Map<T>),
|
||||
File(FileFn<T>),
|
||||
Bytes,
|
||||
Int,
|
||||
Float,
|
||||
Text,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Field {
|
||||
impl<T> Clone for Field<T> {
|
||||
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> fmt::Debug for Field<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Field::Array(ref arr) => f.debug_tuple("Array").field(arr).finish(),
|
||||
|
@ -197,7 +209,7 @@ impl fmt::Debug for Field {
|
|||
}
|
||||
}
|
||||
|
||||
impl Field {
|
||||
impl<T> Field<T> {
|
||||
/// Add a File field with a name generator.
|
||||
///
|
||||
/// The name generator will be called for each file matching this field's key. Keep in mind
|
||||
|
@ -211,7 +223,7 @@ impl Field {
|
|||
/// # use futures::stream::StreamExt;
|
||||
/// #
|
||||
/// let (tx, rx) = channel(1);
|
||||
/// let form = Form::new().field("file-field", Field::file(move |_, _, mut stream| {
|
||||
/// let form = Form::<()>::new().field("file-field", Field::file(move |_, _, mut stream| {
|
||||
/// let mut tx = tx.clone();
|
||||
/// async move {
|
||||
/// while let Some(res) = stream.next().await {
|
||||
|
@ -221,7 +233,7 @@ impl Field {
|
|||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// Ok(None) as Result<_, std::convert::Infallible>
|
||||
/// Ok(()) as Result<_, std::convert::Infallible>
|
||||
/// }
|
||||
/// }));
|
||||
/// ```
|
||||
|
@ -232,7 +244,7 @@ impl Field {
|
|||
+ Sync
|
||||
+ Clone
|
||||
+ 'static,
|
||||
Fut: Future<Output = Result<Option<PathBuf>, E>> + 'static,
|
||||
Fut: Future<Output = Result<T, E>> + 'static,
|
||||
E: Into<actix_web::Error> + 'static,
|
||||
{
|
||||
Field::File(Arc::new(move |filename, mime, stream| {
|
||||
|
@ -246,7 +258,7 @@ impl Field {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// let form = Form::new().field("text-field", Field::bytes());
|
||||
/// let form = Form::<()>::new().field("text-field", Field::bytes());
|
||||
pub fn bytes() -> Self {
|
||||
Field::Bytes
|
||||
}
|
||||
|
@ -256,7 +268,7 @@ impl Field {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// let form = Form::new().field("text-field", Field::text());
|
||||
/// let form = Form::<()>::new().field("text-field", Field::text());
|
||||
pub fn text() -> Self {
|
||||
Field::Text
|
||||
}
|
||||
|
@ -266,7 +278,7 @@ impl Field {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// let form = Form::new().field("int-field", Field::int());
|
||||
/// let form = Form::<()>::new().field("int-field", Field::int());
|
||||
/// ```
|
||||
pub fn int() -> Self {
|
||||
Field::Int
|
||||
|
@ -277,7 +289,7 @@ impl Field {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// let form = Form::new().field("float-field", Field::float());
|
||||
/// let form = Form::<()>::new().field("float-field", Field::float());
|
||||
/// ```
|
||||
pub fn float() -> Self {
|
||||
Field::Float
|
||||
|
@ -289,14 +301,14 @@ impl Field {
|
|||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// # fn main() {
|
||||
/// let form = Form::new()
|
||||
/// let form = Form::<()>::new()
|
||||
/// .field(
|
||||
/// "array-field",
|
||||
/// Field::array(Field::text())
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn array(field: Field) -> Self {
|
||||
pub fn array(field: Field<T>) -> Self {
|
||||
Field::Array(Array::new(field))
|
||||
}
|
||||
|
||||
|
@ -306,7 +318,7 @@ impl Field {
|
|||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// # fn main() {
|
||||
/// let form = Form::new()
|
||||
/// let form = Form::<()>::new()
|
||||
/// .field(
|
||||
/// "map-field",
|
||||
/// Field::map()
|
||||
|
@ -316,11 +328,11 @@ impl Field {
|
|||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn map() -> Map {
|
||||
pub fn map() -> Map<T> {
|
||||
Map::new()
|
||||
}
|
||||
|
||||
fn valid_field(&self, name: VecDeque<&NamePart>) -> Option<FieldTerminator> {
|
||||
fn valid_field(&self, name: VecDeque<&NamePart>) -> Option<FieldTerminator<T>> {
|
||||
trace!("Checking {:?} and {:?}", self, name);
|
||||
match *self {
|
||||
Field::Array(ref arr) => arr.valid_field(name),
|
||||
|
@ -368,19 +380,32 @@ impl Field {
|
|||
///
|
||||
/// The `Array` type should only be constructed in the context of a Form. See the `Form`
|
||||
/// documentation for more information.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Array {
|
||||
inner: Box<Field>,
|
||||
pub struct Array<T = ()> {
|
||||
inner: Box<Field<T>>,
|
||||
}
|
||||
|
||||
impl Array {
|
||||
fn new(field: Field) -> Self {
|
||||
impl<T> Clone for Array<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Array { inner: Box::new((*self.inner).clone()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Array<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Array")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Array<T> {
|
||||
fn new(field: Field<T>) -> Self {
|
||||
Array {
|
||||
inner: Box::new(field),
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_field(&self, mut name: VecDeque<&NamePart>) -> Option<FieldTerminator> {
|
||||
fn valid_field(&self, mut name: VecDeque<&NamePart>) -> Option<FieldTerminator<T>> {
|
||||
trace!("Checking {:?} and {:?}", self, name);
|
||||
match name.pop_front() {
|
||||
Some(name_part) => match name_part {
|
||||
|
@ -393,12 +418,25 @@ impl Array {
|
|||
}
|
||||
|
||||
/// A definition of key-value pairs to be parsed from form data.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Map {
|
||||
inner: Vec<(String, Field)>,
|
||||
pub struct Map<T = ()> {
|
||||
inner: Vec<(String, Field<T>)>,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
impl<T> Clone for Map<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Map { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Map<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Map")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Map<T> {
|
||||
fn new() -> Self {
|
||||
Map { inner: Vec::new() }
|
||||
}
|
||||
|
@ -408,12 +446,12 @@ impl Map {
|
|||
/// ```rust
|
||||
/// # use actix_form_data::Field;
|
||||
/// #
|
||||
/// Field::map()
|
||||
/// Field::<()>::map()
|
||||
/// .field("sub-field", Field::text())
|
||||
/// .field("sub-field-two", Field::text())
|
||||
/// .finalize();
|
||||
/// ```
|
||||
pub fn field(mut self, key: &str, value: Field) -> Self {
|
||||
pub fn field(mut self, key: &str, value: Field<T>) -> Self {
|
||||
self.inner.push((key.to_owned(), value));
|
||||
|
||||
self
|
||||
|
@ -423,16 +461,16 @@ impl Map {
|
|||
/// ```rust
|
||||
/// # use actix_form_data::Field;
|
||||
/// #
|
||||
/// Field::map()
|
||||
/// Field::<()>::map()
|
||||
/// .field("sub-field", Field::text())
|
||||
/// .field("sub-field-two", Field::text())
|
||||
/// .finalize();
|
||||
/// ```
|
||||
pub fn finalize(self) -> Field {
|
||||
pub fn finalize(self) -> Field<T> {
|
||||
Field::Map(self)
|
||||
}
|
||||
|
||||
fn valid_field(&self, mut name: VecDeque<&NamePart>) -> Option<FieldTerminator> {
|
||||
fn valid_field(&self, mut name: VecDeque<&NamePart>) -> Option<FieldTerminator<T>> {
|
||||
trace!("Checking {:?} and {:?}", self, name);
|
||||
match name.pop_front() {
|
||||
Some(name_part) => match name_part {
|
||||
|
@ -453,12 +491,12 @@ impl Map {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// # use actix_form_data::{Form, Field};
|
||||
/// let form = Form::new()
|
||||
/// let form = Form::<()>::new()
|
||||
/// .field("field-name", Field::text())
|
||||
/// .field("second-field", Field::int())
|
||||
/// .field("third-field", Field::float())
|
||||
/// .field("fifth-field", Field::file(|_, _, _| async move {
|
||||
/// Ok(None) as Result<_, std::convert::Infallible>
|
||||
/// Ok(()) as Result<(), std::convert::Infallible>
|
||||
/// }))
|
||||
/// .field(
|
||||
/// "map-field",
|
||||
|
@ -472,18 +510,30 @@ impl Map {
|
|||
/// Field::array(Field::text())
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct Form {
|
||||
pub struct Form<T = ()> {
|
||||
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(crate::error::Error) -> actix_web::Error + Send + Sync>>,
|
||||
inner: Map,
|
||||
inner: Map<T>,
|
||||
}
|
||||
|
||||
impl Form {
|
||||
impl<T> Clone for Form<T> {
|
||||
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: self.transform_error.clone(),
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Form<T> {
|
||||
/// Create a new form
|
||||
///
|
||||
/// If you wish to provide your own executor, use the `with_executor` method.
|
||||
|
@ -550,18 +600,18 @@ impl Form {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn field(mut self, name: &str, field: Field) -> Self {
|
||||
pub fn field(mut self, name: &str, field: Field<T>) -> Self {
|
||||
self.inner = self.inner.field(name, field);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn valid_field(&self, name: VecDeque<&NamePart>) -> Option<FieldTerminator> {
|
||||
pub(crate) fn valid_field(&self, name: VecDeque<&NamePart>) -> Option<FieldTerminator<T>> {
|
||||
self.inner.valid_field(name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Form {
|
||||
impl<T> fmt::Debug for Form<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Form").field("inner", &self.inner).finish()
|
||||
}
|
||||
|
@ -598,15 +648,15 @@ impl NamePart {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum FieldTerminator {
|
||||
File(FileFn),
|
||||
pub(crate) enum FieldTerminator<T = ()> {
|
||||
File(FileFn<T>),
|
||||
Bytes,
|
||||
Int,
|
||||
Float,
|
||||
Text,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FieldTerminator {
|
||||
impl<T> fmt::Debug for FieldTerminator<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
FieldTerminator::File(_) => write!(f, "File"),
|
||||
|
@ -618,12 +668,12 @@ impl fmt::Debug for FieldTerminator {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) type MultipartHash = (Vec<NamePart>, MultipartContent);
|
||||
pub(crate) type MultipartForm = Vec<MultipartHash>;
|
||||
pub(crate) type MultipartHash<T> = (Vec<NamePart>, MultipartContent<T>);
|
||||
pub(crate) type MultipartForm<T> = Vec<MultipartHash<T>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum MultipartContent {
|
||||
File(FileMeta),
|
||||
pub(crate) enum MultipartContent<T = ()> {
|
||||
File(FileMeta<T>),
|
||||
Bytes(Bytes),
|
||||
Text(String),
|
||||
Int(i64),
|
||||
|
|
|
@ -39,7 +39,7 @@ use std::{
|
|||
};
|
||||
use tracing::trace;
|
||||
|
||||
fn consolidate(mf: MultipartForm) -> Value {
|
||||
fn consolidate<T>(mf: MultipartForm<T>) -> Value<T> {
|
||||
mf.into_iter().fold(
|
||||
Value::Map(HashMap::new()),
|
||||
|mut acc, (mut nameparts, content)| {
|
||||
|
@ -99,12 +99,15 @@ fn parse_content_disposition(field: &actix_multipart::Field) -> ContentDispositi
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_file_upload(
|
||||
async fn handle_file_upload<T>(
|
||||
field: actix_multipart::Field,
|
||||
filename: Option<String>,
|
||||
form: Form,
|
||||
file_fn: FileFn,
|
||||
) -> Result<MultipartContent, Error> {
|
||||
form: Form<T>,
|
||||
file_fn: FileFn<T>,
|
||||
) -> Result<MultipartContent<T>, Error>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let filename = filename.ok_or(Error::Filename)?;
|
||||
let path: &Path = filename.as_ref();
|
||||
|
||||
|
@ -116,7 +119,7 @@ async fn handle_file_upload(
|
|||
|
||||
let content_type = field.content_type().clone();
|
||||
|
||||
let saved_as = file_fn(
|
||||
let result = file_fn(
|
||||
filename.clone(),
|
||||
content_type.clone(),
|
||||
Box::pin(field.then(move |res| {
|
||||
|
@ -143,15 +146,18 @@ async fn handle_file_upload(
|
|||
Ok(MultipartContent::File(FileMeta {
|
||||
filename,
|
||||
content_type,
|
||||
saved_as,
|
||||
result,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn handle_form_data(
|
||||
async fn handle_form_data<T>(
|
||||
mut field: actix_multipart::Field,
|
||||
term: FieldTerminator,
|
||||
form: Form,
|
||||
) -> Result<MultipartContent, Error> {
|
||||
term: FieldTerminator<T>,
|
||||
form: Form<T>,
|
||||
) -> Result<MultipartContent<T>, Error>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
trace!("In handle_form_data, term: {:?}", term);
|
||||
let mut bytes = BytesMut::new();
|
||||
|
||||
|
@ -186,10 +192,13 @@ async fn handle_form_data(
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_stream_field(
|
||||
async fn handle_stream_field<T>(
|
||||
field: actix_multipart::Field,
|
||||
form: Form,
|
||||
) -> Result<MultipartHash, Error> {
|
||||
form: Form<T>,
|
||||
) -> Result<MultipartHash<T>, Error>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let content_disposition = parse_content_disposition(&field);
|
||||
|
||||
let name = content_disposition.name.ok_or(Error::Field)?;
|
||||
|
@ -210,7 +219,10 @@ async fn handle_stream_field(
|
|||
}
|
||||
|
||||
/// Handle multipart streams from Actix Web
|
||||
pub async fn handle_multipart(m: actix_multipart::Multipart, form: Form) -> Result<Value, Error> {
|
||||
pub async fn handle_multipart<T>(m: actix_multipart::Multipart, form: Form<T>) -> Result<Value<T>, Error>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let mut multipart_form = Vec::new();
|
||||
let mut file_count: u32 = 0;
|
||||
let mut field_count: u32 = 0;
|
||||
|
@ -244,11 +256,11 @@ pub async fn handle_multipart(m: actix_multipart::Multipart, form: Form) -> Resu
|
|||
Ok(consolidate(multipart_form))
|
||||
}
|
||||
|
||||
fn count(
|
||||
content: &MultipartContent,
|
||||
fn count<T>(
|
||||
content: &MultipartContent<T>,
|
||||
mut file_count: u32,
|
||||
mut field_count: u32,
|
||||
form: &Form,
|
||||
form: &Form<T>,
|
||||
) -> Result<(u32, u32), Error> {
|
||||
match content {
|
||||
MultipartContent::File(_) => {
|
||||
|
|
Loading…
Reference in a new issue