mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 17:47:54 +08:00
353 lines
12 KiB
Rust
353 lines
12 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use proc_macro2::{Span, TokenStream};
|
|
use quote::quote;
|
|
use syn::{self, spanned::Spanned, DeriveInput, Lit, Meta, MetaNameValue, NestedMeta};
|
|
|
|
use crate::macros::helpers;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum Derives {
|
|
Sync,
|
|
Cancel,
|
|
Capture,
|
|
Authorize,
|
|
Authorizedata,
|
|
Syncdata,
|
|
Canceldata,
|
|
Capturedata,
|
|
CompleteAuthorizeData,
|
|
VerifyData,
|
|
Start,
|
|
Verify,
|
|
Session,
|
|
SessionData,
|
|
}
|
|
|
|
impl From<String> for Derives {
|
|
fn from(s: String) -> Self {
|
|
match s.as_str() {
|
|
"sync" => Self::Sync,
|
|
"cancel" => Self::Cancel,
|
|
"syncdata" => Self::Syncdata,
|
|
"authorize" => Self::Authorize,
|
|
"authorizedata" => Self::Authorizedata,
|
|
"canceldata" => Self::Canceldata,
|
|
"capture" => Self::Capture,
|
|
"capturedata" => Self::Capturedata,
|
|
"completeauthorizedata" => Self::CompleteAuthorizeData,
|
|
"start" => Self::Start,
|
|
"verify" => Self::Verify,
|
|
"verifydata" => Self::VerifyData,
|
|
"session" => Self::Session,
|
|
"sessiondata" => Self::SessionData,
|
|
_ => Self::Authorize,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Derives {
|
|
fn to_operation(
|
|
self,
|
|
fns: impl Iterator<Item = TokenStream> + Clone,
|
|
struct_name: &syn::Ident,
|
|
) -> TokenStream {
|
|
let req_type = Conversion::get_req_type(self);
|
|
quote! {
|
|
#[automatically_derived]
|
|
impl<F:Send+Clone> Operation<F,#req_type> for #struct_name {
|
|
#(#fns)*
|
|
}
|
|
}
|
|
}
|
|
|
|
fn to_ref_operation(
|
|
self,
|
|
ref_fns: impl Iterator<Item = TokenStream> + Clone,
|
|
struct_name: &syn::Ident,
|
|
) -> TokenStream {
|
|
let req_type = Conversion::get_req_type(self);
|
|
quote! {
|
|
#[automatically_derived]
|
|
impl<F:Send+Clone> Operation<F,#req_type> for &#struct_name {
|
|
#(#ref_fns)*
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash)]
|
|
enum Conversion {
|
|
ValidateRequest,
|
|
GetTracker,
|
|
Domain,
|
|
UpdateTracker,
|
|
PostUpdateTracker,
|
|
All,
|
|
Invalid(String),
|
|
}
|
|
|
|
impl From<String> for Conversion {
|
|
fn from(s: String) -> Self {
|
|
match s.as_str() {
|
|
"validate_request" => Self::ValidateRequest,
|
|
"get_tracker" => Self::GetTracker,
|
|
"domain" => Self::Domain,
|
|
"update_tracker" => Self::UpdateTracker,
|
|
"post_tracker" => Self::PostUpdateTracker,
|
|
"all" => Self::All,
|
|
s => Self::Invalid(s.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Conversion {
|
|
fn get_req_type(ident: Derives) -> syn::Ident {
|
|
match ident {
|
|
Derives::Authorize => syn::Ident::new("PaymentsRequest", Span::call_site()),
|
|
Derives::Authorizedata => syn::Ident::new("PaymentsAuthorizeData", Span::call_site()),
|
|
Derives::Sync => syn::Ident::new("PaymentsRetrieveRequest", Span::call_site()),
|
|
Derives::Syncdata => syn::Ident::new("PaymentsSyncData", Span::call_site()),
|
|
Derives::Cancel => syn::Ident::new("PaymentsCancelRequest", Span::call_site()),
|
|
Derives::Canceldata => syn::Ident::new("PaymentsCancelData", Span::call_site()),
|
|
Derives::Capture => syn::Ident::new("PaymentsCaptureRequest", Span::call_site()),
|
|
Derives::Capturedata => syn::Ident::new("PaymentsCaptureData", Span::call_site()),
|
|
Derives::CompleteAuthorizeData => {
|
|
syn::Ident::new("CompleteAuthorizeData", Span::call_site())
|
|
}
|
|
Derives::Start => syn::Ident::new("PaymentsStartRequest", Span::call_site()),
|
|
Derives::Verify => syn::Ident::new("VerifyRequest", Span::call_site()),
|
|
Derives::VerifyData => syn::Ident::new("VerifyRequestData", Span::call_site()),
|
|
Derives::Session => syn::Ident::new("PaymentsSessionRequest", Span::call_site()),
|
|
Derives::SessionData => syn::Ident::new("PaymentsSessionData", Span::call_site()),
|
|
}
|
|
}
|
|
|
|
fn to_function(&self, ident: Derives) -> TokenStream {
|
|
let req_type = Self::get_req_type(ident);
|
|
match self {
|
|
Self::ValidateRequest => quote! {
|
|
fn to_validate_request(&self) -> RouterResult<&(dyn ValidateRequest<F,#req_type> + Send + Sync)> {
|
|
Ok(self)
|
|
}
|
|
},
|
|
Self::GetTracker => quote! {
|
|
fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<F,PaymentData<F>,#req_type> + Send + Sync)> {
|
|
Ok(self)
|
|
}
|
|
},
|
|
Self::Domain => quote! {
|
|
fn to_domain(&self) -> RouterResult<&dyn Domain<F,#req_type>> {
|
|
Ok(self)
|
|
}
|
|
},
|
|
Self::UpdateTracker => quote! {
|
|
fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<F,PaymentData<F>,#req_type> + Send + Sync)> {
|
|
Ok(self)
|
|
}
|
|
},
|
|
Self::PostUpdateTracker => quote! {
|
|
fn to_post_update_tracker(&self) -> RouterResult<&(dyn PostUpdateTracker<F, PaymentData<F>, #req_type> + Send + Sync)> {
|
|
Ok(self)
|
|
}
|
|
},
|
|
Self::Invalid(s) => {
|
|
helpers::syn_error(Span::call_site(), &format!("Invalid identifier {s}"))
|
|
.to_compile_error()
|
|
}
|
|
Self::All => {
|
|
let validate_request = Self::ValidateRequest.to_function(ident);
|
|
let get_tracker = Self::GetTracker.to_function(ident);
|
|
let domain = Self::Domain.to_function(ident);
|
|
let update_tracker = Self::UpdateTracker.to_function(ident);
|
|
|
|
quote! {
|
|
#validate_request
|
|
#get_tracker
|
|
#domain
|
|
#update_tracker
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn to_ref_function(&self, ident: Derives) -> TokenStream {
|
|
let req_type = Self::get_req_type(ident);
|
|
match self {
|
|
Self::ValidateRequest => quote! {
|
|
fn to_validate_request(&self) -> RouterResult<&(dyn ValidateRequest<F,#req_type> + Send + Sync)> {
|
|
Ok(*self)
|
|
}
|
|
},
|
|
Self::GetTracker => quote! {
|
|
fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<F,PaymentData<F>,#req_type> + Send + Sync)> {
|
|
Ok(*self)
|
|
}
|
|
},
|
|
Self::Domain => quote! {
|
|
fn to_domain(&self) -> RouterResult<&(dyn Domain<F,#req_type>)> {
|
|
Ok(*self)
|
|
}
|
|
},
|
|
Self::UpdateTracker => quote! {
|
|
fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<F,PaymentData<F>,#req_type> + Send + Sync)> {
|
|
Ok(*self)
|
|
}
|
|
},
|
|
Self::PostUpdateTracker => quote! {
|
|
fn to_post_update_tracker(&self) -> RouterResult<&(dyn PostUpdateTracker<F, PaymentData<F>, #req_type> + Send + Sync)> {
|
|
Ok(*self)
|
|
}
|
|
},
|
|
Self::Invalid(s) => {
|
|
helpers::syn_error(Span::call_site(), &format!("Invalid identifier {s}"))
|
|
.to_compile_error()
|
|
}
|
|
Self::All => {
|
|
let validate_request = Self::ValidateRequest.to_ref_function(ident);
|
|
let get_tracker = Self::GetTracker.to_ref_function(ident);
|
|
let domain = Self::Domain.to_ref_function(ident);
|
|
let update_tracker = Self::UpdateTracker.to_ref_function(ident);
|
|
|
|
quote! {
|
|
#validate_request
|
|
#get_tracker
|
|
#domain
|
|
#update_tracker
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn find_operation_attr(a: &[syn::Attribute]) -> syn::Result<syn::Attribute> {
|
|
a.iter()
|
|
.find(|a| {
|
|
a.path
|
|
.get_ident()
|
|
.map(|ident| *ident == "operation")
|
|
.unwrap_or(false)
|
|
})
|
|
.cloned()
|
|
.ok_or_else(|| {
|
|
helpers::syn_error(
|
|
Span::call_site(),
|
|
"Cannot find attribute 'operation' in the macro",
|
|
)
|
|
})
|
|
}
|
|
|
|
fn find_value(v: &NestedMeta) -> Option<(String, Vec<String>)> {
|
|
match v {
|
|
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
|
ref path,
|
|
eq_token: _,
|
|
lit: Lit::Str(ref litstr),
|
|
})) => {
|
|
let key = path.get_ident()?.to_string();
|
|
Some((
|
|
key,
|
|
litstr.value().split(',').map(ToString::to_string).collect(),
|
|
))
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn find_properties(attr: &syn::Attribute) -> syn::Result<HashMap<String, Vec<String>>> {
|
|
let meta = attr.parse_meta();
|
|
match meta {
|
|
Ok(syn::Meta::List(syn::MetaList {
|
|
ref path,
|
|
paren_token: _,
|
|
nested,
|
|
})) => {
|
|
path.get_ident().map(|i| i == "operation").ok_or_else(|| {
|
|
helpers::syn_error(path.span(), "Attribute 'operation' was not found")
|
|
})?;
|
|
Ok(HashMap::from_iter(nested.iter().filter_map(find_value)))
|
|
}
|
|
_ => Err(helpers::syn_error(
|
|
attr.span(),
|
|
"No attributes were found. Expected format is ops=..,flow=..",
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn operation_derive_inner(input: DeriveInput) -> syn::Result<proc_macro::TokenStream> {
|
|
let struct_name = &input.ident;
|
|
let op = find_operation_attr(&input.attrs)?;
|
|
let prop = find_properties(&op)?;
|
|
let ops = prop.get("ops").ok_or_else(|| {
|
|
helpers::syn_error(
|
|
op.span(),
|
|
"Invalid properties. Property 'ops' was not found",
|
|
)
|
|
})?;
|
|
let flow = prop.get("flow").ok_or_else(|| {
|
|
helpers::syn_error(
|
|
op.span(),
|
|
"Invalid properties. Property 'flow' was not found",
|
|
)
|
|
})?;
|
|
let current_crate = syn::Ident::new(
|
|
&prop
|
|
.get("crate")
|
|
.map(|v| v.join(""))
|
|
.unwrap_or_else(|| String::from("crate")),
|
|
Span::call_site(),
|
|
);
|
|
|
|
let trait_derive = flow.iter().map(|derive| {
|
|
let derive: Derives = derive.to_owned().into();
|
|
let fns = ops.iter().map(|t| {
|
|
let con: Conversion = t.to_owned().into();
|
|
con.to_function(derive)
|
|
});
|
|
derive.to_operation(fns, struct_name)
|
|
});
|
|
let ref_trait_derive = flow.iter().map(|derive| {
|
|
let derive: Derives = derive.to_owned().into();
|
|
let fns = ops.iter().map(|t| {
|
|
let con: Conversion = t.to_owned().into();
|
|
con.to_ref_function(derive)
|
|
});
|
|
derive.to_ref_operation(fns, struct_name)
|
|
});
|
|
let trait_derive = quote! {
|
|
#(#ref_trait_derive)* #(#trait_derive)*
|
|
};
|
|
let output = quote! {
|
|
const _: () = {
|
|
use #current_crate::core::errors::RouterResult;
|
|
use #current_crate::core::payments::{PaymentData,operations::{
|
|
ValidateRequest,
|
|
PostUpdateTracker,
|
|
GetTracker,
|
|
UpdateTracker,
|
|
}};
|
|
use #current_crate::types::{
|
|
VerifyRequestData,
|
|
PaymentsSyncData,
|
|
PaymentsCaptureData,
|
|
PaymentsCancelData,
|
|
PaymentsAuthorizeData,
|
|
PaymentsSessionData,
|
|
CompleteAuthorizeData,
|
|
|
|
api::{
|
|
PaymentsCaptureRequest,
|
|
PaymentsCancelRequest,
|
|
PaymentsRetrieveRequest,
|
|
PaymentsRequest,
|
|
PaymentsStartRequest,
|
|
PaymentsSessionRequest,
|
|
VerifyRequest
|
|
}
|
|
};
|
|
#trait_derive
|
|
};
|
|
};
|
|
Ok(proc_macro::TokenStream::from(output))
|
|
}
|