1use crate::{BootError, BootRequest, Result};
2use serde::de::DeserializeOwned;
3use std::sync::Arc;
4
5pub 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}