1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
//! Input value validators
mod int_validators;
mod list_validators;
mod string_validators;
use crate::{Error, Value};
pub use int_validators::{IntEqual, IntGreaterThan, IntLessThan, IntNonZero, IntRange};
pub use list_validators::{List, ListMaxLength, ListMinLength};
pub use string_validators::{Email, StringMaxLength, StringMinLength, MAC};
/// Input value validator
///
/// You can create your own input value validator by implementing this trait.
///
/// # Examples
///
/// ```no_run
/// use async_graphql::*;
/// use async_graphql::validators::{Email, MAC, IntRange};
///
/// struct QueryRoot;
///
/// #[Object]
/// impl QueryRoot {
/// // Input is email address
/// async fn value1(&self, #[graphql(validator(Email))] email: String) -> i32 {
/// unimplemented!()
/// }
///
/// // Input is email or MAC address
/// async fn value2(&self, #[graphql(validator(or(Email, MAC(colon = "false"))))] email_or_mac: String) -> i32 {
/// unimplemented!()
/// }
///
/// // Input is integer between 100 and 200
/// async fn value3(&self, #[graphql(validator(IntRange(min = "100", max = "200")))] value: i32) -> i32 {
/// unimplemented!()
/// }
/// }
/// ```
pub trait InputValueValidator
where
Self: Sync + Send,
{
/// Check value is valid, returns the reason for the error if it fails, otherwise None.
///
/// If the input type is different from the required type, return `Ok(())` directly, and other validators will find this error.
fn is_valid(&self, _value: &Value) -> Result<(), String> {
Ok(())
}
/// Check value is valid, returns the reason include extensions for the error if it fails, otherwise None.
///
/// If the input type is different from the required type, return `Ok(())` directly, and other validators will find this error.
///
/// # Examples:
///
/// ```no_run
/// use async_graphql::validators::InputValueValidator;
/// use async_graphql::{Value, Error, ErrorExtensions};
///
/// pub struct IntGreaterThanZero;
///
/// impl InputValueValidator for IntGreaterThanZero {
/// fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
/// if let Value::Number(n) = value {
/// if let Some(n) = n.as_i64() {
/// if n <= 0 {
/// return Err(
/// Error::new("Value must be greater than 0").extend_with(|_, e| e.set("code", 400))
/// );
/// }
/// }
/// }
/// Ok(())
/// }
/// }
/// ```
fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
// By default, use is_valid method to keep compatible with previous version
self.is_valid(value).map_err(Error::new)
}
}
/// An extension trait for `InputValueValidator`
pub trait InputValueValidatorExt: InputValueValidator + Sized {
/// Merge the two validators and return None only if both validators are successful.
fn and<R: InputValueValidator>(self, other: R) -> And<Self, R> {
And(self, other)
}
/// Merge two validators, and return None when either validator verifies successfully.
fn or<R: InputValueValidator>(self, other: R) -> Or<Self, R> {
Or(self, other)
}
/// Changes the error message
fn map_err<F: Fn(String) -> String>(self, f: F) -> MapErr<Self, F> {
MapErr(self, f)
}
/// Changes the error struct
fn map_err_ext<F: Fn(Error) -> Error>(self, f: F) -> MapErrExt<Self, F> {
MapErrExt(self, f)
}
}
impl<I: InputValueValidator> InputValueValidatorExt for I {}
/// Invalidator for `InputValueValidatorExt::and`
pub struct And<A, B>(A, B);
impl<A, B> InputValueValidator for And<A, B>
where
A: InputValueValidator,
B: InputValueValidator,
{
fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
// By default, use is_valid method to keep compatible with previous version
self.0.is_valid_with_extensions(value)?;
self.1.is_valid_with_extensions(value)
}
}
/// Invalidator for `InputValueValidator::or`
pub struct Or<A, B>(A, B);
impl<A, B> InputValueValidator for Or<A, B>
where
A: InputValueValidator,
B: InputValueValidator,
{
fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
// By default, use is_valid method to keep compatible with previous version
if self.0.is_valid_with_extensions(value).is_err() {
self.1.is_valid_with_extensions(value)
} else {
Ok(())
}
}
}
/// Invalidator for `InputValueValidator::map_err`
pub struct MapErr<I, F>(I, F);
impl<I, F> InputValueValidator for MapErr<I, F>
where
I: InputValueValidator,
F: Fn(String) -> String + Send + Sync,
{
fn is_valid(&self, value: &Value) -> Result<(), String> {
self.0.is_valid(value).map_err(&self.1)
}
}
/// Invalidator for `InputValueValidator::map_err_ext`
pub struct MapErrExt<I, F>(I, F);
impl<I, F> InputValueValidator for MapErrExt<I, F>
where
I: InputValueValidator,
F: Fn(Error) -> Error + Send + Sync,
{
fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
self.0.is_valid_with_extensions(value).map_err(&self.1)
}
}