1use proc_macro2::{Span, TokenStream};
8use quote::{format_ident, quote, quote_spanned};
9use syn::punctuated::Punctuated;
10use syn::spanned::Spanned as _;
11use syn::visit_mut::visit_type_mut;
12use syn::{Error, Expr, FnArg, Ident, Pat, PatType, Receiver, Result, Stmt, Token, Type, parse_quote, parse2};
13
14use crate::attribute::ParseAttribute as _;
15use crate::attribute::function::ParameterAttribute;
16use crate::utils::{pat_is_ident, path_ends_with};
17use crate::visitors::{LifetimeRemover, SelfRenamer};
18
19pub(crate) struct Parameter {
20 pub(crate) pat_ty: PatType,
21 convert: Option<Box<Expr>>,
22}
23
24#[derive(Clone)]
25pub(crate) enum ThisKind {
26 Ref { ty: Box<Type> },
27 Object,
28 Owned,
29}
30
31pub(crate) struct ThisParameter {
32 pat_ty: PatType,
33 kind: ThisKind,
34}
35
36pub(crate) struct Parameters {
37 pub(crate) parameters: Vec<Parameter>,
38 pub(crate) this: Option<(ThisParameter, Ident, usize)>,
39 pub(crate) nargs: u16,
40}
41
42impl Parameter {
43 pub(crate) fn from_arg(arg: &FnArg) -> Result<Parameter> {
44 match arg {
45 FnArg::Typed(pat_ty) => {
46 let mut pat_ty = pat_ty.clone();
47 let attribute = ParameterAttribute::from_attributes("ion", &mut pat_ty.attrs, ())?;
48 Ok(Parameter { pat_ty, convert: attribute.convert })
49 }
50 FnArg::Receiver(_) => Err(Error::new(arg.span(), "Expected Typed Function Argument")),
51 }
52 }
53
54 pub(crate) fn get_type_without_lifetimes(&self) -> Box<Type> {
55 let mut ty = self.pat_ty.ty.clone();
56 visit_type_mut(&mut LifetimeRemover, &mut ty);
57 ty
58 }
59
60 pub(crate) fn to_statement(&self, ion: &TokenStream, ident: &Ident) -> Result<Stmt> {
61 let span = self.pat_ty.span();
62 let ty = self.get_type_without_lifetimes();
63
64 let pat_ty: PatType = parse2(quote_spanned!(span => #ident: #ty))?;
65
66 let convert;
67 let convert = if let Some(convert) = &self.convert {
68 convert
69 } else {
70 convert = parse_quote!(());
71 &convert
72 };
73
74 parse2(quote_spanned!(span =>
75 let #pat_ty = #ion::function::FromArgument::from_argument(&mut __accessor, #convert)?;
76 ))
77 }
78}
79
80impl ThisParameter {
81 pub(crate) fn from_arg(arg: &FnArg, class_ty: Option<&Type>) -> Result<Option<ThisParameter>> {
82 match arg {
83 FnArg::Typed(pat_ty) => {
84 let span = pat_ty.span();
85 let mut pat_ty = pat_ty.clone();
86 match class_ty {
87 Some(class_ty) if pat_is_ident(&pat_ty.pat, "self") => {
88 visit_type_mut(&mut SelfRenamer { ty: class_ty }, &mut pat_ty.ty);
89 parse_this(pat_ty, true, span).map(Some)
90 }
91 _ => {
92 let attribute = ParameterAttribute::from_attributes("ion", &mut pat_ty.attrs, ())?;
93 if attribute.this {
94 return parse_this(pat_ty, class_ty.is_some(), span).map(Some);
95 }
96 Ok(None)
97 }
98 }
99 }
100 FnArg::Receiver(recv) => {
101 if class_ty.is_none() {
102 return Err(Error::new(arg.span(), "Can only have self on Class Methods"));
103 }
104
105 let class_ty = class_ty.unwrap();
106 if recv.colon_token.is_some() {
107 return Err(Error::new(arg.span(), "Invalid type for self"));
108 }
109
110 let Receiver {
111 attrs, reference, mutability, self_token, ..
112 } = recv;
113 let lifetime = &reference.as_ref().unwrap().1;
114
115 let pat_ty =
116 parse2(quote_spanned!(recv.span() => #(#attrs)* #self_token: &#lifetime #mutability #class_ty))?;
117 parse_this(pat_ty, true, recv.span()).map(Some)
118 }
119 }
120 }
121
122 pub(crate) fn to_statement(&self, ion: &TokenStream, is_class: bool) -> Result<Stmt> {
123 let ThisParameter { pat_ty, kind } = self;
124 let mut pat_ty = pat_ty.clone();
125 pat_ty.attrs.clear();
126
127 if is_class && pat_is_ident(&pat_ty.pat, "self") {
128 pat_ty.pat = parse_quote!(self_);
129 }
130
131 match kind {
132 ThisKind::Ref { ty } => {
133 let mut ty = ty.clone();
134 visit_type_mut(&mut LifetimeRemover, &mut ty);
135
136 if is_class {
137 parse2(quote!(
138 let #pat_ty = <#ty as #ion::ClassDefinition>::get_mut_private(__cx, __this)?;
139 ))
140 } else {
141 parse2(quote!(
142 let #pat_ty = <#ty as #ion::conversions::FromValue>::from_value(__cx, __accessor.this(), true, ())?;
143 ))
144 }
145 }
146 ThisKind::Object => parse2(quote!(let #pat_ty = __this;)),
147 ThisKind::Owned => Err(Error::new(pat_ty.span(), "This cannot be owned")),
148 }
149 }
150}
151
152impl Parameters {
153 pub(crate) fn parse(parameters: &Punctuated<FnArg, Token![,]>, ty: Option<&Type>) -> Result<Parameters> {
154 let mut nargs: u16 = 0;
155 let mut this: Option<(ThisParameter, Ident, usize)> = None;
156
157 let parameters: Vec<_> = parameters
158 .iter()
159 .enumerate()
160 .filter_map(|(i, arg)| {
161 let this_param = ThisParameter::from_arg(arg, ty);
162 match this_param {
163 Ok(Some(this_param)) => {
164 if let Pat::Ident(pat_ident) = &*this_param.pat_ty.pat {
165 if let Some(this) = &this {
166 return Some(Err(Error::new(
167 this.1.span(),
168 "Unable to have multiple this/self parameters",
169 )));
170 }
171 let ident = pat_ident.ident.clone();
172 this = Some((this_param, ident, i));
173 }
174 return None;
175 }
176 Err(e) => return Some(Err(e)),
177 _ => (),
178 }
179
180 let param = match Parameter::from_arg(arg) {
181 Ok(param) => param,
182 Err(e) => return Some(Err(e)),
183 };
184 if let Type::Path(ty) = &*param.pat_ty.ty
185 && !path_ends_with(&ty.path, "Opt")
186 && !path_ends_with(&ty.path, "Rest")
187 {
188 nargs = match nargs.checked_add(1) {
189 Some(nargs) => nargs,
190 None => return Some(Err(Error::new(arg.span(), "Function has too many arguments"))),
191 }
192 }
193 Some(Ok(param))
194 })
195 .collect::<Result<_>>()?;
196
197 Ok(Parameters { parameters, this, nargs })
198 }
199
200 pub(crate) fn to_statements(&self, ion: &TokenStream) -> Result<(Vec<Stmt>, Vec<Ident>)> {
201 let mut statements = Vec::with_capacity(self.parameters.len());
202 let mut idents = Vec::with_capacity(self.parameters.len() + 1);
203 for (i, parameter) in self.parameters.iter().enumerate() {
204 let ident = format_ident!("__ion_var{}", i);
205 statements.push(parameter.to_statement(ion, &ident)?);
206 idents.push(ident);
207 }
208
209 if let Some((_, ident, index)) = &self.this
210 && ident != "self"
211 {
212 idents.insert(*index, ident.clone());
213 }
214
215 Ok((statements, idents))
216 }
217
218 pub(crate) fn get_this_ident(&self) -> Option<Ident> {
219 self.this.as_ref().map(|x| x.1.clone())
220 }
221
222 pub(crate) fn to_this_statement(&self, ion: &TokenStream, is_class: bool) -> Result<Option<Stmt>> {
223 self.this.as_ref().map(|(this, _, _)| this.to_statement(ion, is_class)).transpose()
224 }
225
226 pub(crate) fn to_args(&self) -> Vec<FnArg> {
227 let mut args = Vec::with_capacity(self.parameters.len() + usize::from(self.this.is_some()));
228
229 args.extend(
230 self.parameters
231 .iter()
232 .map(|parameter| FnArg::Typed(parameter.pat_ty.clone()))
233 .collect::<Vec<_>>(),
234 );
235
236 if let Some((ThisParameter { pat_ty, .. }, _, index)) = &self.this {
237 args.insert(*index, FnArg::Typed(pat_ty.clone()));
238 }
239
240 args
241 }
242}
243
244pub(crate) fn parse_this(pat_ty: PatType, is_class: bool, span: Span) -> Result<ThisParameter> {
245 match &*pat_ty.ty {
246 Type::Reference(reference) => {
247 let elem = reference.clone().elem;
248 match &*elem {
249 Type::Path(ty) if path_ends_with(&ty.path, "Object") => {
250 Ok(ThisParameter { pat_ty, kind: ThisKind::Object })
251 }
252 _ => Ok(ThisParameter { pat_ty, kind: ThisKind::Ref { ty: elem } }),
253 }
254 }
255 _ if !is_class => Ok(ThisParameter { pat_ty, kind: ThisKind::Owned }),
256 _ => Err(Error::new(span, "Invalid type for self")),
257 }
258}