feat(documentation): add polymorphic generate_schema macro (#1183)

Co-authored-by: pixincreate@work <69745008+pixincreate@users.noreply.github.com>
This commit is contained in:
Narayan Bhat
2023-05-19 16:37:54 +05:30
committed by GitHub
parent bd0069e2a8
commit 53aa5ac92d
14 changed files with 2568 additions and 1218 deletions

View File

@ -321,7 +321,9 @@ impl From<StraightThroughAlgorithm> for StraightThroughAlgorithmSerde {
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PrimaryBusinessDetails {
#[schema(value_type = CountryAlpha2)]
pub country: api_enums::CountryAlpha2,
#[schema(example = "food")]
pub business: String,
}
@ -452,10 +454,12 @@ pub struct MerchantConnectorCreate {
pub frm_configs: Option<FrmConfigs>,
/// Business Country of the connector
#[schema(value_type = CountryAlpha2, example = "US")]
#[cfg(feature = "multiple_mca")]
#[schema(value_type = CountryAlpha2, example = "US")]
pub business_country: api_enums::CountryAlpha2,
#[cfg(not(feature = "multiple_mca"))]
#[schema(value_type = Option<CountryAlpha2>, example = "US")]
pub business_country: Option<api_enums::CountryAlpha2>,
///Business Type of the merchant
@ -633,7 +637,11 @@ pub struct FrmConfigs {
pub frm_enabled_pms: Option<Vec<String>>,
pub frm_enabled_pm_types: Option<Vec<String>>,
pub frm_enabled_gateways: Option<Vec<String>>,
pub frm_action: api_enums::FrmAction, //What should be the action if FRM declines the txn (autorefund/cancel txn/manual review)
/// What should be the action if FRM declines the txn (autorefund/cancel txn/manual review)
#[schema(value_type = FrmAction)]
pub frm_action: api_enums::FrmAction,
/// Whether to make a call to the FRM before or after the payment
#[schema(value_type = FrmPreferredFlowTypes)]
pub frm_preferred_flow_type: api_enums::FrmPreferredFlowTypes,
}
/// Details of all the payment methods enabled for the connector for the given merchant account
@ -657,7 +665,9 @@ pub struct PaymentMethodsEnabled {
rename_all = "snake_case"
)]
pub enum AcceptedCurrencies {
#[schema(value_type = Vec<Currency>)]
EnableOnly(Vec<api_enums::Currency>),
#[schema(value_type = Vec<Currency>)]
DisableOnly(Vec<api_enums::Currency>),
AllAccepted,
}
@ -670,7 +680,9 @@ pub enum AcceptedCurrencies {
rename_all = "snake_case"
)]
pub enum AcceptedCountries {
#[schema(value_type = Vec<CountryAlpha2>)]
EnableOnly(Vec<api_enums::CountryAlpha2>),
#[schema(value_type = Vec<CountryAlpha2>)]
DisableOnly(Vec<api_enums::CountryAlpha2>),
AllAccepted,
}

View File

@ -42,7 +42,16 @@ pub struct BankCodeResponse {
pub eligible_connectors: Vec<String>,
}
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[derive(
Default,
Debug,
serde::Deserialize,
serde::Serialize,
Clone,
ToSchema,
router_derive::PolymorphicSchema,
)]
#[generate_schemas(PaymentsCreateRequest)]
#[serde(deny_unknown_fields)]
pub struct PaymentsRequest {
/// Unique identifier for the payment. This ensures idempotency for multiple payments
@ -64,6 +73,8 @@ pub struct PaymentsRequest {
/// The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,
#[schema(value_type = Option<u64>, example = 6540)]
#[serde(default, deserialize_with = "amount::deserialize_option")]
#[mandatory_in(PaymentsCreateRequest)]
// Makes the field mandatory in PaymentsCreateRequest
pub amount: Option<Amount>,
#[schema(value_type = Option<RoutingAlgorithm>, example = json!({
@ -78,6 +89,7 @@ pub struct PaymentsRequest {
/// The currency of the payment request can be specified here
#[schema(value_type = Option<Currency>, example = "USD")]
#[mandatory_in(PaymentsCreateRequest)]
pub currency: Option<api_enums::Currency>,
/// This is the instruction for capture/ debit the money from the users' card. On the other hand authorization refers to blocking the amount on the users' payment method.
@ -205,7 +217,7 @@ pub struct PaymentsRequest {
pub payment_method_type: Option<api_enums::PaymentMethodType>,
/// Business country of the merchant for this payment
#[schema(value_type = CountryAlpha2, example = "US")]
#[schema(value_type = Option<CountryAlpha2>, example = "US")]
pub business_country: Option<api_enums::CountryAlpha2>,
/// Business label of the merchant for this payment
@ -213,6 +225,7 @@ pub struct PaymentsRequest {
pub business_label: Option<String>,
/// Merchant connector details used to make payments.
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
/// Allowed Payment Method Types for a given PaymentIntent
@ -637,7 +650,13 @@ pub enum BankRedirectData {
/// The billing details for bank redirection
billing_details: BankRedirectBilling,
/// Bank account details for Giropay
#[schema(value_type = Option<String>)]
/// Bank account bic code
bank_account_bic: Option<Secret<String>>,
/// Bank account iban
#[schema(value_type = Option<String>)]
bank_account_iban: Option<Secret<String>>,
},
Ideal {
@ -653,26 +672,32 @@ pub enum BankRedirectData {
#[schema(value_type = CountryAlpha2, example = "US")]
country: api_enums::CountryAlpha2,
#[schema(value_type = String, example = "john.doe@example.com")]
email: Email,
},
OnlineBankingCzechRepublic {
// Issuer banks
#[schema(value_type = BankNames)]
issuer: api_enums::BankNames,
},
OnlineBankingFinland {
// Shopper Email
#[schema(value_type = Option<String>)]
email: Option<Email>,
},
OnlineBankingPoland {
// Issuer banks
#[schema(value_type = BankNames)]
issuer: api_enums::BankNames,
},
OnlineBankingSlovakia {
// Issuer value corresponds to the bank
#[schema(value_type = BankNames)]
issuer: api_enums::BankNames,
},
Przelewy24 {
//Issuer banks
#[schema(value_type = Option<BankNames>)]
bank_name: Option<api_enums::BankNames>,
// The billing details for bank redirect
@ -796,6 +821,7 @@ pub struct MobilePayRedirection {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct MbWayRedirection {
/// Telephone number of the shopper. Should be Portuguese phone number.
#[schema(value_type = String)]
pub telephone_number: Secret<String>,
}
@ -1008,6 +1034,7 @@ pub struct PaymentsCaptureRequest {
/// Concatenated with the statement descriptor suffix thats set on the account to form the complete statement descriptor.
pub statement_descriptor_prefix: Option<String>,
/// Merchant connector details used to make payments.
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}
@ -1231,6 +1258,7 @@ pub struct PaymentsResponse {
pub connector_label: Option<String>,
/// The business country of merchant for this payment
#[schema(value_type = CountryAlpha2)]
pub business_country: api_enums::CountryAlpha2,
/// The business label of merchant for this payment
@ -1487,6 +1515,7 @@ pub struct PaymentsRetrieveRequest {
/// The name of the connector
pub connector: Option<String>,
/// Merchant connector details used to make payments.
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}
@ -1510,7 +1539,9 @@ pub struct Metadata {
#[schema(value_type = Object, example = r#"{ "city": "NY", "unit": "245" }"#)]
#[serde(flatten)]
pub data: pii::SecretSerdeValue,
/// Payload coming in request as a metadata field
#[schema(value_type = Option<Object>)]
pub payload: Option<pii::SecretSerdeValue>,
/// Allowed payment method types for a payment intent
@ -1528,6 +1559,7 @@ pub struct PaymentsSessionRequest {
#[schema(value_type = Vec<PaymentMethodType>)]
pub wallets: Vec<api_enums::PaymentMethodType>,
/// Merchant connector details used to make payments.
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}
@ -1796,6 +1828,7 @@ pub struct PaymentsCancelRequest {
/// The reason for the payment cancel
pub cancellation_reason: Option<String>,
/// Merchant connector details used to make payments.
#[schema(value_type = MerchantConnectorDetailsWrap)]
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}

View File

@ -45,6 +45,7 @@ pub struct RefundRequest {
pub metadata: Option<pii::SecretSerdeValue>,
/// Merchant connector details used to make payments.
#[schema(value_type = Option<MerchantConnectorDetailsWrap>)]
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}

View File

@ -205,6 +205,7 @@ pub struct CurrencyCountryFlowFilter {
pub country: Option<HashSet<api_models::enums::CountryAlpha2>>,
pub not_available_flows: Option<NotAvailableFlows>,
}
#[derive(Debug, Deserialize, Copy, Clone, Default)]
#[serde(default)]
pub struct NotAvailableFlows {

View File

@ -29,7 +29,7 @@ Use the following base URLs when making requests to the APIs:
| Environment | Base URL |
|---------------|------------------------------------|
| Sandbox | <https://sandbox.hyperswitch.io> |
| Production | Coming Soon! |
| Production | <https://api.hyperswitch.io> |
## Authentication
@ -38,10 +38,10 @@ account, you are given a secret key (also referred as api-key) and a publishable
You may authenticate all API requests with Hyperswitch server by providing the appropriate key in
the request Authorization header.
| Key | Description |
|---------------|-----------------------------------------------------------------------------------------------|
| Sandbox | Private key. Used to authenticate all API requests from your merchant server |
| Production | Unique identifier for your account. Used to authenticate API requests from your app's client |
| Key | Description |
|-----------------|-----------------------------------------------------------------------------------------------|
| api-key | Private key. Used to authenticate all API requests from your merchant server |
| publishable key | Unique identifier for your account. Used to authenticate API requests from your app's client |
Never share your secret api keys. Keep them guarded and secure.
"#,
@ -65,15 +65,16 @@ Never share your secret api keys. Keep them guarded and secure.
crate::routes::refunds::refunds_retrieve,
crate::routes::refunds::refunds_update,
crate::routes::refunds::refunds_list,
crate::routes::admin::merchant_account_create,
crate::routes::admin::retrieve_merchant_account,
crate::routes::admin::update_merchant_account,
crate::routes::admin::delete_merchant_account,
crate::routes::admin::payment_connector_create,
crate::routes::admin::payment_connector_retrieve,
crate::routes::admin::payment_connector_list,
crate::routes::admin::payment_connector_update,
crate::routes::admin::payment_connector_delete,
// Commenting this out as these are admin apis and not to be used by the merchant
// crate::routes::admin::merchant_account_create,
// crate::routes::admin::retrieve_merchant_account,
// crate::routes::admin::update_merchant_account,
// crate::routes::admin::delete_merchant_account,
// crate::routes::admin::payment_connector_create,
// crate::routes::admin::payment_connector_retrieve,
// crate::routes::admin::payment_connector_list,
// crate::routes::admin::payment_connector_update,
// crate::routes::admin::payment_connector_delete,
crate::routes::mandates::get_mandate,
crate::routes::mandates::revoke_mandate,
crate::routes::payments::payments_create,
@ -114,6 +115,7 @@ Never share your secret api keys. Keep them guarded and secure.
crate::types::api::admin::MerchantAccountUpdate,
crate::types::api::admin::MerchantAccountDeleteResponse,
crate::types::api::admin::MerchantConnectorDeleteResponse,
crate::types::api::admin::MerchantConnectorResponse,
crate::types::api::customers::CustomerRequest,
crate::types::api::customers::CustomerDeleteResponse,
crate::types::api::payment_methods::PaymentMethodCreate,
@ -148,10 +150,25 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::enums::DisputeStage,
api_models::enums::DisputeStatus,
api_models::enums::CountryAlpha2,
api_models::enums::FrmAction,
api_models::enums::FrmPreferredFlowTypes,
api_models::admin::MerchantConnectorCreate,
api_models::admin::MerchantConnectorUpdate,
api_models::admin::PrimaryBusinessDetails,
api_models::admin::FrmConfigs,
api_models::admin::PaymentMethodsEnabled,
api_models::admin::MerchantConnectorDetailsWrap,
api_models::admin::MerchantConnectorDetails,
api_models::disputes::DisputeResponse,
api_models::disputes::DisputeResponsePaymentsRetrieve,
api_models::payments::AddressDetails,
api_models::payments::BankDebitData,
api_models::payments::AliPayRedirection,
api_models::payments::MbWayRedirection,
api_models::payments::MobilePayRedirection,
api_models::payments::WeChatPayRedirection,
api_models::payments::BankDebitBilling,
api_models::payments::CryptoData,
api_models::payments::Address,
api_models::payments::BankRedirectData,
api_models::payments::BankRedirectBilling,
@ -172,6 +189,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::Card,
api_models::payments::CustomerAcceptance,
api_models::payments::PaymentsRequest,
api_models::payments::PaymentsCreateRequest,
api_models::payments::PaymentsResponse,
api_models::payments::PaymentsStartRequest,
api_models::payments::PaymentRetrieveBody,

View File

@ -158,9 +158,9 @@ pub async fn delete_merchant_account(
#[utoipa::path(
post,
path = "/accounts/{account_id}/connectors",
request_body = MerchantConnector,
request_body = MerchantConnectorCreate,
responses(
(status = 200, description = "Merchant Connector Created", body = MerchantConnector),
(status = 200, description = "Merchant Connector Created", body = MerchantConnectorResponse),
(status = 400, description = "Missing Mandatory fields"),
),
tag = "Merchant Connector Account",
@ -198,7 +198,7 @@ pub async fn payment_connector_create(
("connector_id" = i32, Path, description = "The unique identifier for the Merchant Connector")
),
responses(
(status = 200, description = "Merchant Connector retrieved successfully", body = MerchantConnector),
(status = 200, description = "Merchant Connector retrieved successfully", body = MerchantConnectorResponse),
(status = 404, description = "Merchant Connector does not exist in records"),
(status = 401, description = "Unauthorized request")
),
@ -242,7 +242,7 @@ pub async fn payment_connector_retrieve(
("account_id" = String, Path, description = "The unique identifier for the merchant account"),
),
responses(
(status = 200, description = "Merchant Connector list retrieved successfully", body = Vec<MerchantConnector>),
(status = 200, description = "Merchant Connector list retrieved successfully", body = Vec<MerchantConnectorResponse>),
(status = 404, description = "Merchant Connector does not exist in records"),
(status = 401, description = "Unauthorized request")
),
@ -275,13 +275,13 @@ pub async fn payment_connector_list(
#[utoipa::path(
post,
path = "/accounts/{account_id}/connectors/{connector_id}",
request_body = MerchantConnector,
request_body = MerchantConnectorUpdate,
params(
("account_id" = String, Path, description = "The unique identifier for the merchant account"),
("connector_id" = i32, Path, description = "The unique identifier for the Merchant Connector")
),
responses(
(status = 200, description = "Merchant Connector Updated", body = MerchantConnector),
(status = 200, description = "Merchant Connector Updated", body = MerchantConnectorResponse),
(status = 404, description = "Merchant Connector does not exist in records"),
(status = 401, description = "Unauthorized request")
),

View File

@ -11,7 +11,7 @@ use crate::{
types::api::disputes as dispute_types,
};
/// Diputes - Retrieve Dispute
/// Disputes - Retrieve Dispute
#[utoipa::path(
get,
path = "/disputes/{dispute_id}",
@ -47,7 +47,7 @@ pub async fn retrieve_dispute(
.await
}
/// Diputes - List Disputes
/// Disputes - List Disputes
#[utoipa::path(
get,
path = "/disputes/list",
@ -90,7 +90,7 @@ pub async fn retrieve_disputes_list(
.await
}
/// Diputes - Accept Dispute
/// Disputes - Accept Dispute
#[utoipa::path(
get,
path = "/disputes/accept/{dispute_id}",
@ -126,7 +126,7 @@ pub async fn accept_dispute(
.await
}
/// Diputes - Submit Dispute Evidence
/// Disputes - Submit Dispute Evidence
#[utoipa::path(
post,
path = "/disputes/evidence",

View File

@ -48,7 +48,7 @@ pub async fn create_payment_method_api(
/// To filter and list the applicable payment methods for a particular Merchant ID
#[utoipa::path(
get,
path = "/payment_methods/{account_id}",
path = "/account/payment_methods",
params (
("account_id" = String, Path, description = "The unique identifier for the merchant account"),
("accepted_country" = Vec<String>, Query, description = "The two-letter ISO currency code"),
@ -96,7 +96,7 @@ pub async fn list_payment_method_api(
/// To filter and list the applicable payment methods for a particular Customer ID
#[utoipa::path(
get,
path = "/payment_methods/{customer_id}",
path = "/customer/{customer_id}/payment_methods",
params (
("customer_id" = String, Path, description = "The unique identifier for the customer account"),
("accepted_country" = Vec<String>, Query, description = "The two-letter ISO currency code"),

View File

@ -18,7 +18,7 @@ use crate::{
#[utoipa::path(
post,
path = "/payments",
request_body=PaymentsRequest,
request_body=PaymentsCreateRequest,
responses(
(status = 200, description = "Payment created", body = PaymentsResponse),
(status = 400, description = "Missing Mandatory fields")

View File

@ -1,9 +1,9 @@
pub use api_models::admin::{
MerchantAccountCreate, MerchantAccountDeleteResponse, MerchantAccountResponse,
MerchantAccountUpdate, MerchantConnectorCreate, MerchantConnectorDeleteResponse,
MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId, MerchantDetails,
MerchantId, PaymentMethodsEnabled, RoutingAlgorithm, StraightThroughAlgorithm, ToggleKVRequest,
ToggleKVResponse, WebhookDetails,
MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId,
MerchantConnectorResponse, MerchantDetails, MerchantId, PaymentMethodsEnabled,
RoutingAlgorithm, StraightThroughAlgorithm, ToggleKVRequest, ToggleKVResponse, WebhookDetails,
};
use common_utils::ext_traits::ValueExt;

View File

@ -462,3 +462,50 @@ pub fn operation_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
let input = syn::parse_macro_input!(input as syn::DeriveInput);
macros::operation_derive_inner(input).unwrap_or_else(|err| err.to_compile_error().into())
}
/// Generates different schemas with the ability to mark few fields as mandatory for certain schema
/// Usage
/// ```
/// #[derive(PolymorphicSchema)]
/// #[generate_schemas(PaymentsCreateRequest, PaymentsConfirmRequest)]
/// struct PaymentsRequest {
/// #[mandatory_in(PaymentsCreateRequest)]
/// amount: Option<u64>,
/// #[mandatory_in(PaymentsCreateRequest)]
/// currency: Option<String>,
/// payment_method: String,
/// }
/// ```
///
/// This will create two structs `PaymentsCreateRequest` and `PaymentsConfirmRequest` as follows
/// It will retain all the other attributes that are used in the original struct, and only consume
/// the #[mandatory_in] attribute to generate schemas
///
/// ```
/// #[derive(utoipa::ToSchema)]
/// struct PaymentsCreateRequest {
/// #[schema(required = true)]
/// amount: Option<u64>,
///
/// #[schema(required = true)]
/// currency: Option<String>,
///
/// payment_method: String,
/// }
///
/// #[derive(utoipa::ToSchema)]
/// struct PaymentsConfirmRequest {
/// amount: Option<u64>,
/// currency: Option<String>,
/// payment_method: String,
/// }
/// ```
#[proc_macro_derive(PolymorphicSchema, attributes(mandatory_in, generate_schemas))]
pub fn polymorphic_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
macros::polymorphic_macro_derive_inner(input)
.unwrap_or_else(|error| error.into_compile_error())
.into()
}

View File

@ -1,8 +1,10 @@
pub(crate) mod api_error;
pub(crate) mod diesel;
mod helpers;
pub(crate) mod generate_schema;
pub(crate) mod operation;
mod helpers;
use proc_macro2::TokenStream;
use quote::quote;
use syn::DeriveInput;
@ -12,6 +14,7 @@ pub(crate) use self::{
diesel::{
diesel_enum_attribute_inner, diesel_enum_derive_inner, diesel_enum_text_derive_inner,
},
generate_schema::polymorphic_macro_derive_inner,
operation::operation_derive_inner,
};

View File

@ -0,0 +1,140 @@
use std::collections::{HashMap, HashSet};
use syn::{self, parse_quote, punctuated::Punctuated, Token};
use crate::macros::helpers;
/// Parse schemas from attribute
/// Example
///
/// #[mandatory_in(PaymentsCreateRequest, PaymentsUpdateRequest)]
/// would return
///
/// [PaymentsCreateRequest, PaymentsUpdateRequest]
fn get_inner_path_ident(attribute: &syn::Attribute) -> syn::Result<Vec<syn::Ident>> {
Ok(attribute
.parse_args_with(Punctuated::<syn::Ident, Token![,]>::parse_terminated)?
.into_iter()
.collect::<Vec<_>>())
}
fn get_struct_fields(data: syn::Data) -> syn::Result<Punctuated<syn::Field, syn::token::Comma>> {
if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
..
}) = data
{
Ok(named.to_owned())
} else {
Err(syn::Error::new(
proc_macro2::Span::call_site(),
"This macro cannot be used on structs with no fields",
))
}
}
pub fn polymorphic_macro_derive_inner(
input: syn::DeriveInput,
) -> syn::Result<proc_macro2::TokenStream> {
let schemas_to_create =
helpers::get_metadata_inner::<syn::Ident>("generate_schemas", &input.attrs)?;
let fields = get_struct_fields(input.data)
.map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?;
// Go through all the fields and create a mapping of required fields for a schema
// PaymentsCreate -> ["amount","currency"]
// This will be stored in a hashset
// mandatory_hashset -> ((PaymentsCreate, amount), (PaymentsCreate,currency))
let mut mandatory_hashset = HashSet::<(syn::Ident, syn::Ident)>::new();
let mut other_fields_hm = HashMap::<syn::Field, Vec<syn::Attribute>>::new();
fields.iter().for_each(|field| {
// Partition the attributes of a field into two vectors
// One with #[mandatory_in] attributes present
// Rest of the attributes ( include only the schema attribute, serde is not required)
let (mandatory_attribute, other_attributes) = field
.attrs
.iter()
.partition::<Vec<_>, _>(|attribute| attribute.path.is_ident("mandatory_in"));
// Other attributes ( schema ) are to be printed as is
other_attributes
.iter()
.filter(|attribute| attribute.path.is_ident("schema") || attribute.path.is_ident("doc"))
.for_each(|attribute| {
// Since attributes will be modified, the field should not contain any attributes
// So create a field, with previous attributes removed
let mut field_without_attributes = field.clone();
field_without_attributes.attrs.clear();
other_fields_hm
.entry(field_without_attributes.to_owned())
.or_insert(vec![])
.push(attribute.to_owned().to_owned());
});
// Mandatory attributes are to be inserted into hashset
// The hashset will store it in this format
// (PaymentsCreateRequest, "amount")
// (PaymentsConfirmRequest, "currency")
//
// For these attributes, we need to later add #[schema(required = true)] attribute
_ = mandatory_attribute
.iter()
// Filter only #[mandatory_in] attributes
.map(|&attribute| get_inner_path_ident(attribute))
.try_for_each(|schemas| {
let res = schemas
.map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?
.iter()
.filter_map(|schema| field.ident.to_owned().zip(Some(schema.to_owned())))
.collect::<HashSet<_>>();
mandatory_hashset.extend(res);
Ok::<_, syn::Error>(())
});
});
let schemas = schemas_to_create
.iter()
.map(|schema| {
let fields = other_fields_hm
.iter()
.flat_map(|(field, value)| {
let mut attributes = value
.iter()
.map(|attribute| quote::quote!(#attribute))
.collect::<Vec<_>>();
// If the field is required for this schema, then add
// #[schema(required = true)] for this field
let required_attribute: syn::Attribute =
parse_quote!(#[schema(required = true)]);
// Can be none, because tuple fields have no ident
field.ident.to_owned().and_then(|field_ident| {
mandatory_hashset
.contains(&(field_ident, schema.to_owned()))
.then(|| attributes.push(quote::quote!(#required_attribute)))
});
quote::quote! {
#(#attributes)*
#field,
}
})
.collect::<Vec<_>>();
quote::quote! {
#[derive(utoipa::ToSchema)]
pub struct #schema {
#(#fields)*
}
}
})
.collect::<Vec<_>>();
Ok(quote::quote! {
#(#schemas)*
})
}

File diff suppressed because it is too large Load Diff