Skip to main content

a3s_boot/
validation.rs

1use crate::{BootError, BootRequest, Result};
2use serde::de::DeserializeOwned;
3use std::sync::Arc;
4
5/// DTO validation hook used by validating route helpers and controller macros.
6pub trait Validate {
7    fn validate(&self) -> Result<()> {
8        Ok(())
9    }
10}
11
12pub(crate) type RequestValidator = Arc<dyn Fn(&BootRequest) -> Result<()> + Send + Sync>;
13
14pub(crate) fn body_validator<T>() -> RequestValidator
15where
16    T: DeserializeOwned + Validate + 'static,
17{
18    Arc::new(|request| {
19        request.require_json_content_type()?;
20        validate_value(request.json::<T>()?).map(|_| ())
21    })
22}
23
24pub(crate) fn params_validator<T>() -> RequestValidator
25where
26    T: DeserializeOwned + Validate + 'static,
27{
28    Arc::new(|request| validate_value(request.params::<T>()?).map(|_| ()))
29}
30
31pub(crate) fn query_validator<T>() -> RequestValidator
32where
33    T: DeserializeOwned + Validate + 'static,
34{
35    Arc::new(|request| validate_value(request.query::<T>()?).map(|_| ()))
36}
37
38pub(crate) fn validate_value<T>(value: T) -> Result<T>
39where
40    T: Validate,
41{
42    value
43        .validate()
44        .map_err(|error| validation_bad_request(error, std::any::type_name::<T>()))?;
45    Ok(value)
46}
47
48fn validation_bad_request(error: BootError, type_name: &'static str) -> BootError {
49    match error {
50        BootError::BadRequest(message) => {
51            BootError::BadRequest(format!("validation failed for {type_name}: {message}"))
52        }
53        error => error,
54    }
55}