windows_interface/
lib.rs

1//! Define COM interfaces to call or implement.
2//!
3//! Take a look at [macro@interface] for an example.
4//!
5//! Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs>
6
7use quote::quote;
8use syn::spanned::Spanned;
9
10/// Defines a COM interface to call or implement.
11///
12/// # Example
13/// ```rust,no_run
14/// use windows_core::*;
15///
16/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")]
17/// unsafe trait IValue: IUnknown {
18///     fn GetValue(&self, value: *mut i32) -> HRESULT;
19/// }
20///
21/// #[implement(IValue)]
22/// struct Value(i32);
23///
24/// impl IValue_Impl for Value_Impl {
25///     unsafe fn GetValue(&self, value: *mut i32) -> HRESULT {
26///         *value = self.0;
27///         HRESULT(0)
28///     }
29/// }
30///
31/// let object: IValue = Value(123).into();
32/// // Call interface methods...
33/// ```
34#[proc_macro_attribute]
35pub fn interface(
36    attributes: proc_macro::TokenStream,
37    original_type: proc_macro::TokenStream,
38) -> proc_macro::TokenStream {
39    let guid = syn::parse_macro_input!(attributes as Guid);
40    let interface = syn::parse_macro_input!(original_type as Interface);
41    let tokens = match interface.gen_tokens(&guid) {
42        Ok(t) => t,
43        Err(e) => return e.to_compile_error().into(),
44    };
45    tokens.into()
46}
47
48macro_rules! bail {
49    ($item:expr, $($msg:tt),*) => {
50        return Err(syn::Error::new($item.span(), std::fmt::format(format_args!($($msg),*))));
51    };
52
53}
54
55macro_rules! unexpected_token {
56    ($item:expr, $msg:expr) => {
57        if let Some(i) = $item {
58            bail!(i, "unexpected {}", $msg);
59        }
60    };
61}
62macro_rules! expected_token {
63    ($sig:tt.$item:tt(), $msg:expr) => {
64        if let None = $sig.$item() {
65            bail!($sig, "expected {}", $msg);
66        }
67    };
68}
69
70/// Parsed interface
71///
72/// ```rust,ignore
73/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
74/// unsafe trait IUIAnimationVariable: IUnknown {
75/// //^ parses this
76///     fn GetValue(&self, value: *mut f64) -> HRESULT;
77/// }
78/// ```
79struct Interface {
80    visibility: syn::Visibility,
81    name: syn::Ident,
82    parent: Option<syn::Path>,
83    methods: Vec<InterfaceMethod>,
84    docs: Vec<syn::Attribute>,
85}
86
87impl Interface {
88    /// Generates all the code needed for a COM interface
89    fn gen_tokens(&self, guid: &Guid) -> syn::Result<proc_macro2::TokenStream> {
90        let vis = &self.visibility;
91        let name = &self.name;
92        let docs = &self.docs;
93        let parent = self.parent_type();
94        let vtable_name = quote::format_ident!("{}_Vtbl", name);
95        let guid = guid.to_tokens()?;
96        let implementation = self.gen_implementation();
97        let com_trait = self.get_com_trait();
98        let vtable = self.gen_vtable(&vtable_name);
99        let conversions = self.gen_conversions();
100
101        Ok(quote! {
102            #[repr(transparent)]
103            #(#docs)*
104            #vis struct #name(#parent);
105            #implementation
106            unsafe impl ::windows_core::Interface for #name {
107                type Vtable = #vtable_name;
108                const IID: ::windows_core::GUID = #guid;
109            }
110            impl ::windows_core::RuntimeName for #name {}
111            impl ::core::ops::Deref for #name {
112                type Target = #parent;
113                fn deref(&self) -> &Self::Target {
114                    unsafe { ::core::mem::transmute(self) }
115                }
116            }
117            #com_trait
118            #vtable
119            #conversions
120        })
121    }
122
123    /// Generates the methods users can call on the COM interface pointer
124    fn gen_implementation(&self) -> proc_macro2::TokenStream {
125        let name = &self.name;
126        let methods = self
127            .methods
128            .iter()
129            .map(|m| {
130                let vis = &m.visibility;
131                let name = &m.name;
132
133                let generics = m.gen_consume_generics();
134                let params = m.gen_consume_params();
135                let args = m.gen_consume_args();
136                let ret = &m.ret;
137
138                if m.is_result() {
139                    quote! {
140                    #[inline(always)]
141                    #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret {
142                            (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*).ok()
143                        }
144                    }
145                } else {
146                    quote! {
147                        #[inline(always)]
148                        #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret {
149                            (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*)
150                        }
151                    }
152                }
153            })
154            .collect::<Vec<_>>();
155        quote! {
156            impl #name {
157                #(#methods)*
158            }
159        }
160    }
161
162    fn get_com_trait(&self) -> proc_macro2::TokenStream {
163        let name = quote::format_ident!("{}_Impl", self.name);
164        let vis = &self.visibility;
165        let methods = self
166            .methods
167            .iter()
168            .map(|m| {
169                let name = &m.name;
170                let docs = &m.docs;
171                let args = m.gen_args();
172                let ret = &m.ret;
173                quote! {
174                    #(#docs)*
175                    unsafe fn #name(&self, #(#args),*) #ret;
176                }
177            })
178            .collect::<Vec<_>>();
179        let parent = self.parent_trait_constraint();
180
181        quote! {
182            #[allow(non_camel_case_types)]
183            #vis trait #name: Sized + #parent {
184                #(#methods)*
185            }
186        }
187    }
188
189    /// Generates the vtable for a COM interface
190    fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream {
191        let vis = &self.visibility;
192        let name = &self.name;
193        let trait_name = quote::format_ident!("{}_Impl", name);
194        let implvtbl_name = quote::format_ident!("{}_ImplVtbl", name);
195
196        let vtable_entries = self
197            .methods
198            .iter()
199            .map(|m| {
200                let name = &m.name;
201                let ret = &m.ret;
202                let args = m.gen_args();
203
204                if m.is_result() {
205                    quote! {
206                        pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows_core::HRESULT,
207                    }
208                } else {
209                    quote! {
210                        pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) #ret,
211                    }
212                }
213            })
214            .collect::<Vec<_>>();
215
216        let parent_vtable_generics = quote!(Identity, OFFSET);
217        let parent_vtable = self.parent_vtable();
218
219        // or_parent_matches will be `|| parent::matches(iid)` if this interface inherits from another
220        // interface (except for IUnknown) or will be empty if this is not applicable. This is what allows
221        // QueryInterface to work correctly for all interfaces in an inheritance chain, e.g.
222        // IFoo3 derives from IFoo2 derives from IFoo.
223        //
224        // We avoid matching IUnknown because object identity depends on the uniqueness of the IUnknown pointer.
225        let or_parent_matches = match parent_vtable.as_ref() {
226            Some(parent) if !self.parent_is_iunknown() => quote! (|| <#parent>::matches(iid)),
227            _ => quote!(),
228        };
229
230        let functions = self
231            .methods
232            .iter()
233            .map(|m| {
234                let name = &m.name;
235                let args = m.gen_args();
236                let params = &m
237                    .args
238                    .iter()
239                    .map(|a| {
240                        let pat = &a.pat;
241                        quote! { #pat }
242                    })
243                    .collect::<Vec<_>>();
244                let ret = &m.ret;
245
246                let ret = if m.is_result() {
247                    quote! { -> ::windows_core::HRESULT }
248                } else {
249                    quote! { #ret }
250                };
251
252                if parent_vtable.is_some() {
253                    quote! {
254                        unsafe extern "system" fn #name<
255                            Identity: ::windows_core::IUnknownImpl,
256                            const OFFSET: isize
257                        >(
258                            this: *mut ::core::ffi::c_void, // <-- This is the COM "this" pointer, which is not the same as &T or &T_Impl.
259                            #(#args),*
260                        ) #ret
261                        where
262                            Identity : #trait_name
263                        {
264                            // This step is essentially a virtual dispatch adjustor thunk. Its purpose is to adjust
265                            // the "this" pointer from the address used by the COM interface to the root of the
266                            // MyApp_Impl object.  Since a given MyApp_Impl may implement more than one COM interface
267                            // (and more than one COM interface chain), we need to know how to get from COM's "this"
268                            // back to &MyApp_Impl. The OFFSET constant gives us the value (in pointer-sized units).
269                            let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity);
270
271                            // Last, we invoke the implementation function.
272                            // We use explicit <Impl as IFoo_Impl> so that we can select the correct method
273                            // for situations where IFoo3 derives from IFoo2 and both declare a method with
274                            // the same name.
275                            <Identity as #trait_name>::#name(this_outer, #(#params),*).into()
276                        }
277                    }
278                } else {
279                    quote! {
280                        unsafe extern "system" fn #name<Impl: #trait_name>(this: *mut ::core::ffi::c_void, #(#args),*) #ret {
281                            let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows_core::ScopedHeap;
282                            let this = (*this).this as *const Impl;
283                            (*this).#name(#(#params),*).into()
284                        }
285                    }
286                }
287            })
288            .collect::<Vec<_>>();
289
290        if let Some(parent_vtable) = parent_vtable {
291            let entries = self
292                .methods
293                .iter()
294                .map(|m| {
295                    let name = &m.name;
296                    quote!(#name: #name::<Identity, OFFSET>)
297                })
298                .collect::<Vec<_>>();
299
300            quote! {
301                #[repr(C)]
302                #[doc(hidden)]
303                #vis struct #vtable_name {
304                    pub base__: #parent_vtable,
305                    #(#vtable_entries)*
306                }
307                impl #vtable_name {
308                    pub const fn new<
309                        Identity: ::windows_core::IUnknownImpl,
310                        const OFFSET: isize,
311                    >() -> Self
312                    where
313                        Identity : #trait_name
314                    {
315                        #(#functions)*
316                        Self { base__: #parent_vtable::new::<#parent_vtable_generics>(), #(#entries),* }
317                    }
318
319                    #[inline(always)]
320                    pub fn matches(iid: &::windows_core::GUID) -> bool {
321                        *iid == <#name as ::windows_core::Interface>::IID
322                        #or_parent_matches
323                    }
324                }
325            }
326        } else {
327            let entries = self
328                .methods
329                .iter()
330                .map(|m| {
331                    let name = &m.name;
332                    quote!(#name: #name::<Impl>)
333                })
334                .collect::<Vec<_>>();
335
336            quote! {
337                #[repr(C)]
338                #[doc(hidden)]
339                #vis struct #vtable_name {
340                    #(#vtable_entries)*
341                }
342                impl #vtable_name {
343                    pub const fn new<Impl: #trait_name>() -> Self {
344                        #(#functions)*
345                        Self { #(#entries),* }
346                    }
347                }
348                struct #implvtbl_name<T: #trait_name> (::core::marker::PhantomData<T>);
349                impl<T: #trait_name> #implvtbl_name<T> {
350                    const VTABLE: #vtable_name = #vtable_name::new::<T>();
351                }
352                impl #name {
353                    fn new<'a, T: #trait_name>(this: &'a T) -> ::windows_core::ScopedInterface<'a, #name> {
354                        let this = ::windows_core::ScopedHeap { vtable: &#implvtbl_name::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ };
355                        let this = ::core::mem::ManuallyDrop::new(::windows_core::imp::Box::new(this));
356                        unsafe { ::windows_core::ScopedInterface::new(::core::mem::transmute(&this.vtable)) }
357                    }
358                }
359            }
360        }
361    }
362
363    /// Generates various conversions such as from and to `IUnknown`
364    fn gen_conversions(&self) -> proc_macro2::TokenStream {
365        let name = &self.name;
366        let name_string = format!("{name}");
367        quote! {
368            impl ::core::convert::From<#name> for ::windows_core::IUnknown {
369                fn from(value: #name) -> Self {
370                    unsafe { ::core::mem::transmute(value) }
371                }
372            }
373            impl ::core::convert::From<&#name> for ::windows_core::IUnknown {
374                fn from(value: &#name) -> Self {
375                    ::core::convert::From::from(::core::clone::Clone::clone(value))
376                }
377            }
378            impl ::core::clone::Clone for #name {
379                fn clone(&self) -> Self {
380                    Self(self.0.clone())
381                }
382            }
383            impl ::core::cmp::PartialEq for #name {
384                fn eq(&self, other: &Self) -> bool {
385                    self.0 == other.0
386                }
387            }
388            impl ::core::cmp::Eq for #name {}
389            impl ::core::fmt::Debug for #name {
390                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
391                    f.debug_tuple(#name_string).field(&::windows_core::Interface::as_raw(self)).finish()
392                }
393            }
394        }
395    }
396
397    fn parent_type(&self) -> proc_macro2::TokenStream {
398        if let Some(parent) = &self.parent {
399            quote!(#parent)
400        } else {
401            quote!(::core::ptr::NonNull<::core::ffi::c_void>)
402        }
403    }
404
405    fn parent_vtable(&self) -> Option<proc_macro2::TokenStream> {
406        if let Some((ident, path)) = self.parent_path().split_last() {
407            let ident = quote::format_ident!("{}_Vtbl", ident);
408            Some(quote! { #(#path::)* #ident })
409        } else {
410            None
411        }
412    }
413
414    fn parent_is_iunknown(&self) -> bool {
415        if let Some(ident) = self.parent_path().last() {
416            ident == "IUnknown"
417        } else {
418            false
419        }
420    }
421
422    fn parent_path(&self) -> Vec<syn::Ident> {
423        if let Some(parent) = &self.parent {
424            parent
425                .segments
426                .iter()
427                .map(|segment| segment.ident.clone())
428                .collect()
429        } else {
430            vec![]
431        }
432    }
433
434    /// Gets the parent trait constrait which is nothing if the parent is IUnknown
435    fn parent_trait_constraint(&self) -> proc_macro2::TokenStream {
436        if let Some((ident, path)) = self.parent_path().split_last() {
437            if ident != "IUnknown" {
438                let ident = quote::format_ident!("{}_Impl", ident);
439                return quote! { #(#path::)* #ident };
440            }
441        }
442
443        quote! {}
444    }
445}
446
447impl syn::parse::Parse for Interface {
448    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
449        let attributes = input.call(syn::Attribute::parse_outer)?;
450        let mut docs = Vec::new();
451        for attr in attributes.into_iter() {
452            let path = attr.path();
453            if path.is_ident("doc") {
454                docs.push(attr);
455            } else {
456                return Err(syn::Error::new(path.span(), "Unrecognized attribute "));
457            }
458        }
459
460        let visibility = input.parse::<syn::Visibility>()?;
461        _ = input.parse::<syn::Token![unsafe]>()?;
462        _ = input.parse::<syn::Token![trait]>()?;
463        let name = input.parse::<syn::Ident>()?;
464        _ = input.parse::<syn::Token![:]>();
465        let parent = input.parse::<syn::Path>().ok();
466        let content;
467        syn::braced!(content in input);
468        let mut methods = Vec::new();
469        while !content.is_empty() {
470            methods.push(content.parse::<InterfaceMethod>()?);
471        }
472        Ok(Self {
473            visibility,
474            methods,
475            name,
476            parent,
477            docs,
478        })
479    }
480}
481
482/// Parsed interface guid attribute
483///
484/// ```rust,ignore
485/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
486///                              //^ parses this
487/// unsafe trait IUIAnimationVariable: IUnknown {
488///     fn GetValue(&self, value: *mut f64) -> HRESULT;
489/// }
490/// ```
491struct Guid(Option<syn::LitStr>);
492
493impl Guid {
494    fn to_tokens(&self) -> syn::Result<proc_macro2::TokenStream> {
495        fn hex_lit(num: &str) -> syn::LitInt {
496            syn::LitInt::new(&format!("0x{num}"), proc_macro2::Span::call_site())
497        }
498
499        fn ensure_length(
500            part: Option<&str>,
501            index: usize,
502            length: usize,
503            span: proc_macro2::Span,
504        ) -> syn::Result<String> {
505            let part = match part {
506                Some(p) => p,
507                None => {
508                    return Err(syn::Error::new(
509                        span,
510                        format!("The IID missing part at index {index}"),
511                    ))
512                }
513            };
514
515            if part.len() != length {
516                return Err(syn::Error::new(
517                    span,
518                    format!(
519                        "The IID part at index {} must be {} characters long but was {} characters",
520                        index,
521                        length,
522                        part.len()
523                    ),
524                ));
525            }
526
527            Ok(part.to_owned())
528        }
529
530        if let Some(value) = &self.0 {
531            let guid_value = value.value();
532            let mut delimited = guid_value.split('-').fuse();
533            let chunks = [
534                ensure_length(delimited.next(), 0, 8, value.span())?,
535                ensure_length(delimited.next(), 1, 4, value.span())?,
536                ensure_length(delimited.next(), 2, 4, value.span())?,
537                ensure_length(delimited.next(), 3, 4, value.span())?,
538                ensure_length(delimited.next(), 4, 12, value.span())?,
539            ];
540
541            let data1 = hex_lit(&chunks[0]);
542            let data2 = hex_lit(&chunks[1]);
543            let data3 = hex_lit(&chunks[2]);
544            let (data4_1, data4_2) = chunks[3].split_at(2);
545            let data4_1 = hex_lit(data4_1);
546            let data4_2 = hex_lit(data4_2);
547            let (data4_3, rest) = chunks[4].split_at(2);
548            let data4_3 = hex_lit(data4_3);
549
550            let (data4_4, rest) = rest.split_at(2);
551            let data4_4 = hex_lit(data4_4);
552
553            let (data4_5, rest) = rest.split_at(2);
554            let data4_5 = hex_lit(data4_5);
555
556            let (data4_6, rest) = rest.split_at(2);
557            let data4_6 = hex_lit(data4_6);
558
559            let (data4_7, data4_8) = rest.split_at(2);
560            let data4_7 = hex_lit(data4_7);
561            let data4_8 = hex_lit(data4_8);
562            Ok(quote! {
563                ::windows_core::GUID {
564                    data1: #data1,
565                    data2: #data2,
566                    data3: #data3,
567                    data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8]
568                }
569            })
570        } else {
571            Ok(quote! {
572                ::windows_core::GUID::zeroed()
573            })
574        }
575    }
576}
577
578impl syn::parse::Parse for Guid {
579    fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
580        let string: Option<syn::LitStr> = cursor.parse().ok();
581
582        Ok(Self(string))
583    }
584}
585
586/// A parsed interface method
587///
588/// ```rust,ignore
589/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
590/// unsafe trait IUIAnimationVariable: IUnknown {
591///     fn GetValue(&self, value: *mut f64) -> HRESULT;
592///   //^ parses this
593/// }
594/// ```
595struct InterfaceMethod {
596    pub name: syn::Ident,
597    pub visibility: syn::Visibility,
598    pub args: Vec<InterfaceMethodArg>,
599    pub ret: syn::ReturnType,
600    pub docs: Vec<syn::Attribute>,
601}
602
603impl InterfaceMethod {
604    fn is_result(&self) -> bool {
605        if let syn::ReturnType::Type(_, ty) = &self.ret {
606            if let syn::Type::Path(path) = &**ty {
607                if let Some(segment) = path.path.segments.last() {
608                    let ident = segment.ident.to_string();
609                    if ident == "Result" {
610                        if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
611                            if args.args.len() == 1 {
612                                return true;
613                            }
614                        }
615                    }
616                }
617            }
618        }
619
620        false
621    }
622
623    /// Generates arguments (of the form `$pat: $type`)
624    fn gen_args(&self) -> Vec<proc_macro2::TokenStream> {
625        self.args
626            .iter()
627            .map(|a| {
628                let pat = &a.pat;
629                let ty = &a.ty;
630                quote! { #pat: #ty }
631            })
632            .collect::<Vec<_>>()
633    }
634
635    fn gen_consume_generics(&self) -> Vec<proc_macro2::TokenStream> {
636        self.args
637            .iter()
638            .enumerate()
639            .filter_map(|(generic_index, a)| {
640                if let Some((ty, ident)) = a.borrow_type() {
641                    let generic_ident = quote::format_ident!("P{generic_index}");
642                    if ident == "Ref" {
643                        Some(quote! { #generic_ident: ::windows_core::Param<#ty> })
644                    } else {
645                        Some(quote! { #generic_ident: ::windows_core::OutParam<#ty> })
646                    }
647                } else {
648                    None
649                }
650            })
651            .collect::<Vec<_>>()
652    }
653
654    fn gen_consume_params(&self) -> Vec<proc_macro2::TokenStream> {
655        self.args
656            .iter()
657            .enumerate()
658            .map(|(generic_index, a)| {
659                let pat = &a.pat;
660
661                if a.borrow_type().is_some() {
662                    let generic_ident = quote::format_ident!("P{generic_index}");
663                    quote! { #pat: #generic_ident }
664                } else {
665                    let ty = &a.ty;
666                    quote! { #pat: #ty }
667                }
668            })
669            .collect::<Vec<_>>()
670    }
671
672    fn gen_consume_args(&self) -> Vec<proc_macro2::TokenStream> {
673        self.args
674            .iter()
675            .map(|a| {
676                let pat = &a.pat;
677
678                if let Some((_, ident)) = a.borrow_type() {
679                    if ident == "Ref" {
680                        quote! { #pat.param().borrow() }
681                    } else {
682                        quote! { #pat.borrow_mut() }
683                    }
684                } else {
685                    quote! { #pat }
686                }
687            })
688            .collect::<Vec<_>>()
689    }
690}
691
692impl syn::parse::Parse for InterfaceMethod {
693    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
694        let docs = input.call(syn::Attribute::parse_outer)?;
695        let visibility = input.parse::<syn::Visibility>()?;
696        let method = input.parse::<syn::TraitItemFn>()?;
697        unexpected_token!(docs.iter().find(|a| !a.path().is_ident("doc")), "attribute");
698        unexpected_token!(method.default, "default method implementation");
699        let sig = method.sig;
700        unexpected_token!(sig.abi, "abi declaration");
701        unexpected_token!(sig.asyncness, "async declaration");
702        unexpected_token!(sig.generics.params.iter().next(), "generics declaration");
703        unexpected_token!(sig.constness, "const declaration");
704        expected_token!(
705            sig.receiver(),
706            "the method to have &self as its first argument"
707        );
708        unexpected_token!(sig.variadic, "variadic args");
709        let args = sig
710            .inputs
711            .into_iter()
712            .filter_map(|a| match a {
713                syn::FnArg::Receiver(_) => None,
714                syn::FnArg::Typed(p) => Some(p),
715            })
716            .map(|p| {
717                Ok(InterfaceMethodArg {
718                    ty: p.ty,
719                    pat: p.pat,
720                })
721            })
722            .collect::<Result<Vec<InterfaceMethodArg>, syn::Error>>()?;
723
724        let ret = sig.output;
725        Ok(InterfaceMethod {
726            name: sig.ident,
727            visibility,
728            args,
729            ret,
730            docs,
731        })
732    }
733}
734
735/// An argument to an interface method
736struct InterfaceMethodArg {
737    /// The type of the argument
738    pub ty: Box<syn::Type>,
739    /// The name of the argument
740    pub pat: Box<syn::Pat>,
741}
742
743impl InterfaceMethodArg {
744    fn borrow_type(&self) -> Option<(syn::Type, String)> {
745        if let syn::Type::Path(path) = &*self.ty {
746            if let Some(segment) = path.path.segments.last() {
747                let ident = segment.ident.to_string();
748                if matches!(ident.as_str(), "Ref" | "OutRef") {
749                    if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
750                        if args.args.len() == 1 {
751                            if let Some(syn::GenericArgument::Type(ty)) = args.args.first() {
752                                return Some((ty.clone(), ident));
753                            }
754                        }
755                    }
756                }
757            }
758        }
759
760        None
761    }
762}