Skip to main content

rglw_codegen_utils/generics/
macro_attribute_function_arguments.rs

1use proc_macro2::{Ident, Span, TokenStream};
2use quote::{quote, ToTokens};
3use syn::{FnArg, GenericArgument, ItemFn, Pat, PathArguments, PathSegment, Type};
4use crate::generics::get_type_path_segments;
5
6#[derive(Clone)]
7pub struct MacroAttributeFunctionArgument {
8    _raw: FnArg,
9    _field_name: String,
10    type_name: String,
11    argument_type: Type
12}
13
14#[derive(Clone)]
15pub struct MacroAttributeFunctionArguments {
16    arguments: Vec<MacroAttributeFunctionArgument>
17}
18
19pub fn get_function_argument_type(argument: FnArg) -> Type {
20    match argument {
21        FnArg::Receiver(receiver) => {
22            *receiver.ty.clone()
23        }
24        FnArg::Typed(typed) => {
25            *typed.ty.clone()
26        },
27    }
28}
29
30pub fn get_function_argument_field_name(argument: FnArg) -> String {
31    match argument {
32        FnArg::Receiver(_receiver) => "self".to_string(),
33        FnArg::Typed(typed) => {
34            match *typed.pat {
35                Pat::Ident(pat_ident) => {
36                    pat_ident.ident.to_string()
37                },
38                _=> {
39                    panic!("Argument type not supported!");
40                }
41            }
42        },
43    }
44}
45
46pub fn get_function_type_name(argument: FnArg) -> String {
47    match argument {
48        FnArg::Receiver(receiver) => {
49            receiver.ty.to_token_stream().to_string()
50        }
51        FnArg::Typed(typed) => {
52            typed.ty.clone().to_token_stream().to_string()
53        },
54    }
55}
56
57impl MacroAttributeFunctionArguments {
58    pub fn parse(input: &ItemFn) -> MacroAttributeFunctionArguments {
59        let mut arguments: Vec<MacroAttributeFunctionArgument> = Vec::new();
60        for argument in input.sig.inputs.clone() {
61            arguments.push(MacroAttributeFunctionArgument {
62                _raw: argument.clone(),
63                _field_name: get_function_argument_field_name(argument.clone()),
64                type_name: get_function_type_name(argument.clone()),
65                argument_type: get_function_argument_type(argument.clone()),
66            });
67        }
68        MacroAttributeFunctionArguments {
69            arguments,
70        }
71    }
72    
73    pub fn get_path_segment_by_type_name(&self, type_name: &str) -> Option<PathSegment> {
74        let mut result: Option<PathSegment> = None;
75        for argument in self.arguments.clone() {
76            let segments = get_type_path_segments(argument.argument_type);
77            for segment in segments {
78                if segment.ident.to_string().eq(type_name) {
79                    result = Some(segment.clone());
80                }
81            }
82        }
83        result
84    }
85
86    pub fn get_path_arguments_by_type_name(&self, type_name: &str) -> Option<PathArguments> {
87        let mut result: Option<PathArguments> = None;
88        for argument in self.arguments.clone() {
89            let segments = get_type_path_segments(argument.argument_type);
90            for segment in segments {
91                if segment.ident.to_string().eq(type_name) {
92                    result = Some(segment.clone().arguments);
93                }
94            }
95        }
96        result
97    }
98    
99    pub fn get_stream_of_argument_by_name(&self, container_type_name: &str) -> Option<TokenStream> {
100        let mut result: Option<TokenStream> = None;
101        let response_type_segment = self.get_path_segment_by_type_name(container_type_name).unwrap();
102        if response_type_segment.arguments.is_empty() {
103            panic!("Stream has to contain a generic type!");
104        }
105        let type_arguments = response_type_segment.arguments;
106        if let PathArguments::AngleBracketed(generic_arguments) = type_arguments { 
107            let generic_arguments = generic_arguments.args.to_token_stream();
108            result = Some(quote! {
109                #generic_arguments
110            });
111        }
112        result
113    }
114
115    pub fn get_stream_of_argument_without_generics_by_name(&self, container_type_name: &str) -> Option<TokenStream> {
116        let mut result: Option<TokenStream> = None;
117        let response_type_segment = self.get_path_segment_by_type_name(container_type_name).unwrap();
118        if response_type_segment.arguments.is_empty() {
119            panic!("Stream has to contain a generic type!");
120        }
121        let type_arguments = response_type_segment.arguments;
122        if let PathArguments::AngleBracketed(generic_arguments) = type_arguments {
123            let generic_argument = generic_arguments.args.first()?;
124            if let GenericArgument::Type(Type::Path(type_path)) = generic_argument { 
125                let generic_argument = type_path.path.segments.first()?.ident.to_token_stream();
126                result = Some(quote! {
127                    #generic_argument
128                });
129            }
130        }
131        result
132    }
133
134    pub fn get_injectable_arguments(&self) -> Vec<TokenStream> {
135        let mut result: Vec<TokenStream> = Vec::new();
136        if self.arguments.len() > 2 {
137            let injectable_arguments = self.arguments[2..].to_vec();
138            for injectable_argument in injectable_arguments {
139                let injectable = Ident::new(injectable_argument.type_name.as_str(), Span::call_site());
140                result.push(quote! {
141                    #injectable::inject().await.unwrap(),
142                });
143            }
144        }
145        result
146    }
147}