use actix_web::{FromRequest, HttpRequest, dev::Payload};
use serde::de::DeserializeOwned;
use std::future::{ready, Ready};
use crate::validation::validators::ValidationError;
#[derive(Debug)]
pub struct ValidatedForm<T>(pub T);
impl<T: DeserializeOwned + Validate> FromRequest for ValidatedForm<T> {
type Error = actix_web::Error;
type Future = Ready<Result<Self, Self::Error>>;
async fn from_request(req: &HttpRequest, payload: &mut Payload) -> Result<Self, Self::Error> {
let form = actix_web::web::Form::<T>::from_request(req, payload).await?;
let inner = form.into_inner();
inner.validate().map(ValidatedForm).map_err(|_| {
actix_web::error::ErrorBadRequest("Validation failed")
})
}
}
#[derive(Debug)]
pub struct ValidatedQuery<T>(pub T);
impl<T: DeserializeOwned + Validate> FromRequest for ValidatedQuery<T> {
type Error = actix_web::Error;
type Future = Ready<Result<Self, Self::Error>>;
async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result<Self, Self::Error> {
let query_string = req.query_string();
let result: Result<T, _> = serde_json::from_str::<serde_json::Value>(&format!("{{{}}}", query_string))
.and_then(|v| serde_json::from_value(v));
result
.map(|data| {
let validated = T::validate_data(data)?;
Ok(ValidatedQuery(validated))
})
.map_err(|_| actix_web::error::ErrorBadRequest("Invalid query parameters"))
}
}
pub trait Validate: Sized {
fn validate(self) -> Result<Self, Vec<ValidationError>>;
fn validate_data(data: serde_json::Value) -> Result<Self, Vec<ValidationError>>
where
Self: for<'de> serde::Deserialize<'de>,
{
serde_json::from_value(data).map_err(|e| {
vec![ValidationError::new("", &e.to_string())]
})
}
}
pub fn validate_form<T: DeserializeOwned + Validate>(
data: serde_json::Value,
) -> Result<T, Vec<ValidationError>> {
let parsed = serde_json::from_value::<T>(data)
.map_err(|e| vec![ValidationError::new("", &e.to_string())])?;
parsed.validate()
}
pub fn validation_error(field: &str, message: &str) -> ValidationError {
ValidationError::new(field, message)
}
pub fn combine_errors<A, B>(a: Result<A, Vec<ValidationError>>, b: Result<B, Vec<ValidationError>>) -> Result<(), Vec<ValidationError>> {
match (a, b) {
(Ok(_), Ok(_)) => Ok(()),
(Ok(_), Err(e)) => Err(e),
(Err(e), Ok(_)) => Err(e),
(Err(mut e1), Err(e2)) => {
e1.extend(e2);
Err(e1)
}
}
}