From 9ec72202bbc3754c0aa26aa704edb206edce04d2 Mon Sep 17 00:00:00 2001 From: Roland Ma Date: Tue, 5 Jul 2022 18:34:07 +0800 Subject: [PATCH 1/4] support custorm properties Signed-off-by: Roland Ma --- core/src/v3/schema.rs | 4 ++-- macros/src/actix.rs | 47 ++++++++++++++++++++++++++++++++++++++++++- macros/src/core.rs | 5 +++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/core/src/v3/schema.rs b/core/src/v3/schema.rs index 149dc35fe..3cbd91ec1 100644 --- a/core/src/v3/schema.rs +++ b/core/src/v3/schema.rs @@ -1,5 +1,5 @@ use super::{invalid_referenceor, v2}; -use std::ops::Deref; +use std::{ops::Deref, iter::FromIterator}; impl From for openapiv3::ReferenceOr> { fn from(v2: v2::DefaultSchemaRaw) -> Self { @@ -30,7 +30,7 @@ impl From for openapiv3::ReferenceOr { description: v2.description, discriminator: None, default: None, - extensions: indexmap::IndexMap::new(), + extensions: indexmap::IndexMap::from_iter(v2.extensions.into_iter()), }, schema_kind: { if let Some(data_type) = v2.data_type { diff --git a/macros/src/actix.rs b/macros/src/actix.rs index 63f2a7994..9dfe93bde 100644 --- a/macros/src/actix.rs +++ b/macros/src/actix.rs @@ -756,6 +756,39 @@ fn extract_example(attrs: &[Attribute]) -> Option { None } +fn extract_extensions(attrs: &[Attribute]) -> proc_macro2::TokenStream { + let mut ext_attrs = HashMap::new(); + let attrs = extract_openapi_attrs(attrs); + for attr in attrs.flat_map(|attr| attr.into_iter()) { + if let NestedMeta::Meta(Meta::NameValue(nv)) = attr { + if let Some (id) = nv.path.get_ident() { + let s_id = id.to_string(); + if s_id.starts_with("x") { + ext_attrs.insert( id.clone(),nv.lit); + } + } + } + } + + if ext_attrs.is_empty() { + quote!{ std::collections::BTreeMap::new() } + } + else { + // ext_attrs + let mut items = quote!(); + for (k,v) in ext_attrs { + let lit_key = syn::LitStr::new(&k.to_string().replace("_","-"), k.span()); + items.extend(quote!((#lit_key.to_string(),serde_json::to_value(&#v).unwrap()),)); + } + + let gen = quote!{ + std::collections::BTreeMap::from([#items]); + }; + + gen + } +} + /// Actual parser and emitter for `api_v2_schema` macro. pub fn emit_v2_definition(input: TokenStream) -> TokenStream { let item_ast = match crate::expect_struct_or_enum(input) { @@ -779,6 +812,8 @@ pub fn emit_v2_definition(input: TokenStream) -> TokenStream { quote!(None) }; + let extensions = extract_extensions(&item_ast.attrs); + let props = SerdeProps::from_item_attrs(&item_ast.attrs); let name = &item_ast.ident; @@ -846,8 +881,10 @@ pub fn emit_v2_definition(input: TokenStream) -> TokenStream { let mut schema = DefaultSchemaRaw { name: Some(#schema_name.into()), example: #example, + extensions:#extensions, ..Default::default() }; + // schema.extensions.insert("Pulp Fiction".to_string(), serde_json::json!("hello world")); }; #[cfg(feature = "path-in-definition")] @@ -855,6 +892,7 @@ pub fn emit_v2_definition(input: TokenStream) -> TokenStream { let mut schema = DefaultSchemaRaw { name: Some(Self::__paperclip_schema_name()), // Add name for later use. example: #example, + extensions:#extensions, .. Default::default() }; }; @@ -1572,12 +1610,19 @@ fn handle_field_struct( quote!({}) }; + let extensions = extract_extensions(&field.attrs); + let gen = if !SerdeFlatten::exists(&field.attrs) { quote!({ let mut s = #ty_ref::raw_schema(); if !#docs.is_empty() { s.description = Some(#docs.to_string()); } + + s.extensions = #extensions; + + // s.extensions.insert("Pulp Fiction".to_string(), serde_json::json!("hello world")); + #example; schema.properties.insert(#field_name.into(), s.into()); @@ -1589,7 +1634,7 @@ fn handle_field_struct( quote!({ let s = #ty_ref::raw_schema(); schema.properties.extend(s.properties); - + s.extensions = #extensions; if #ty_ref::required() { schema.required.extend(s.required); } diff --git a/macros/src/core.rs b/macros/src/core.rs index a87152807..7edb243ec 100644 --- a/macros/src/core.rs +++ b/macros/src/core.rs @@ -320,6 +320,11 @@ fn schema_fields(name: &Ident, is_ref: bool) -> proc_macro2::TokenStream { pub required: std::collections::BTreeSet, )); + gen.extend(quote!( + #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty", default = "BTreeMap::default")] + pub extensions: std::collections::BTreeMap, + )); + if is_ref { gen.extend(quote!( #[serde(skip)] From 94a25f3b6ddc3c60897c24faa8c0b565bb3b0f7f Mon Sep 17 00:00:00 2001 From: Roland Ma Date: Wed, 6 Jul 2022 08:30:03 +0800 Subject: [PATCH 2/4] fix coding style Signed-off-by: Roland Ma --- core/src/v3/schema.rs | 2 +- macros/src/actix.rs | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/core/src/v3/schema.rs b/core/src/v3/schema.rs index 3cbd91ec1..5dbe6dee8 100644 --- a/core/src/v3/schema.rs +++ b/core/src/v3/schema.rs @@ -1,5 +1,5 @@ use super::{invalid_referenceor, v2}; -use std::{ops::Deref, iter::FromIterator}; +use std::{iter::FromIterator, ops::Deref}; impl From for openapiv3::ReferenceOr> { fn from(v2: v2::DefaultSchemaRaw) -> Self { diff --git a/macros/src/actix.rs b/macros/src/actix.rs index 9dfe93bde..87c489936 100644 --- a/macros/src/actix.rs +++ b/macros/src/actix.rs @@ -761,27 +761,26 @@ fn extract_extensions(attrs: &[Attribute]) -> proc_macro2::TokenStream { let attrs = extract_openapi_attrs(attrs); for attr in attrs.flat_map(|attr| attr.into_iter()) { if let NestedMeta::Meta(Meta::NameValue(nv)) = attr { - if let Some (id) = nv.path.get_ident() { + if let Some(id) = nv.path.get_ident() { let s_id = id.to_string(); - if s_id.starts_with("x") { - ext_attrs.insert( id.clone(),nv.lit); + if s_id.starts_with("x") { + ext_attrs.insert(id.clone(), nv.lit); } } } } if ext_attrs.is_empty() { - quote!{ std::collections::BTreeMap::new() } - } - else { + quote! { std::collections::BTreeMap::new() } + } else { // ext_attrs let mut items = quote!(); - for (k,v) in ext_attrs { - let lit_key = syn::LitStr::new(&k.to_string().replace("_","-"), k.span()); + for (k, v) in ext_attrs { + let lit_key = syn::LitStr::new(&k.to_string().replace("_", "-"), k.span()); items.extend(quote!((#lit_key.to_string(),serde_json::to_value(&#v).unwrap()),)); } - let gen = quote!{ + let gen = quote! { std::collections::BTreeMap::from([#items]); }; @@ -884,7 +883,6 @@ pub fn emit_v2_definition(input: TokenStream) -> TokenStream { extensions:#extensions, ..Default::default() }; - // schema.extensions.insert("Pulp Fiction".to_string(), serde_json::json!("hello world")); }; #[cfg(feature = "path-in-definition")] @@ -1621,8 +1619,6 @@ fn handle_field_struct( s.extensions = #extensions; - // s.extensions.insert("Pulp Fiction".to_string(), serde_json::json!("hello world")); - #example; schema.properties.insert(#field_name.into(), s.into()); From ab7e2b68fbadb33471a7a425216d3a86d9915371 Mon Sep 17 00:00:00 2001 From: Roland Ma Date: Thu, 7 Jul 2022 11:34:39 +0800 Subject: [PATCH 3/4] fix tailing semicolon Signed-off-by: Roland Ma --- macros/src/actix.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/macros/src/actix.rs b/macros/src/actix.rs index 87c489936..c8c9df520 100644 --- a/macros/src/actix.rs +++ b/macros/src/actix.rs @@ -763,7 +763,7 @@ fn extract_extensions(attrs: &[Attribute]) -> proc_macro2::TokenStream { if let NestedMeta::Meta(Meta::NameValue(nv)) = attr { if let Some(id) = nv.path.get_ident() { let s_id = id.to_string(); - if s_id.starts_with("x") { + if s_id.starts_with('x') { ext_attrs.insert(id.clone(), nv.lit); } } @@ -776,12 +776,12 @@ fn extract_extensions(attrs: &[Attribute]) -> proc_macro2::TokenStream { // ext_attrs let mut items = quote!(); for (k, v) in ext_attrs { - let lit_key = syn::LitStr::new(&k.to_string().replace("_", "-"), k.span()); + let lit_key = syn::LitStr::new(&k.to_string().replace('_', "-"), k.span()); items.extend(quote!((#lit_key.to_string(),serde_json::to_value(&#v).unwrap()),)); } let gen = quote! { - std::collections::BTreeMap::from([#items]); + std::collections::BTreeMap::from([#items]) }; gen From cad9b8d1ae2cd53254b7aa4a31e7434b1af7679e Mon Sep 17 00:00:00 2001 From: Roland Ma Date: Sat, 23 Jul 2022 18:06:50 +0900 Subject: [PATCH 4/4] fix ut Signed-off-by: Roland Ma --- macros/src/actix.rs | 2 +- macros/src/core.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/macros/src/actix.rs b/macros/src/actix.rs index c8c9df520..c3a346d0e 100644 --- a/macros/src/actix.rs +++ b/macros/src/actix.rs @@ -1628,7 +1628,7 @@ fn handle_field_struct( }) } else { quote!({ - let s = #ty_ref::raw_schema(); + let mut s = #ty_ref::raw_schema(); schema.properties.extend(s.properties); s.extensions = #extensions; if #ty_ref::required() { diff --git a/macros/src/core.rs b/macros/src/core.rs index 7edb243ec..90cd4d5a6 100644 --- a/macros/src/core.rs +++ b/macros/src/core.rs @@ -321,7 +321,7 @@ fn schema_fields(name: &Ident, is_ref: bool) -> proc_macro2::TokenStream { )); gen.extend(quote!( - #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty", default = "BTreeMap::default")] + #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty", default = "std::collections::BTreeMap::default")] pub extensions: std::collections::BTreeMap, ));