1use std::collections::HashMap;
8
9use proc_macro2::{Ident, Span, TokenStream};
10use quote::{format_ident, quote_spanned};
11use syn::{ImplItemFn, Result, Type, parse_quote, parse2};
12
13use crate::class::accessor::{Accessor, flatten_accessors};
14use crate::class::method::Method;
15use crate::class::property::Property;
16
17#[derive(Default)]
18pub(super) struct PrototypeSpecs {
19 pub(super) methods: (Vec<Method>, Vec<Method>),
20 pub(super) properties: (Vec<Property>, Vec<Property>),
21 pub(super) accessors: (HashMap<String, Accessor>, HashMap<String, Accessor>),
22}
23
24impl PrototypeSpecs {
25 pub(super) fn to_impl_fns(
26 &self, ion: &TokenStream, span: Span, ident: &Ident,
27 ) -> Result<(Vec<ImplItemFn>, Vec<ImplItemFn>)> {
28 let mut impl_fns = Vec::with_capacity(4);
29
30 if !self.methods.0.is_empty() {
31 impl_fns.push(methods_to_impl_fn(ion, span, ident, &self.methods.0, false)?);
32 }
33 if !self.methods.1.is_empty() {
34 impl_fns.push(methods_to_impl_fn(ion, span, ident, &self.methods.1, true)?);
35 }
36
37 if !self.properties.0.is_empty() || !self.accessors.0.is_empty() {
38 impl_fns.push(properties_to_spec_function(
39 ion,
40 span,
41 ident,
42 &self.properties.0,
43 &self.accessors.0,
44 false,
45 )?);
46 }
47 if !self.properties.1.is_empty() || !self.accessors.1.is_empty() {
48 impl_fns.push(properties_to_spec_function(
49 ion,
50 span,
51 ident,
52 &self.properties.1,
53 &self.accessors.1,
54 true,
55 )?);
56 }
57
58 Ok(impl_fns.into_iter().unzip())
59 }
60
61 pub(super) fn into_functions(self) -> Vec<Method> {
62 let len = self.methods.0.len() + self.methods.1.len() + self.accessors.0.len() + self.accessors.1.len();
63 let mut functions = Vec::with_capacity(len);
64
65 functions.extend(self.methods.0);
66 functions.extend(self.methods.1);
67 functions.extend(flatten_accessors(self.accessors.0));
68 functions.extend(flatten_accessors(self.accessors.1));
69
70 functions
71 }
72}
73
74fn methods_to_impl_fn(
75 ion: &TokenStream, span: Span, class: &Ident, methods: &[Method], r#static: bool,
76) -> Result<(ImplItemFn, ImplItemFn)> {
77 let mut ident = parse_quote!(functions);
78 if r#static {
79 ident = format_ident!("static_{}", ident);
80 }
81 let function_ident = format_ident!("__ion_{}_specs", ident);
82
83 let specs: Vec<_> = methods.iter().flat_map(|method| method.to_specs(ion, class)).collect();
84
85 let ty = parse_quote!(::mozjs::jsapi::JSFunctionSpec);
86 Ok((
87 spec_function(span, &function_ident, &specs, &ty)?,
88 def_function(span, &ident, &function_ident, &ty)?,
89 ))
90}
91
92fn properties_to_spec_function(
93 ion: &TokenStream, span: Span, class: &Ident, properties: &[Property], accessors: &HashMap<String, Accessor>,
94 r#static: bool,
95) -> Result<(ImplItemFn, ImplItemFn)> {
96 let mut ident = parse_quote!(properties);
97 if r#static {
98 ident = format_ident!("static_{}", ident);
99 }
100 let function_ident = format_ident!("__ion_{}_specs", ident);
101
102 let mut specs: Vec<_> = properties.iter().flat_map(|property| property.to_specs(ion, class)).collect();
103 for accessor in accessors.values() {
104 specs.extend(accessor.to_specs(ion, class));
105 }
106
107 let ty = parse_quote!(::mozjs::jsapi::JSPropertySpec);
108 Ok((
109 spec_function(span, &function_ident, &specs, &ty)?,
110 def_function(span, &ident, &function_ident, &ty)?,
111 ))
112}
113
114fn spec_function(span: Span, function_ident: &Ident, specs: &[TokenStream], ty: &Type) -> Result<ImplItemFn> {
115 assert!(!specs.is_empty());
116 parse2(quote_spanned!(span => fn #function_ident() -> &'static [#ty] {
117 static SPECS: &[#ty] = &[
118 #(#specs,)*
119 #ty::ZERO,
120 ];
121 SPECS
122 }))
123}
124
125fn def_function(span: Span, ident: &Ident, function_ident: &Ident, ty: &Type) -> Result<ImplItemFn> {
126 parse2(
127 quote_spanned!(span => fn #ident() -> ::std::option::Option<&'static [#ty]> {
128 ::std::option::Option::Some(Self::#function_ident())
129 }),
130 )
131}