ion_proc/class/
method.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use 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}