diff --git a/schemars/tests/ui/invalid_validation_attrs.rs b/schemars/tests/ui/invalid_validation_attrs.rs index 4914b70..fef0fe3 100644 --- a/schemars/tests/ui/invalid_validation_attrs.rs +++ b/schemars/tests/ui/invalid_validation_attrs.rs @@ -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 )] diff --git a/schemars/tests/ui/invalid_validation_attrs.stderr b/schemars/tests/ui/invalid_validation_attrs.stderr index 684343c..51e7510 100644 --- a/schemars/tests/ui/invalid_validation_attrs.stderr +++ b/schemars/tests/ui/invalid_validation_attrs.stderr @@ -82,10 +82,28 @@ error: `schemars(regex(...))` attribute requires `pattern = ...` 30 | regex(path = "baz"), | ^^^^^^^^^^^^^^^^^^^ -error: schemars attribute cannot contain both `url` and `email` - --> tests/ui/invalid_validation_attrs.rs:34:9 +error: unexpected value of schemars email attribute item + --> tests/ui/invalid_validation_attrs.rs:33:14 | -34 | url +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:37:9 + | +37 | url | ^^^ error: unknown schemars attribute `phone` diff --git a/schemars/tests/validate.rs b/schemars/tests/validate.rs index 1595e71..bd4f3a1 100644 --- a/schemars/tests/validate.rs +++ b/schemars/tests/validate.rs @@ -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, diff --git a/schemars_derive/src/attr/parse_meta.rs b/schemars_derive/src/attr/parse_meta.rs index f77b732..9259641 100644 --- a/schemars_derive/src/attr/parse_meta.rs +++ b/schemars_derive/src/attr/parse_meta.rs @@ -2,7 +2,7 @@ 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}; @@ -10,10 +10,27 @@ 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 diff --git a/schemars_derive/src/attr/validation.rs b/schemars_derive/src/attr/validation.rs index ca4270e..41031db 100644 --- a/schemars_derive/src/attr/validation.rs +++ b/schemars_derive/src/attr/validation.rs @@ -32,14 +32,12 @@ impl Format { } } - fn from_attr_str(s: &str) -> Self { - match s { + fn from_attr_str(s: &str) -> Option { + 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, + }) } } @@ -132,6 +130,10 @@ impl ValidationAttrs { } fn process_meta(&mut self, meta: Meta, meta_name: &str, cx: &AttrCtxt) -> Option { + 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), @@ -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); @@ -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) { @@ -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); } } }