Skip to content

Commit a244af4

Browse files
authored
Merge pull request async-graphql#1588 from edbo/uuid_validator
UUID validator
2 parents dcab817 + ac5c03e commit a244af4

File tree

6 files changed

+101
-0
lines changed

6 files changed

+101
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ graphiql = ["handlebars"]
3232
altair = ["handlebars", "schemars"]
3333
playground = []
3434
raw_value = ["async-graphql-value/raw_value"]
35+
uuid-validator = ["uuid"]
3536

3637
[[bench]]
3738
harness = false

derive/src/validators.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,22 @@ impl ToTokens for Number {
2828
}
2929
}
3030

31+
#[derive(Clone, Debug, PartialEq)]
32+
pub enum UuidVersionValidation {
33+
None,
34+
Value(Lit),
35+
}
36+
37+
impl FromMeta for UuidVersionValidation {
38+
fn from_word() -> darling::Result<Self> {
39+
Ok(UuidVersionValidation::None)
40+
}
41+
42+
fn from_value(value: &Lit) -> darling::Result<Self> {
43+
Ok(UuidVersionValidation::Value(value.clone()))
44+
}
45+
}
46+
3147
#[derive(FromMeta, Default, Clone)]
3248
pub struct Validators {
3349
#[darling(default)]
@@ -58,6 +74,8 @@ pub struct Validators {
5874
ip: bool,
5975
#[darling(default)]
6076
regex: Option<String>,
77+
#[darling(default)]
78+
uuid: Option<UuidVersionValidation>,
6179
#[darling(default, multiple)]
6280
custom: Vec<Expr>,
6381
#[darling(default)]
@@ -159,6 +177,21 @@ impl Validators {
159177
});
160178
}
161179

180+
if let Some(version_validation) = &self.uuid {
181+
match version_validation {
182+
UuidVersionValidation::None => {
183+
elem_validators.push(quote! {
184+
#crate_name::validators::uuid(__raw_value, None)
185+
});
186+
}
187+
UuidVersionValidation::Value(version) => {
188+
elem_validators.push(quote! {
189+
#crate_name::validators::uuid(__raw_value, Some(#version))
190+
});
191+
}
192+
}
193+
}
194+
162195
if !list_validators.is_empty() {
163196
codes.push(quote! {
164197
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(#value) {

docs/en/src/input_value_validators.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- **url** is valid url.
1616
- **ip** is valid ip address.
1717
- **regex=RE** is match for the regex.
18+
- **uuid=V** the string or ID is a valid UUID with version `V`. You may omit `V` to accept any UUID version.
1819

1920
```rust
2021
# extern crate async_graphql;

src/types/id.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
1818
#[serde(transparent)]
1919
pub struct ID(pub String);
2020

21+
impl AsRef<str> for ID {
22+
fn as_ref(&self) -> &str {
23+
self.0.as_str()
24+
}
25+
}
26+
2127
impl Deref for ID {
2228
type Target = String;
2329

src/validators/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ mod minimum;
1414
mod multiple_of;
1515
mod regex;
1616
mod url;
17+
#[cfg(feature = "uuid-validator")]
18+
mod uuid;
1719

1820
pub use chars_max_length::chars_max_length;
1921
pub use chars_min_length::chars_min_length;
@@ -29,6 +31,8 @@ pub use min_length::min_length;
2931
pub use min_password_strength::min_password_strength;
3032
pub use minimum::minimum;
3133
pub use multiple_of::multiple_of;
34+
#[cfg(feature = "uuid-validator")]
35+
pub use uuid::uuid;
3236

3337
pub use self::{regex::regex, url::url};
3438
use crate::{InputType, InputValueError};

src/validators/uuid.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use uuid::Uuid;
2+
3+
use crate::{InputType, InputValueError};
4+
5+
pub fn uuid<T: AsRef<str> + InputType>(
6+
value: &T,
7+
version_option: Option<usize>,
8+
) -> Result<(), InputValueError<T>> {
9+
match Uuid::try_parse(value.as_ref()) {
10+
Ok(uuid) => {
11+
if let Some(version) = version_option {
12+
if uuid.get_version_num() != version {
13+
return Err(InputValueError::custom("UUID version mismatch"));
14+
}
15+
}
16+
Ok(())
17+
}
18+
Err(_) => Err(InputValueError::custom("Invalid UUID")),
19+
}
20+
}
21+
22+
#[cfg(test)]
23+
mod tests {
24+
use super::*;
25+
26+
#[test]
27+
fn test_uuid() {
28+
assert!(uuid(&"94c59486-c302-4f43-abd7-a9c980ddab36".to_string(), None).is_ok());
29+
assert!(
30+
uuid(&"94c59486-c302-4f43-abd7-a9c980ddab3".to_string(), None).is_err_and(|e| {
31+
let message = format!("{:?}", e);
32+
println!("{}", message);
33+
message.contains("Invalid UUID")
34+
})
35+
);
36+
}
37+
38+
#[test]
39+
fn test_uuid_version() {
40+
assert!(uuid(&"94c59486-c302-4f43-abd7-a9c980ddab36".to_string(), Some(4)).is_ok());
41+
assert!(
42+
uuid(&"94c59486-c302-4f43-abd7-a9c980ddab3".to_string(), Some(4)).is_err_and(|e| {
43+
let message = format!("{:?}", e);
44+
println!("{}", message);
45+
message.contains("Invalid UUID")
46+
})
47+
);
48+
assert!(
49+
uuid(&"94c59486-c302-5f43-abd7-a9c980ddab36".to_string(), Some(4)).is_err_and(|e| {
50+
let message = format!("{:?}", e);
51+
println!("{}", message);
52+
message.contains("UUID version mismatch")
53+
})
54+
);
55+
}
56+
}

0 commit comments

Comments
 (0)