Update to syn 2 and serde_derive_internals 0.29
This commit is contained in:
parent
e04e3a3a81
commit
9501fe319f
15 changed files with 206 additions and 235 deletions
|
@ -20,6 +20,7 @@ enum OrDef<A, B> {
|
||||||
|
|
||||||
struct Str<'a>(&'a str);
|
struct Str<'a>(&'a str);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema, Serialize)]
|
#[derive(JsonSchema, Serialize)]
|
||||||
#[serde(remote = "Str")]
|
#[serde(remote = "Str")]
|
||||||
struct StrDef<'a>(&'a str);
|
struct StrDef<'a>(&'a str);
|
||||||
|
|
|
@ -23,6 +23,7 @@ fn struct_normal() -> TestResult {
|
||||||
test_default_generated_schema::<Struct>("schema_with-struct")
|
test_default_generated_schema::<Struct>("schema_with-struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
pub struct Tuple(
|
pub struct Tuple(
|
||||||
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
|
||||||
|
|
|
@ -21,6 +21,7 @@ fn skip_struct_fields() -> TestResult {
|
||||||
test_default_generated_schema::<MyStruct>("skip_struct_fields")
|
test_default_generated_schema::<MyStruct>("skip_struct_fields")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
struct TupleStruct(
|
struct TupleStruct(
|
||||||
#[schemars(skip)] i32,
|
#[schemars(skip)] i32,
|
||||||
|
|
|
@ -18,6 +18,7 @@ fn struct_normal() -> TestResult {
|
||||||
test_default_generated_schema::<Struct>("struct-normal")
|
test_default_generated_schema::<Struct>("struct-normal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
pub struct Tuple(i32, bool, Option<&'static str>);
|
pub struct Tuple(i32, bool, Option<&'static str>);
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ fn struct_tuple() -> TestResult {
|
||||||
test_default_generated_schema::<Tuple>("struct-tuple")
|
test_default_generated_schema::<Tuple>("struct-tuple")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
pub struct Newtype(i32);
|
pub struct Newtype(i32);
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,12 @@ pub struct TransparentStruct {
|
||||||
inner: (),
|
inner: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
#[schemars(transparent)]
|
#[schemars(transparent)]
|
||||||
pub struct TransparentNewType(Option<InnerStruct>);
|
pub struct TransparentNewType(Option<InnerStruct>);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
pub struct InnerStruct(String, i32);
|
pub struct InnerStruct(String, i32);
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ fn validate_schemars_attrs() -> TestResult {
|
||||||
test_default_generated_schema::<Struct2>("validate_schemars_attrs")
|
test_default_generated_schema::<Struct2>("validate_schemars_attrs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
pub struct Tuple(
|
pub struct Tuple(
|
||||||
#[validate(range(max = 10))] u8,
|
#[validate(range(max = 10))] u8,
|
||||||
|
@ -115,6 +116,7 @@ fn validate_tuple() -> TestResult {
|
||||||
test_default_generated_schema::<Tuple>("validate_tuple")
|
test_default_generated_schema::<Tuple>("validate_tuple")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(JsonSchema)]
|
#[derive(JsonSchema)]
|
||||||
pub struct NewType(#[validate(range(max = 10))] u8);
|
pub struct NewType(#[validate(range(max = 10))] u8);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub struct Struct<'a> {
|
||||||
#[schemars(inner(length(min = 5, max = 100)))]
|
#[schemars(inner(length(min = 5, max = 100)))]
|
||||||
array_str_length: [&'a str; 2],
|
array_str_length: [&'a str; 2],
|
||||||
#[schemars(inner(contains(pattern = "substring...")))]
|
#[schemars(inner(contains(pattern = "substring...")))]
|
||||||
slice_str_contains: &'a[&'a str],
|
slice_str_contains: &'a [&'a str],
|
||||||
#[schemars(inner(regex = "STARTS_WITH_HELLO"))]
|
#[schemars(inner(regex = "STARTS_WITH_HELLO"))]
|
||||||
vec_str_regex: Vec<String>,
|
vec_str_regex: Vec<String>,
|
||||||
#[schemars(inner(length(min = 1, max = 100)))]
|
#[schemars(inner(length(min = 1, max = 100)))]
|
||||||
|
|
|
@ -17,8 +17,8 @@ proc-macro = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "1.0", features = ["extra-traits"] }
|
syn = { version = "2.0", features = ["extra-traits"] }
|
||||||
serde_derive_internals = "0.26.0"
|
serde_derive_internals = "0.29"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.2.1"
|
pretty_assertions = "1.2.1"
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub struct Field<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Container<'a> {
|
impl<'a> Container<'a> {
|
||||||
pub fn from_ast(item: &'a syn::DeriveInput) -> Result<Container<'a>, Vec<syn::Error>> {
|
pub fn from_ast(item: &'a syn::DeriveInput) -> syn::Result<Container<'a>> {
|
||||||
let ctxt = Ctxt::new();
|
let ctxt = Ctxt::new();
|
||||||
let result = serde_ast::Container::from_ast(&ctxt, item, Derive::Deserialize)
|
let result = serde_ast::Container::from_ast(&ctxt, item, Derive::Deserialize)
|
||||||
.ok_or(())
|
.ok_or(())
|
||||||
|
@ -48,7 +48,7 @@ impl<'a> Container<'a> {
|
||||||
.map(|_| result.expect("from_ast set no errors on Ctxt, so should have returned Ok"))
|
.map(|_| result.expect("from_ast set no errors on Ctxt, so should have returned Ok"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> &str {
|
||||||
self.serde_attrs.name().deserialize_name()
|
self.serde_attrs.name().deserialize_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ impl<'a> Container<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Variant<'a> {
|
impl<'a> Variant<'a> {
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> &str {
|
||||||
self.serde_attrs.name().deserialize_name()
|
self.serde_attrs.name().deserialize_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ impl<'a> Variant<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Field<'a> {
|
impl<'a> Field<'a> {
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> &str {
|
||||||
self.serde_attrs.name().deserialize_name()
|
self.serde_attrs.name().deserialize_name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use syn::{Attribute, Lit::Str, Meta::NameValue, MetaNameValue};
|
use syn::Attribute;
|
||||||
|
|
||||||
pub fn get_title_and_desc_from_doc(attrs: &[Attribute]) -> (Option<String>, Option<String>) {
|
pub fn get_title_and_desc_from_doc(attrs: &[Attribute]) -> (Option<String>, Option<String>) {
|
||||||
let doc = match get_doc(attrs) {
|
let doc = match get_doc(attrs) {
|
||||||
|
@ -35,13 +35,17 @@ fn get_doc(attrs: &[Attribute]) -> Option<String> {
|
||||||
let attrs = attrs
|
let attrs = attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|attr| {
|
.filter_map(|attr| {
|
||||||
if !attr.path.is_ident("doc") {
|
if !attr.path().is_ident("doc") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let meta = attr.parse_meta().ok()?;
|
let meta = attr.meta.require_name_value().ok()?;
|
||||||
if let NameValue(MetaNameValue { lit: Str(s), .. }) = meta {
|
if let syn::Expr::Lit(syn::ExprLit {
|
||||||
return Some(s.value());
|
lit: syn::Lit::Str(lit_str),
|
||||||
|
..
|
||||||
|
}) = &meta.value
|
||||||
|
{
|
||||||
|
return Some(lit_str.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
|
|
@ -10,9 +10,7 @@ use proc_macro2::{Group, Span, TokenStream, TokenTree};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use serde_derive_internals::Ctxt;
|
use serde_derive_internals::Ctxt;
|
||||||
use syn::parse::{self, Parse};
|
use syn::parse::{self, Parse};
|
||||||
use syn::Meta::{List, NameValue};
|
use syn::{Meta, MetaNameValue};
|
||||||
use syn::MetaNameValue;
|
|
||||||
use syn::NestedMeta::{Lit, Meta};
|
|
||||||
|
|
||||||
// FIXME using the same struct for containers+variants+fields means that
|
// FIXME using the same struct for containers+variants+fields means that
|
||||||
// with/schema_with are accepted (but ignored) on containers, and
|
// with/schema_with are accepted (but ignored) on containers, and
|
||||||
|
@ -42,10 +40,10 @@ impl Attrs {
|
||||||
.populate(attrs, "schemars", false, errors)
|
.populate(attrs, "schemars", false, errors)
|
||||||
.populate(attrs, "serde", true, errors);
|
.populate(attrs, "serde", true, errors);
|
||||||
|
|
||||||
result.deprecated = attrs.iter().any(|a| a.path.is_ident("deprecated"));
|
result.deprecated = attrs.iter().any(|a| a.path().is_ident("deprecated"));
|
||||||
result.repr = attrs
|
result.repr = attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|a| a.path.is_ident("repr"))
|
.find(|a| a.path().is_ident("repr"))
|
||||||
.and_then(|a| a.parse_args().ok());
|
.and_then(|a| a.parse_args().ok());
|
||||||
|
|
||||||
let (doc_title, doc_description) = doc::get_title_and_desc_from_doc(attrs);
|
let (doc_title, doc_description) = doc::get_title_and_desc_from_doc(attrs);
|
||||||
|
@ -105,8 +103,8 @@ impl Attrs {
|
||||||
|
|
||||||
for meta_item in get_meta_items(attrs, attr_type, errors, ignore_errors) {
|
for meta_item in get_meta_items(attrs, attr_type, errors, ignore_errors) {
|
||||||
match &meta_item {
|
match &meta_item {
|
||||||
Meta(NameValue(m)) if m.path.is_ident("with") => {
|
Meta::NameValue(m) if m.path.is_ident("with") => {
|
||||||
if let Ok(ty) = parse_lit_into_ty(errors, attr_type, "with", &m.lit) {
|
if let Ok(ty) = parse_lit_into_ty(errors, attr_type, "with", &m.value) {
|
||||||
match self.with {
|
match self.with {
|
||||||
Some(WithAttr::Type(_)) => duplicate_error(m),
|
Some(WithAttr::Type(_)) => duplicate_error(m),
|
||||||
Some(WithAttr::Function(_)) => mutual_exclusive_error(m, "schema_with"),
|
Some(WithAttr::Function(_)) => mutual_exclusive_error(m, "schema_with"),
|
||||||
|
@ -115,8 +113,9 @@ impl Attrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta(NameValue(m)) if m.path.is_ident("schema_with") => {
|
Meta::NameValue(m) if m.path.is_ident("schema_with") => {
|
||||||
if let Ok(fun) = parse_lit_into_path(errors, attr_type, "schema_with", &m.lit) {
|
if let Ok(fun) = parse_lit_into_path(errors, attr_type, "schema_with", &m.value)
|
||||||
|
{
|
||||||
match self.with {
|
match self.with {
|
||||||
Some(WithAttr::Function(_)) => duplicate_error(m),
|
Some(WithAttr::Function(_)) => duplicate_error(m),
|
||||||
Some(WithAttr::Type(_)) => mutual_exclusive_error(m, "with"),
|
Some(WithAttr::Type(_)) => mutual_exclusive_error(m, "with"),
|
||||||
|
@ -125,8 +124,8 @@ impl Attrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta(NameValue(m)) if m.path.is_ident("title") => {
|
Meta::NameValue(m) if m.path.is_ident("title") => {
|
||||||
if let Ok(title) = get_lit_str(errors, attr_type, "title", &m.lit) {
|
if let Ok(title) = expr_as_lit_str(errors, attr_type, "title", &m.value) {
|
||||||
match self.title {
|
match self.title {
|
||||||
Some(_) => duplicate_error(m),
|
Some(_) => duplicate_error(m),
|
||||||
None => self.title = Some(title.value()),
|
None => self.title = Some(title.value()),
|
||||||
|
@ -134,8 +133,10 @@ impl Attrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta(NameValue(m)) if m.path.is_ident("description") => {
|
Meta::NameValue(m) if m.path.is_ident("description") => {
|
||||||
if let Ok(description) = get_lit_str(errors, attr_type, "description", &m.lit) {
|
if let Ok(description) =
|
||||||
|
expr_as_lit_str(errors, attr_type, "description", &m.value)
|
||||||
|
{
|
||||||
match self.description {
|
match self.description {
|
||||||
Some(_) => duplicate_error(m),
|
Some(_) => duplicate_error(m),
|
||||||
None => self.description = Some(description.value()),
|
None => self.description = Some(description.value()),
|
||||||
|
@ -143,16 +144,16 @@ impl Attrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta(NameValue(m)) if m.path.is_ident("example") => {
|
Meta::NameValue(m) if m.path.is_ident("example") => {
|
||||||
if let Ok(fun) = parse_lit_into_path(errors, attr_type, "example", &m.lit) {
|
if let Ok(fun) = parse_lit_into_path(errors, attr_type, "example", &m.value) {
|
||||||
self.examples.push(fun)
|
self.examples.push(fun)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta(NameValue(m)) if m.path.is_ident("rename") => self.is_renamed = true,
|
Meta::NameValue(m) if m.path.is_ident("rename") => self.is_renamed = true,
|
||||||
|
|
||||||
Meta(NameValue(m)) if m.path.is_ident("crate") && attr_type == "schemars" => {
|
Meta::NameValue(m) if m.path.is_ident("crate") && attr_type == "schemars" => {
|
||||||
if let Ok(p) = parse_lit_into_path(errors, attr_type, "crate", &m.lit) {
|
if let Ok(p) = parse_lit_into_path(errors, attr_type, "crate", &m.value) {
|
||||||
if self.crate_name.is_some() {
|
if self.crate_name.is_some() {
|
||||||
duplicate_error(m)
|
duplicate_error(m)
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,14 +164,14 @@ impl Attrs {
|
||||||
|
|
||||||
_ if ignore_errors => {}
|
_ if ignore_errors => {}
|
||||||
|
|
||||||
Meta(List(m)) if m.path.is_ident("inner") && attr_type == "schemars" => {
|
Meta::List(m) if m.path.is_ident("inner") && attr_type == "schemars" => {
|
||||||
// This will be processed with the validation attributes.
|
// This will be processed with the validation attributes.
|
||||||
// It's allowed only for the schemars attribute because the
|
// It's allowed only for the schemars attribute because the
|
||||||
// validator crate doesn't support it yet.
|
// validator crate doesn't support it yet.
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta(meta_item) => {
|
_ => {
|
||||||
if !is_known_serde_or_validation_keyword(meta_item) {
|
if !is_known_serde_or_validation_keyword(&meta_item) {
|
||||||
let path = meta_item
|
let path = meta_item
|
||||||
.path()
|
.path()
|
||||||
.into_token_stream()
|
.into_token_stream()
|
||||||
|
@ -182,10 +183,6 @@ impl Attrs {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Lit(lit) => {
|
|
||||||
errors.error_spanned_by(lit, "unexpected literal in schemars attribute");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
|
@ -220,34 +217,35 @@ fn get_meta_items(
|
||||||
attr_type: &'static str,
|
attr_type: &'static str,
|
||||||
errors: &Ctxt,
|
errors: &Ctxt,
|
||||||
ignore_errors: bool,
|
ignore_errors: bool,
|
||||||
) -> Vec<syn::NestedMeta> {
|
) -> Vec<Meta> {
|
||||||
attrs.iter().fold(vec![], |mut acc, attr| {
|
let mut result = vec![];
|
||||||
if !attr.path.is_ident(attr_type) {
|
for attr in attrs.iter().filter(|a| a.path().is_ident(attr_type)) {
|
||||||
return acc;
|
match attr.parse_args_with(syn::punctuated::Punctuated::<Meta, Token![,]>::parse_terminated)
|
||||||
|
{
|
||||||
|
Ok(list) => result.extend(list),
|
||||||
|
Err(err) if !ignore_errors => errors.syn_error(err),
|
||||||
|
Err(_) => {}
|
||||||
}
|
}
|
||||||
match attr.parse_meta() {
|
|
||||||
Ok(List(meta)) => acc.extend(meta.nested),
|
|
||||||
Ok(other) if !ignore_errors => {
|
|
||||||
errors.error_spanned_by(other, format!("expected #[{}(...)]", attr_type))
|
|
||||||
}
|
}
|
||||||
Err(err) if !ignore_errors => errors.error_spanned_by(attr, err),
|
|
||||||
_ => (),
|
result
|
||||||
}
|
|
||||||
acc
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lit_str<'a>(
|
fn expr_as_lit_str<'a>(
|
||||||
cx: &Ctxt,
|
cx: &Ctxt,
|
||||||
attr_type: &'static str,
|
attr_type: &'static str,
|
||||||
meta_item_name: &'static str,
|
meta_item_name: &'static str,
|
||||||
lit: &'a syn::Lit,
|
expr: &'a syn::Expr,
|
||||||
) -> Result<&'a syn::LitStr, ()> {
|
) -> Result<&'a syn::LitStr, ()> {
|
||||||
if let syn::Lit::Str(lit) = lit {
|
if let syn::Expr::Lit(syn::ExprLit {
|
||||||
Ok(lit)
|
lit: syn::Lit::Str(lit_str),
|
||||||
|
..
|
||||||
|
}) = expr
|
||||||
|
{
|
||||||
|
Ok(lit_str)
|
||||||
} else {
|
} else {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
lit,
|
expr,
|
||||||
format!(
|
format!(
|
||||||
"expected {} {} attribute to be a string: `{} = \"...\"`",
|
"expected {} {} attribute to be a string: `{} = \"...\"`",
|
||||||
attr_type, meta_item_name, meta_item_name
|
attr_type, meta_item_name, meta_item_name
|
||||||
|
@ -261,9 +259,9 @@ fn parse_lit_into_ty(
|
||||||
cx: &Ctxt,
|
cx: &Ctxt,
|
||||||
attr_type: &'static str,
|
attr_type: &'static str,
|
||||||
meta_item_name: &'static str,
|
meta_item_name: &'static str,
|
||||||
lit: &syn::Lit,
|
lit: &syn::Expr,
|
||||||
) -> Result<syn::Type, ()> {
|
) -> Result<syn::Type, ()> {
|
||||||
let string = get_lit_str(cx, attr_type, meta_item_name, lit)?;
|
let string = expr_as_lit_str(cx, attr_type, meta_item_name, lit)?;
|
||||||
|
|
||||||
parse_lit_str(string).map_err(|_| {
|
parse_lit_str(string).map_err(|_| {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
|
@ -281,17 +279,17 @@ fn parse_lit_into_path(
|
||||||
cx: &Ctxt,
|
cx: &Ctxt,
|
||||||
attr_type: &'static str,
|
attr_type: &'static str,
|
||||||
meta_item_name: &'static str,
|
meta_item_name: &'static str,
|
||||||
lit: &syn::Lit,
|
expr: &syn::Expr,
|
||||||
) -> Result<syn::Path, ()> {
|
) -> Result<syn::Path, ()> {
|
||||||
let string = get_lit_str(cx, attr_type, meta_item_name, lit)?;
|
let lit_str = expr_as_lit_str(cx, attr_type, meta_item_name, expr)?;
|
||||||
|
|
||||||
parse_lit_str(string).map_err(|_| {
|
parse_lit_str(lit_str).map_err(|_| {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
lit,
|
expr,
|
||||||
format!(
|
format!(
|
||||||
"failed to parse path: `{} = {:?}`",
|
"failed to parse path: `{} = {:?}`",
|
||||||
meta_item_name,
|
meta_item_name,
|
||||||
string.value()
|
lit_str.value()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,9 @@ use quote::ToTokens;
|
||||||
use serde_derive_internals::Ctxt;
|
use serde_derive_internals::Ctxt;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use syn::parse::Parser;
|
use syn::parse::Parser;
|
||||||
use syn::{Attribute, Data, Field, Meta, NestedMeta, Variant};
|
use syn::{Attribute, Data, Field, Meta, Variant};
|
||||||
|
|
||||||
|
use super::get_meta_items;
|
||||||
|
|
||||||
// List of keywords that can appear in #[serde(...)]/#[schemars(...)] attributes which we want serde_derive_internals to parse for us.
|
// List of keywords that can appear in #[serde(...)]/#[schemars(...)] attributes which we want serde_derive_internals to parse for us.
|
||||||
pub(crate) static SERDE_KEYWORDS: &[&str] = &[
|
pub(crate) static SERDE_KEYWORDS: &[&str] = &[
|
||||||
|
@ -32,7 +34,7 @@ pub(crate) static SERDE_KEYWORDS: &[&str] = &[
|
||||||
|
|
||||||
// If a struct/variant/field has any #[schemars] attributes, then create copies of them
|
// If a struct/variant/field has any #[schemars] attributes, then create copies of them
|
||||||
// as #[serde] attributes so that serde_derive_internals will parse them for us.
|
// as #[serde] attributes so that serde_derive_internals will parse them for us.
|
||||||
pub fn process_serde_attrs(input: &mut syn::DeriveInput) -> Result<(), Vec<syn::Error>> {
|
pub fn process_serde_attrs(input: &mut syn::DeriveInput) -> syn::Result<()> {
|
||||||
let ctxt = Ctxt::new();
|
let ctxt = Ctxt::new();
|
||||||
process_attrs(&ctxt, &mut input.attrs);
|
process_attrs(&ctxt, &mut input.attrs);
|
||||||
match input.data {
|
match input.data {
|
||||||
|
@ -60,25 +62,19 @@ fn process_serde_field_attrs<'a>(ctxt: &Ctxt, fields: impl Iterator<Item = &'a m
|
||||||
fn process_attrs(ctxt: &Ctxt, attrs: &mut Vec<Attribute>) {
|
fn process_attrs(ctxt: &Ctxt, attrs: &mut Vec<Attribute>) {
|
||||||
// Remove #[serde(...)] attributes (some may be re-added later)
|
// Remove #[serde(...)] attributes (some may be re-added later)
|
||||||
let (serde_attrs, other_attrs): (Vec<_>, Vec<_>) =
|
let (serde_attrs, other_attrs): (Vec<_>, Vec<_>) =
|
||||||
attrs.drain(..).partition(|at| at.path.is_ident("serde"));
|
attrs.drain(..).partition(|at| at.path().is_ident("serde"));
|
||||||
*attrs = other_attrs;
|
*attrs = other_attrs;
|
||||||
|
|
||||||
let schemars_attrs: Vec<_> = attrs
|
|
||||||
.iter()
|
|
||||||
.filter(|at| at.path.is_ident("schemars"))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Copy appropriate #[schemars(...)] attributes to #[serde(...)] attributes
|
// Copy appropriate #[schemars(...)] attributes to #[serde(...)] attributes
|
||||||
let (mut serde_meta, mut schemars_meta_names): (Vec<_>, HashSet<_>) = schemars_attrs
|
let (mut serde_meta, mut schemars_meta_names): (Vec<_>, HashSet<_>) =
|
||||||
.iter()
|
get_meta_items(attrs, "schemars", ctxt, false)
|
||||||
.flat_map(|at| get_meta_items(ctxt, at))
|
.into_iter()
|
||||||
.flatten()
|
|
||||||
.filter_map(|meta| {
|
.filter_map(|meta| {
|
||||||
let keyword = get_meta_ident(ctxt, &meta).ok()?;
|
let keyword = get_meta_ident(&meta)?;
|
||||||
if keyword.ends_with("with") || !SERDE_KEYWORDS.contains(&keyword.as_ref()) {
|
if SERDE_KEYWORDS.contains(&keyword.as_ref()) && !keyword.ends_with("with") {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((meta, keyword))
|
Some((meta, keyword))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
|
@ -89,12 +85,8 @@ fn process_attrs(ctxt: &Ctxt, attrs: &mut Vec<Attribute>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-add #[serde(...)] attributes that weren't overridden by #[schemars(...)] attributes
|
// Re-add #[serde(...)] attributes that weren't overridden by #[schemars(...)] attributes
|
||||||
for meta in serde_attrs
|
for meta in get_meta_items(&serde_attrs, "serde", ctxt, false) {
|
||||||
.into_iter()
|
if let Some(i) = get_meta_ident(&meta) {
|
||||||
.flat_map(|at| get_meta_items(ctxt, &at))
|
|
||||||
.flatten()
|
|
||||||
{
|
|
||||||
if let Ok(i) = get_meta_ident(ctxt, &meta) {
|
|
||||||
if !schemars_meta_names.contains(&i)
|
if !schemars_meta_names.contains(&i)
|
||||||
&& SERDE_KEYWORDS.contains(&i.as_ref())
|
&& SERDE_KEYWORDS.contains(&i.as_ref())
|
||||||
&& i != "bound"
|
&& i != "bound"
|
||||||
|
@ -125,34 +117,8 @@ fn to_tokens(attrs: &[Attribute]) -> impl ToTokens {
|
||||||
tokens
|
tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_meta_items(ctxt: &Ctxt, attr: &Attribute) -> Result<Vec<NestedMeta>, ()> {
|
fn get_meta_ident(meta: &Meta) -> Option<String> {
|
||||||
match attr.parse_meta() {
|
meta.path().get_ident().map(|i| i.to_string())
|
||||||
Ok(Meta::List(meta)) => Ok(meta.nested.into_iter().collect()),
|
|
||||||
Ok(_) => {
|
|
||||||
ctxt.error_spanned_by(attr, "expected #[schemars(...)] or #[serde(...)]");
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
ctxt.error_spanned_by(attr, err);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_meta_ident(ctxt: &Ctxt, meta: &NestedMeta) -> Result<String, ()> {
|
|
||||||
match meta {
|
|
||||||
NestedMeta::Meta(m) => m.path().get_ident().map(|i| i.to_string()).ok_or(()),
|
|
||||||
NestedMeta::Lit(lit) => {
|
|
||||||
ctxt.error_spanned_by(
|
|
||||||
meta,
|
|
||||||
format!(
|
|
||||||
"unexpected literal in attribute: {}",
|
|
||||||
lit.into_token_stream()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -198,7 +164,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = process_serde_attrs(&mut input) {
|
if let Err(e) = process_serde_attrs(&mut input) {
|
||||||
panic!("process_serde_attrs returned error: {}", e[0])
|
panic!("process_serde_attrs returned error: {}", e)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(input, expected);
|
assert_eq!(input, expected);
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use super::{get_lit_str, get_meta_items, parse_lit_into_path, parse_lit_str};
|
use super::{expr_as_lit_str, get_meta_items, parse_lit_into_path, parse_lit_str};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::ToTokens;
|
||||||
use serde_derive_internals::Ctxt;
|
use serde_derive_internals::Ctxt;
|
||||||
use syn::{Expr, ExprLit, ExprPath, Lit, Meta, MetaNameValue, NestedMeta, Path};
|
use syn::{
|
||||||
|
parse::Parser, punctuated::Punctuated, Expr, ExprPath, Lit, Meta, MetaList, MetaNameValue, Path,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) static VALIDATION_KEYWORDS: &[&str] = &[
|
pub(crate) static VALIDATION_KEYWORDS: &[&str] = &[
|
||||||
"range", "regex", "contains", "email", "phone", "url", "length", "required",
|
"range", "regex", "contains", "email", "phone", "url", "length", "required",
|
||||||
|
@ -62,7 +65,7 @@ impl ValidationAttrs {
|
||||||
|
|
||||||
fn populate(
|
fn populate(
|
||||||
mut self,
|
mut self,
|
||||||
meta_items: Vec<syn::NestedMeta>,
|
meta_items: Vec<Meta>,
|
||||||
attr_type: &'static str,
|
attr_type: &'static str,
|
||||||
ignore_errors: bool,
|
ignore_errors: bool,
|
||||||
errors: &Ctxt,
|
errors: &Ctxt,
|
||||||
|
@ -100,31 +103,43 @@ impl ValidationAttrs {
|
||||||
errors.error_spanned_by(path, msg)
|
errors.error_spanned_by(path, msg)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let parse_nested_meta = |meta_list: MetaList| {
|
||||||
|
let parser = Punctuated::<syn::Meta, Token![,]>::parse_terminated;
|
||||||
|
match parser.parse2(meta_list.tokens) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
if !ignore_errors {
|
||||||
|
errors.syn_error(e);
|
||||||
|
}
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for meta_item in meta_items {
|
for meta_item in meta_items {
|
||||||
match &meta_item {
|
match meta_item {
|
||||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("length") => {
|
Meta::List(meta_list) if meta_list.path.is_ident("length") => {
|
||||||
for nested in meta_list.nested.iter() {
|
for nested in parse_nested_meta(meta_list) {
|
||||||
match nested {
|
match nested {
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("min") => {
|
Meta::NameValue(nv) if nv.path.is_ident("min") => {
|
||||||
if self.length_min.is_some() {
|
if self.length_min.is_some() {
|
||||||
duplicate_error(&nv.path)
|
duplicate_error(&nv.path)
|
||||||
} else if self.length_equal.is_some() {
|
} else if self.length_equal.is_some() {
|
||||||
mutual_exclusive_error(&nv.path, "equal")
|
mutual_exclusive_error(&nv.path, "equal")
|
||||||
} else {
|
} else {
|
||||||
self.length_min = str_or_num_to_expr(errors, "min", &nv.lit);
|
self.length_min = str_or_num_to_expr(errors, "min", nv.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("max") => {
|
Meta::NameValue(nv) if nv.path.is_ident("max") => {
|
||||||
if self.length_max.is_some() {
|
if self.length_max.is_some() {
|
||||||
duplicate_error(&nv.path)
|
duplicate_error(&nv.path)
|
||||||
} else if self.length_equal.is_some() {
|
} else if self.length_equal.is_some() {
|
||||||
mutual_exclusive_error(&nv.path, "equal")
|
mutual_exclusive_error(&nv.path, "equal")
|
||||||
} else {
|
} else {
|
||||||
self.length_max = str_or_num_to_expr(errors, "max", &nv.lit);
|
self.length_max = str_or_num_to_expr(errors, "max", nv.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("equal") => {
|
Meta::NameValue(nv) if nv.path.is_ident("equal") => {
|
||||||
if self.length_equal.is_some() {
|
if self.length_equal.is_some() {
|
||||||
duplicate_error(&nv.path)
|
duplicate_error(&nv.path)
|
||||||
} else if self.length_min.is_some() {
|
} else if self.length_min.is_some() {
|
||||||
|
@ -133,7 +148,7 @@ impl ValidationAttrs {
|
||||||
mutual_exclusive_error(&nv.path, "max")
|
mutual_exclusive_error(&nv.path, "max")
|
||||||
} else {
|
} else {
|
||||||
self.length_equal =
|
self.length_equal =
|
||||||
str_or_num_to_expr(errors, "equal", &nv.lit);
|
str_or_num_to_expr(errors, "equal", nv.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
meta => {
|
meta => {
|
||||||
|
@ -148,21 +163,21 @@ impl ValidationAttrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("range") => {
|
Meta::List(meta_list) if meta_list.path.is_ident("range") => {
|
||||||
for nested in meta_list.nested.iter() {
|
for nested in parse_nested_meta(meta_list) {
|
||||||
match nested {
|
match nested {
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("min") => {
|
Meta::NameValue(nv) if nv.path.is_ident("min") => {
|
||||||
if self.range_min.is_some() {
|
if self.range_min.is_some() {
|
||||||
duplicate_error(&nv.path)
|
duplicate_error(&nv.path)
|
||||||
} else {
|
} else {
|
||||||
self.range_min = str_or_num_to_expr(errors, "min", &nv.lit);
|
self.range_min = str_or_num_to_expr(errors, "min", nv.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("max") => {
|
Meta::NameValue(nv) if nv.path.is_ident("max") => {
|
||||||
if self.range_max.is_some() {
|
if self.range_max.is_some() {
|
||||||
duplicate_error(&nv.path)
|
duplicate_error(&nv.path)
|
||||||
} else {
|
} else {
|
||||||
self.range_max = str_or_num_to_expr(errors, "max", &nv.lit);
|
self.range_max = str_or_num_to_expr(errors, "max", nv.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
meta => {
|
meta => {
|
||||||
|
@ -177,64 +192,54 @@ impl ValidationAttrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::Path(m))
|
Meta::Path(m) if m.is_ident("required") || m.is_ident("required_nested") => {
|
||||||
if m.is_ident("required") || m.is_ident("required_nested") =>
|
|
||||||
{
|
|
||||||
self.required = true;
|
self.required = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::Path(p)) if p.is_ident(Format::Email.attr_str()) => {
|
Meta::Path(p) if p.is_ident(Format::Email.attr_str()) => match self.format {
|
||||||
match self.format {
|
Some(f) => duplicate_format_error(f, Format::Email, &p),
|
||||||
Some(f) => duplicate_format_error(f, Format::Email, p),
|
|
||||||
None => self.format = Some(Format::Email),
|
None => self.format = Some(Format::Email),
|
||||||
}
|
},
|
||||||
}
|
Meta::Path(p) if p.is_ident(Format::Uri.attr_str()) => match self.format {
|
||||||
NestedMeta::Meta(Meta::Path(p)) if p.is_ident(Format::Uri.attr_str()) => {
|
Some(f) => duplicate_format_error(f, Format::Uri, &p),
|
||||||
match self.format {
|
|
||||||
Some(f) => duplicate_format_error(f, Format::Uri, p),
|
|
||||||
None => self.format = Some(Format::Uri),
|
None => self.format = Some(Format::Uri),
|
||||||
}
|
},
|
||||||
}
|
Meta::Path(p) if p.is_ident(Format::Phone.attr_str()) => match self.format {
|
||||||
NestedMeta::Meta(Meta::Path(p)) if p.is_ident(Format::Phone.attr_str()) => {
|
Some(f) => duplicate_format_error(f, Format::Phone, &p),
|
||||||
match self.format {
|
|
||||||
Some(f) => duplicate_format_error(f, Format::Phone, p),
|
|
||||||
None => self.format = Some(Format::Phone),
|
None => self.format = Some(Format::Phone),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("regex") => {
|
Meta::NameValue(nv) if nv.path.is_ident("regex") => {
|
||||||
match (&self.regex, &self.contains) {
|
match (&self.regex, &self.contains) {
|
||||||
(Some(_), _) => duplicate_error(&nv.path),
|
(Some(_), _) => duplicate_error(&nv.path),
|
||||||
(None, Some(_)) => mutual_exclusive_error(&nv.path, "contains"),
|
(None, Some(_)) => mutual_exclusive_error(&nv.path, "contains"),
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
self.regex =
|
self.regex =
|
||||||
parse_lit_into_expr_path(errors, attr_type, "regex", &nv.lit).ok()
|
parse_lit_into_expr_path(errors, attr_type, "regex", &nv.value).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("regex") => {
|
Meta::List(meta_list) if meta_list.path.is_ident("regex") => {
|
||||||
match (&self.regex, &self.contains) {
|
match (&self.regex, &self.contains) {
|
||||||
(Some(_), _) => duplicate_error(&meta_list.path),
|
(Some(_), _) => duplicate_error(&meta_list.path),
|
||||||
(None, Some(_)) => mutual_exclusive_error(&meta_list.path, "contains"),
|
(None, Some(_)) => mutual_exclusive_error(&meta_list.path, "contains"),
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
for x in meta_list.nested.iter() {
|
for x in parse_nested_meta(meta_list) {
|
||||||
match x {
|
match x {
|
||||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
Meta::NameValue(MetaNameValue { path, value, .. })
|
||||||
path,
|
if path.is_ident("path") =>
|
||||||
lit,
|
{
|
||||||
..
|
self.regex = parse_lit_into_expr_path(
|
||||||
})) if path.is_ident("path") => {
|
errors, attr_type, "path", &value,
|
||||||
self.regex =
|
)
|
||||||
parse_lit_into_expr_path(errors, attr_type, "path", lit)
|
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
Meta::NameValue(MetaNameValue { path, value, .. })
|
||||||
path,
|
if path.is_ident("pattern") =>
|
||||||
lit,
|
{
|
||||||
..
|
self.regex =
|
||||||
})) if path.is_ident("pattern") => {
|
expr_as_lit_str(errors, attr_type, "pattern", &value)
|
||||||
self.regex = get_lit_str(errors, attr_type, "pattern", lit)
|
|
||||||
.ok()
|
.ok()
|
||||||
.map(|litstr| {
|
.map(|litstr| {
|
||||||
Expr::Lit(syn::ExprLit {
|
Expr::Lit(syn::ExprLit {
|
||||||
|
@ -258,34 +263,30 @@ impl ValidationAttrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. }))
|
Meta::NameValue(MetaNameValue { path, value, .. }) if path.is_ident("contains") => {
|
||||||
if path.is_ident("contains") =>
|
|
||||||
{
|
|
||||||
match (&self.contains, &self.regex) {
|
match (&self.contains, &self.regex) {
|
||||||
(Some(_), _) => duplicate_error(path),
|
(Some(_), _) => duplicate_error(&path),
|
||||||
(None, Some(_)) => mutual_exclusive_error(path, "regex"),
|
(None, Some(_)) => mutual_exclusive_error(&path, "regex"),
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
self.contains = get_lit_str(errors, attr_type, "contains", lit)
|
self.contains = expr_as_lit_str(errors, attr_type, "contains", &value)
|
||||||
.map(|litstr| litstr.value())
|
.map(|litstr| litstr.value())
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("contains") => {
|
Meta::List(meta_list) if meta_list.path.is_ident("contains") => {
|
||||||
match (&self.contains, &self.regex) {
|
match (&self.contains, &self.regex) {
|
||||||
(Some(_), _) => duplicate_error(&meta_list.path),
|
(Some(_), _) => duplicate_error(&meta_list.path),
|
||||||
(None, Some(_)) => mutual_exclusive_error(&meta_list.path, "regex"),
|
(None, Some(_)) => mutual_exclusive_error(&meta_list.path, "regex"),
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
for x in meta_list.nested.iter() {
|
for x in parse_nested_meta(meta_list) {
|
||||||
match x {
|
match x {
|
||||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
Meta::NameValue(MetaNameValue { path, value, .. })
|
||||||
path,
|
if path.is_ident("pattern") =>
|
||||||
lit,
|
{
|
||||||
..
|
|
||||||
})) if path.is_ident("pattern") => {
|
|
||||||
self.contains =
|
self.contains =
|
||||||
get_lit_str(errors, attr_type, "contains", lit)
|
expr_as_lit_str(errors, attr_type, "contains", &value)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|litstr| litstr.value())
|
.map(|litstr| litstr.value())
|
||||||
}
|
}
|
||||||
|
@ -304,20 +305,18 @@ impl ValidationAttrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("inner") => {
|
Meta::List(meta_list) if meta_list.path.is_ident("inner") => match self.inner {
|
||||||
match self.inner {
|
|
||||||
Some(_) => duplicate_error(&meta_list.path),
|
Some(_) => duplicate_error(&meta_list.path),
|
||||||
None => {
|
None => {
|
||||||
let inner_attrs = ValidationAttrs::default().populate(
|
let inner_attrs = ValidationAttrs::default().populate(
|
||||||
meta_list.nested.clone().into_iter().collect(),
|
parse_nested_meta(meta_list).into_iter().collect(),
|
||||||
attr_type,
|
attr_type,
|
||||||
ignore_errors,
|
ignore_errors,
|
||||||
errors,
|
errors,
|
||||||
);
|
);
|
||||||
self.inner = Some(Box::new(inner_attrs));
|
self.inner = Some(Box::new(inner_attrs));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -446,7 +445,7 @@ fn parse_lit_into_expr_path(
|
||||||
cx: &Ctxt,
|
cx: &Ctxt,
|
||||||
attr_type: &'static str,
|
attr_type: &'static str,
|
||||||
meta_item_name: &'static str,
|
meta_item_name: &'static str,
|
||||||
lit: &syn::Lit,
|
lit: &Expr,
|
||||||
) -> Result<Expr, ()> {
|
) -> Result<Expr, ()> {
|
||||||
parse_lit_into_path(cx, attr_type, meta_item_name, lit).map(|path| {
|
parse_lit_into_path(cx, attr_type, meta_item_name, lit).map(|path| {
|
||||||
Expr::Path(ExprPath {
|
Expr::Path(ExprPath {
|
||||||
|
@ -510,19 +509,24 @@ fn wrap_string_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, lit: &Lit) -> Option<Expr> {
|
fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, expr: Expr) -> Option<Expr> {
|
||||||
|
let lit: Lit = match syn::parse2(expr.to_token_stream()) {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(err) => {
|
||||||
|
cx.syn_error(err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match lit {
|
match lit {
|
||||||
Lit::Str(s) => parse_lit_str::<ExprPath>(s).ok().map(Expr::Path),
|
Lit::Str(s) => parse_lit_str::<ExprPath>(&s).ok().map(Expr::Path),
|
||||||
Lit::Int(_) | Lit::Float(_) => Some(Expr::Lit(ExprLit {
|
Lit::Int(_) | Lit::Float(_) => Some(expr),
|
||||||
attrs: Vec::new(),
|
|
||||||
lit: lit.clone(),
|
|
||||||
})),
|
|
||||||
_ => {
|
_ => {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
lit,
|
&expr,
|
||||||
format!(
|
format!(
|
||||||
"expected `{}` to be a string or number literal",
|
"expected `{}` to be a string or number literal, not {:?}",
|
||||||
meta_item_name
|
meta_item_name, &expr
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
|
|
|
@ -20,7 +20,7 @@ use syn::spanned::Spanned;
|
||||||
pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as syn::DeriveInput);
|
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||||
derive_json_schema(input, false)
|
derive_json_schema(input, false)
|
||||||
.unwrap_or_else(compile_error)
|
.unwrap_or_else(syn::Error::into_compile_error)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,14 +28,11 @@ pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro:
|
||||||
pub fn derive_json_schema_repr_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_json_schema_repr_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as syn::DeriveInput);
|
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||||
derive_json_schema(input, true)
|
derive_json_schema(input, true)
|
||||||
.unwrap_or_else(compile_error)
|
.unwrap_or_else(syn::Error::into_compile_error)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_json_schema(
|
fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<TokenStream> {
|
||||||
mut input: syn::DeriveInput,
|
|
||||||
repr: bool,
|
|
||||||
) -> Result<TokenStream, Vec<syn::Error>> {
|
|
||||||
attr::process_serde_attrs(&mut input)?;
|
attr::process_serde_attrs(&mut input)?;
|
||||||
|
|
||||||
let mut cont = Container::from_ast(&input)?;
|
let mut cont = Container::from_ast(&input)?;
|
||||||
|
@ -87,7 +84,7 @@ fn derive_json_schema(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut schema_base_name = cont.name();
|
let mut schema_base_name = cont.name().to_string();
|
||||||
|
|
||||||
if !cont.attrs.is_renamed {
|
if !cont.attrs.is_renamed {
|
||||||
if let Some(path) = cont.serde_attrs.remote() {
|
if let Some(path) = cont.serde_attrs.remote() {
|
||||||
|
@ -165,7 +162,7 @@ fn derive_json_schema(
|
||||||
};
|
};
|
||||||
|
|
||||||
let schema_expr = if repr {
|
let schema_expr = if repr {
|
||||||
schema_exprs::expr_for_repr(&cont).map_err(|e| vec![e])?
|
schema_exprs::expr_for_repr(&cont)?
|
||||||
} else {
|
} else {
|
||||||
schema_exprs::expr_for_container(&cont)
|
schema_exprs::expr_for_container(&cont)
|
||||||
};
|
};
|
||||||
|
@ -207,10 +204,3 @@ fn add_trait_bounds(cont: &mut Container) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_error(errors: Vec<syn::Error>) -> TokenStream {
|
|
||||||
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
|
|
||||||
quote! {
|
|
||||||
#(#compile_errors)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ fn expr_for_external_tagged_enum<'a>(
|
||||||
variants: impl Iterator<Item = &'a Variant<'a>>,
|
variants: impl Iterator<Item = &'a Variant<'a>>,
|
||||||
deny_unknown_fields: bool,
|
deny_unknown_fields: bool,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let mut unique_names = HashSet::<String>::new();
|
let mut unique_names = HashSet::<&str>::new();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = variants
|
let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = variants
|
||||||
.inspect(|v| {
|
.inspect(|v| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue