ion_proc/class/
accessor.rs1use std::collections::HashMap;
8use std::collections::hash_map::Entry;
9
10use convert_case::{Case, Casing as _};
11use proc_macro2::{Ident, TokenStream};
12use quote::{format_ident, quote};
13use syn::spanned::Spanned as _;
14use syn::{Error, ItemFn, Result, Type};
15
16use crate::class::method::{Method, impl_method};
17use crate::function::parameter::Parameters;
18
19pub(super) struct Accessor(pub(super) Option<Method>, Option<Method>);
20
21impl Accessor {
22 pub(super) fn to_specs(&self, ion: &TokenStream, class: &Ident) -> Vec<TokenStream> {
23 let names = self.0.as_ref().or(self.1.as_ref()).map(|method| &*method.names).unwrap_or_default();
24 names
25 .iter()
26 .map(|name| {
27 let mut function_ident = format_ident!("property_spec");
28
29 let (key, flags) = name.to_property_spec(ion, &mut function_ident);
30
31 match self {
32 Accessor(Some(getter), Some(setter)) => {
33 let getter = getter.method.sig.ident.clone();
34 let setter = setter.method.sig.ident.clone();
35
36 function_ident = format_ident!("{}_getter_setter", function_ident);
37 quote!(#ion::#function_ident!(#class::#getter, #class::#setter, #key, #flags))
38 }
39 Accessor(Some(getter), None) => {
40 let getter = getter.method.sig.ident.clone();
41
42 function_ident = format_ident!("{}_getter", function_ident);
43 quote!(#ion::#function_ident!(#class::#getter, #key, #flags))
44 }
45 Accessor(None, Some(setter)) => {
46 let setter = setter.method.sig.ident.clone();
47 function_ident = format_ident!("{}_getter", function_ident);
48 quote!(#ion::#function_ident!(#class::#setter, #key, #flags))
49 }
50 Accessor(None, None) => {
51 function_ident = format_ident!("create_{}_accessor", function_ident);
52 quote!(
53 #ion::spec::#function_ident(
54 #key,
55 ::mozjs::jsapi::JSNativeWrapper { op: None, info: ::std::ptr::null_mut() },
56 ::mozjs::jsapi::JSNativeWrapper { op: None, info: ::std::ptr::null_mut() },
57 #flags,
58 )
59 )
60 }
61 }
62 })
63 .collect()
64 }
65}
66
67pub(super) fn get_accessor_name(mut name: String, is_setter: bool) -> String {
68 let pat_snake = if is_setter { "set_" } else { "get_" };
69 let pat_camel = if is_setter { "set" } else { "get" };
70 if name.starts_with(pat_snake) {
71 name.drain(0..4);
72 if name.is_case(Case::Snake) {
73 name = name.to_case(Case::Camel);
74 }
75 } else if name.starts_with(pat_camel) {
76 name.drain(0..3);
77 if name.is_case(Case::Pascal) {
78 name = name.to_case(Case::Camel);
79 }
80 }
81 name
82}
83
84pub(super) fn impl_accessor(
85 ion: &TokenStream, method: ItemFn, ty: &Type, is_setter: bool, class: &str,
86) -> Result<(Method, Parameters)> {
87 let expected_args = i32::from(is_setter);
88 let error_message = if is_setter {
89 format!("Expected Setter to have {expected_args} argument")
90 } else {
91 format!("Expected Getter to have {expected_args} arguments")
92 };
93 let error = Error::new(method.sig.span(), error_message);
94
95 let ident = if is_setter {
96 format_ident!("__ion_bindings_setter_{}", method.sig.ident)
97 } else {
98 format_ident!("__ion_bindings_getter_{}", method.sig.ident)
99 };
100 let (mut accessor, parameters) = impl_method(ion, method, ty, class, |sig| {
101 let parameters = Parameters::parse(&sig.inputs, Some(ty))?;
102 let nargs: i32 = parameters
103 .parameters
104 .iter()
105 .map(|param| i32::from(matches!(&*param.pat_ty.ty, Type::Path(_))))
106 .sum();
107 (nargs == expected_args).then_some(()).ok_or(error)
108 })?;
109 accessor.method.sig.ident = ident;
110
111 Ok((accessor, parameters))
112}
113
114pub(super) fn insert_accessor(
115 accessors: &mut HashMap<String, Accessor>, name: String, getter: Option<Method>, setter: Option<Method>,
116) {
117 match accessors.entry(name) {
118 Entry::Occupied(mut o) => match (getter, setter) {
119 (Some(g), Some(s)) => *o.get_mut() = Accessor(Some(g), Some(s)),
120 (Some(g), None) => o.get_mut().0 = Some(g),
121 (None, Some(s)) => o.get_mut().1 = Some(s),
122 (None, None) => {}
123 },
124 Entry::Vacant(v) => {
125 v.insert(Accessor(getter, setter));
126 }
127 }
128}
129
130pub(super) fn flatten_accessors(accessors: HashMap<String, Accessor>) -> impl Iterator<Item = Method> {
131 accessors.into_iter().flat_map(|(_, Accessor(getter, setter))| [getter, setter]).flatten()
132}