diff --git a/schemars/tests/ui/invalid_validation_attrs.rs b/schemars/tests/ui/invalid_validation_attrs.rs index be843625..79a47352 100644 --- a/schemars/tests/ui/invalid_validation_attrs.rs +++ b/schemars/tests/ui/invalid_validation_attrs.rs @@ -1,10 +1,14 @@ use schemars::JsonSchema; +// FIXME validation attrs like `email` should be disallowed non structs/enums/variants + #[derive(JsonSchema)] -pub struct Struct1(#[validate(regex = 0, foo, length(min = 1, equal = 2, bar))] String); +#[validate(email)] +pub struct Struct1(#[validate(regex, foo, length(min = 1, equal = 2, bar))] String); #[derive(JsonSchema)] -pub struct Struct2(#[schemars(regex = 0, foo, length(min = 1, equal = 2, bar))] String); +#[schemars(email)] +pub struct Struct2(#[schemars(regex, foo, length(min = 1, equal = 2, bar))] String); #[derive(JsonSchema)] pub struct Struct3( diff --git a/schemars/tests/ui/invalid_validation_attrs.stderr b/schemars/tests/ui/invalid_validation_attrs.stderr index 933fd66f..56d4404a 100644 --- a/schemars/tests/ui/invalid_validation_attrs.stderr +++ b/schemars/tests/ui/invalid_validation_attrs.stderr @@ -1,59 +1,53 @@ -error: expected validate regex attribute to be a string: `regex = "..."` - --> $DIR/invalid_validation_attrs.rs:4:39 - | -4 | pub struct Struct1(#[validate(regex = 0, foo, length(min = 1, equal = 2, bar))] String); - | ^ - error: unknown schemars attribute `foo` - --> $DIR/invalid_validation_attrs.rs:7:42 - | -7 | pub struct Struct2(#[schemars(regex = 0, foo, length(min = 1, equal = 2, bar))] String); - | ^^^ + --> tests/ui/invalid_validation_attrs.rs:11:38 + | +11 | pub struct Struct2(#[schemars(regex, foo, length(min = 1, equal = 2, bar))] String); + | ^^^ -error: expected schemars regex attribute to be a string: `regex = "..."` - --> $DIR/invalid_validation_attrs.rs:7:39 - | -7 | pub struct Struct2(#[schemars(regex = 0, foo, length(min = 1, equal = 2, bar))] String); - | ^ +error: could not parse `regex` item in schemars attribute + --> tests/ui/invalid_validation_attrs.rs:11:31 + | +11 | pub struct Struct2(#[schemars(regex, foo, length(min = 1, equal = 2, bar))] String); + | ^^^^^ error: schemars attribute cannot contain both `equal` and `min` - --> $DIR/invalid_validation_attrs.rs:7:63 - | -7 | pub struct Struct2(#[schemars(regex = 0, foo, length(min = 1, equal = 2, bar))] String); - | ^^^^^ + --> tests/ui/invalid_validation_attrs.rs:11:59 + | +11 | pub struct Struct2(#[schemars(regex, foo, length(min = 1, equal = 2, bar))] String); + | ^^^^^ error: unknown item in schemars length attribute - --> $DIR/invalid_validation_attrs.rs:7:74 - | -7 | pub struct Struct2(#[schemars(regex = 0, foo, length(min = 1, equal = 2, bar))] String); - | ^^^ + --> tests/ui/invalid_validation_attrs.rs:11:70 + | +11 | pub struct Struct2(#[schemars(regex, foo, length(min = 1, equal = 2, bar))] String); + | ^^^ error: schemars attribute cannot contain both `contains` and `regex` - --> $DIR/invalid_validation_attrs.rs:26:9 + --> tests/ui/invalid_validation_attrs.rs:30:9 | -26 | contains = "bar", +30 | contains = "bar", | ^^^^^^^^ error: duplicate schemars attribute `regex` - --> $DIR/invalid_validation_attrs.rs:27:9 + --> tests/ui/invalid_validation_attrs.rs:31:9 | -27 | regex(path = "baz"), +31 | regex(path = "baz"), | ^^^^^ error: schemars attribute cannot contain both `phone` and `email` - --> $DIR/invalid_validation_attrs.rs:29:9 + --> tests/ui/invalid_validation_attrs.rs:33:9 | -29 | email, +33 | email, | ^^^^^ error: schemars attribute cannot contain both `phone` and `url` - --> $DIR/invalid_validation_attrs.rs:30:9 + --> tests/ui/invalid_validation_attrs.rs:34:9 | -30 | url +34 | url | ^^^ error[E0425]: cannot find value `foo` in this scope - --> $DIR/invalid_validation_attrs.rs:12:17 + --> tests/ui/invalid_validation_attrs.rs:16:17 | -12 | regex = "foo", +16 | regex = "foo", | ^^^^^ not found in this scope diff --git a/schemars/tests/validate.rs b/schemars/tests/validate.rs index 560fd573..0d0eefb7 100644 --- a/schemars/tests/validate.rs +++ b/schemars/tests/validate.rs @@ -16,7 +16,7 @@ pub struct Struct { min_max: f32, #[validate(range(min = "MIN", max = "MAX"))] min_max2: f32, - #[validate(regex = "STARTS_WITH_HELLO")] + #[validate(regex = &*STARTS_WITH_HELLO)] regex_str1: String, #[validate(regex(path = "STARTS_WITH_HELLO", code = "foo"))] regex_str2: String, diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs index e99e4f26..8e7caabd 100644 --- a/schemars_derive/src/attr/mod.rs +++ b/schemars_derive/src/attr/mod.rs @@ -190,12 +190,6 @@ impl Attrs { _ if ignore_errors => {} - Meta::List(m) if m.path.is_ident("inner") && attr_type == "schemars" => { - // This will be processed with the validation attributes. - // It's allowed only for the schemars attribute because the - // validator crate doesn't support it yet. - } - _ => { if !is_known_serde_or_validation_keyword(&meta_item) { let path = meta_item diff --git a/schemars_derive/src/attr/validation.rs b/schemars_derive/src/attr/validation.rs index 095590a1..0d9acf9c 100644 --- a/schemars_derive/src/attr/validation.rs +++ b/schemars_derive/src/attr/validation.rs @@ -1,13 +1,14 @@ -use super::{expr_as_lit_str, get_meta_items, parse_lit_into_path, parse_lit_str}; +use super::{expr_as_lit_str, get_meta_items, parse_lit_str}; use proc_macro2::TokenStream; use quote::ToTokens; use serde_derive_internals::Ctxt; use syn::{ - parse::Parser, punctuated::Punctuated, Expr, ExprPath, Lit, Meta, MetaList, MetaNameValue, Path, + parse::Parser, punctuated::Punctuated, Expr, ExprLit, ExprPath, Lit, Meta, MetaList, + MetaNameValue, Path, }; pub(crate) static VALIDATION_KEYWORDS: &[&str] = &[ - "range", "regex", "contains", "email", "phone", "url", "length", "required", + "range", "regex", "contains", "email", "phone", "url", "length", "required", "inner", ]; #[derive(Debug, Clone, Copy, PartialEq)] @@ -214,8 +215,7 @@ impl ValidationAttrs { (Some(_), _) => duplicate_error(&nv.path), (None, Some(_)) => mutual_exclusive_error(&nv.path, "contains"), (None, None) => { - self.regex = - parse_lit_into_expr_path(errors, attr_type, "regex", &nv.value).ok() + self.regex = parse_regex_expr(errors, nv.value); } } } @@ -230,10 +230,7 @@ impl ValidationAttrs { Meta::NameValue(MetaNameValue { path, value, .. }) if path.is_ident("path") => { - self.regex = parse_lit_into_expr_path( - errors, attr_type, "path", &value, - ) - .ok() + self.regex = parse_regex_expr(errors, value); } Meta::NameValue(MetaNameValue { path, value, .. }) if path.is_ident("pattern") => @@ -242,7 +239,7 @@ impl ValidationAttrs { expr_as_lit_str(errors, attr_type, "pattern", &value) .ok() .map(|litstr| { - Expr::Lit(syn::ExprLit { + Expr::Lit(ExprLit { attrs: Vec::new(), lit: Lit::Str(litstr.clone()), }) @@ -316,7 +313,18 @@ impl ValidationAttrs { } }, - _ => {} + _ if ignore_errors => {} + + _ => { + if let Some(ident) = meta_item.path().get_ident() { + if VALIDATION_KEYWORDS.iter().any(|k| ident == k) { + errors.error_spanned_by( + &meta_item, + format!("could not parse `{ident}` item in schemars attribute"), + ); + } + } + } } } self @@ -405,21 +413,6 @@ impl ValidationAttrs { } } -fn parse_lit_into_expr_path( - cx: &Ctxt, - attr_type: &'static str, - meta_item_name: &'static str, - lit: &Expr, -) -> Result { - parse_lit_into_path(cx, attr_type, meta_item_name, lit).map(|path| { - Expr::Path(ExprPath { - attrs: Vec::new(), - qself: None, - path, - }) - }) -} - fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, expr: Expr) -> Option { // this odd double-parsing is to make `-10` parsed as an Lit instead of an Expr::Unary let lit: Lit = match syn::parse2(expr.to_token_stream()) { @@ -445,3 +438,13 @@ fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, expr: Expr) -> Option Option { + match value { + Expr::Lit(ExprLit { + lit: Lit::Str(litstr), + .. + }) => parse_lit_str(&litstr).map_err(|e| cx.syn_error(e)).ok(), + v => Some(v), + } +}