1use std::ffi::CString;
8
9use convert_case::{Case, Casing as _};
10use proc_macro2::{Ident, TokenStream};
11use quote::{format_ident, quote};
12use syn::{ItemFn, LitCStr, Result, Signature, Type};
13
14use crate::attribute::name::Name;
15use crate::function::parameter::Parameters;
16use crate::function::wrapper::impl_wrapper_fn;
17use crate::function::{check_abi, impl_fn_body, set_signature};
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub(crate) enum MethodKind {
21 Constructor,
22 Getter,
23 Setter,
24}
25
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub(super) enum MethodReceiver {
28 Dynamic,
29 Static,
30}
31
32#[derive(Clone)]
33pub(super) struct Method {
34 pub(super) receiver: MethodReceiver,
35 pub(super) method: ItemFn,
36 pub(super) nargs: u16,
37 pub(super) names: Vec<Name>,
38}
39
40impl Method {
41 pub(super) fn to_specs(&self, ion: &TokenStream, class: &Ident) -> Vec<TokenStream> {
42 let ident = &self.method.sig.ident;
43 let nargs = self.nargs;
44
45 self.names
46 .iter()
47 .map(|name| match name {
48 Name::String(literal) => {
49 let mut name = literal.value();
50 if name.is_case(Case::Snake) {
51 name = name.to_case(Case::Camel);
52 }
53 let literal = LitCStr::new(&CString::new(name).unwrap(), literal.span());
54 quote!(#ion::function_spec!(#class::#ident, #literal, #nargs, #ion::flags::PropertyFlags::CONSTANT_ENUMERATED))
55 }
56 Name::Symbol(symbol) => {
57 quote!(#ion::function_spec_symbol!(#class::#ident, #symbol, #nargs, #ion::flags::PropertyFlags::CONSTANT))
58 }
59 })
60 .collect()
61 }
62}
63
64pub(super) fn impl_method<F>(
65 ion: &TokenStream, mut method: ItemFn, ty: &Type, class: &str, predicate: F,
66) -> Result<(Method, Parameters)>
67where
68 F: FnOnce(&Signature) -> Result<()>,
69{
70 let fn_ident = &method.sig.ident;
71 let (wrapper, parameters) = impl_wrapper_fn(ion, method.clone(), Some(ty), false, &format!("{class}.{fn_ident}"))?;
72
73 predicate(&method.sig)?;
74
75 check_abi(&mut method)?;
76 set_signature(&mut method);
77
78 method.attrs.clear();
79 method.sig.ident = format_ident!("__ion_bindings_method_{}", method.sig.ident);
80 method.block = impl_fn_body(ion, &wrapper)?;
81
82 let method = Method {
83 receiver: if parameters.this.is_some() {
84 MethodReceiver::Dynamic
85 } else {
86 MethodReceiver::Static
87 },
88 method,
89 nargs: parameters.nargs,
90 names: vec![],
91 };
92 Ok((method, parameters))
93}