Skip to content

Commit

Permalink
Allow validate (but not schemars) attributes to have extra values…
Browse files Browse the repository at this point in the history
… where necessary
  • Loading branch information
GREsau committed Aug 28, 2024
1 parent a85f0fc commit 56cdd45
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 23 deletions.
3 changes: 3 additions & 0 deletions schemars/tests/ui/invalid_validation_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct Struct4(
regex(path = "baz"),
regex(pattern = "baz"),
phone,
email(code = "code_str", message = "message"),
email = "foo",
email,
email,
url
)]
Expand Down
22 changes: 20 additions & 2 deletions schemars/tests/ui/invalid_validation_attrs.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,28 @@ error: `schemars(regex(...))` attribute requires `pattern = ...`
30 | regex(path = "baz"),
| ^^^^^^^^^^^^^^^^^^^

error: unexpected value of schemars email attribute item
--> tests/ui/invalid_validation_attrs.rs:33:14
|
33 | email(code = "code_str", message = "message"),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: unexpected value of schemars email attribute item
--> tests/ui/invalid_validation_attrs.rs:34:15
|
34 | email = "foo",
| ^^^^^^^

error: duplicate schemars attribute item `email`
--> tests/ui/invalid_validation_attrs.rs:36:9
|
36 | email,
| ^^^^^

error: schemars attribute cannot contain both `url` and `email`
--> tests/ui/invalid_validation_attrs.rs:34:9
--> tests/ui/invalid_validation_attrs.rs:37:9
|
34 | url
37 | url
| ^^^

error: unknown schemars attribute `phone`
Expand Down
2 changes: 1 addition & 1 deletion schemars/tests/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct Struct {
contains_str2: String,
#[validate(email)]
email_address: String,
#[validate(url)]
#[validate(url(code = "code_str", message = "message"))]
homepage: String,
#[validate(length(min = 1, max = 100))]
non_empty_str: String,
Expand Down
25 changes: 21 additions & 4 deletions schemars_derive/src/attr/parse_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,35 @@ use proc_macro2::{TokenStream, TokenTree};
use syn::{
parse::{Parse, ParseStream, Parser},
punctuated::Punctuated,
Expr, ExprLit, Lit, LitStr, Meta, MetaNameValue,
Expr, ExprLit, Lit, LitStr, Meta, MetaList, MetaNameValue,
};

use super::{path_str, AttrCtxt};

pub fn require_path_only(meta: Meta, cx: &AttrCtxt) -> Result<(), ()> {
match meta {
Meta::Path(_) => Ok(()),
_ => {
let name = path_str(meta.path());
Meta::List(MetaList {
path, delimiter, ..
}) => {
let name = path_str(&path);
cx.syn_error(syn::Error::new(
delimiter.span().join(),
format_args!(
"unexpected value of {} {} attribute item",
cx.attr_type, name
),
));
Err(())
}
Meta::NameValue(MetaNameValue {
path,
eq_token,
value,
}) => {
let name = path_str(&path);
cx.error_spanned_by(
meta,
quote!(#eq_token #value),
format_args!(
"unexpected value of {} {} attribute item",
cx.attr_type, name
Expand Down
33 changes: 17 additions & 16 deletions schemars_derive/src/attr/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,12 @@ impl Format {
}
}

fn from_attr_str(s: &str) -> Self {
match s {
fn from_attr_str(s: &str) -> Option<Self> {
Some(match s {
"email" => Format::Email,
"url" => Format::Uri,
_ => {
panic!("Invalid format attr string `{s}`. This is a bug in schemars, please raise an issue!")
}
}
_ => return None,
})
}
}

Expand Down Expand Up @@ -132,6 +130,10 @@ impl ValidationAttrs {
}

fn process_meta(&mut self, meta: Meta, meta_name: &str, cx: &AttrCtxt) -> Option<Meta> {
if let Some(format) = Format::from_attr_str(meta_name) {
self.handle_format(meta, format, cx);
return None;
}
match meta_name {
"length" => match self.length {
Some(_) => cx.duplicate_error(&meta),
Expand All @@ -143,8 +145,6 @@ impl ValidationAttrs {
None => self.range = parse_length_or_range(meta, cx).ok(),
},

"email" | "url" => self.handle_format(meta, meta_name, cx),

"required" => {
if self.required {
cx.duplicate_error(&meta);
Expand All @@ -159,7 +159,7 @@ impl ValidationAttrs {
(None, None, "schemars") => self.regex = parse_schemars_regex(meta, cx).ok(),
(None, None, "validate") => self.regex = parse_validate_regex(meta, cx).ok(),
(None, None, wat) => {
panic!("Unexpected attr type `{wat}` for regex item. This is a bug in schemars, please raise an issue!")
unreachable!("Unexpected attr type `{wat}` for regex item. This is a bug in schemars, please raise an issue!")
}
},
"contains" => match (&self.regex, &self.contains) {
Expand All @@ -184,14 +184,15 @@ impl ValidationAttrs {
None
}

fn handle_format(&mut self, meta: Meta, meta_name: &str, cx: &AttrCtxt) {
match &self.format {
Some(f) if f.attr_str() == meta_name => cx.duplicate_error(&meta),
Some(f) => cx.mutual_exclusive_error(&meta, f.attr_str()),
fn handle_format(&mut self, meta: Meta, format: Format, cx: &AttrCtxt) {
match self.format {
Some(current) if current == format => cx.duplicate_error(&meta),
Some(current) => cx.mutual_exclusive_error(&meta, current.attr_str()),
None => {
// FIXME this is too strict - it may be a MetaList in validator attr (e.g. with message/code items)
if require_path_only(meta, cx).is_ok() {
self.format = Some(Format::from_attr_str(meta_name))
// Allow a MetaList in validator attr (e.g. with message/code items),
// but restrict it to path only in schemars attr.
if cx.attr_type == "validate" || require_path_only(meta, cx).is_ok() {
self.format = Some(format);
}
}
}
Expand Down

0 comments on commit 56cdd45

Please sign in to comment.