ligen_core/ir/
function.rs

1use crate::ir::{Attributes, Identifier, Parameter, Type};
2use std::convert::{TryFrom, TryInto};
3use syn::{ImplItemMethod, ItemFn};
4
5#[derive(Debug, PartialEq, Copy, Clone)]
6/// Async Struct
7pub struct Async;
8
9#[derive(Debug, PartialEq, Clone, Copy)]
10/// Function visibility.
11pub enum Visibility {
12    /// Public
13    Public,
14    /// Crate
15    Crate,
16    /// Restricted
17    Restricted,
18    /// Inherited
19    Inherited,
20}
21
22#[derive(Debug, PartialEq, Clone)]
23/// Function Struct
24pub struct Function {
25    /// Attributes field.
26    pub attributes: Attributes,
27    /// Visibility field.
28    pub visibility: Visibility,
29    /// Asyncness field.
30    pub asyncness: Option<Async>,
31    /// Identifier field.
32    pub identifier: Identifier,
33    /// Inputs field.
34    pub inputs: Vec<Parameter>,
35    /// Output field.
36    pub output: Option<Type>,
37}
38
39impl From<syn::Visibility> for Visibility {
40    fn from(visibility: syn::Visibility) -> Self {
41        match visibility {
42            syn::Visibility::Public(_) => Self::Public,
43            syn::Visibility::Crate(_) => Self::Crate,
44            syn::Visibility::Restricted(_) => Self::Restricted,
45            syn::Visibility::Inherited => Self::Inherited,
46        }
47    }
48}
49
50macro_rules! impl_function {
51    ($T:ident) => {
52        impl From<$T> for Function {
53            fn from(item_fn: $T) -> Self {
54                let syn::Signature {
55                    asyncness,
56                    ident,
57                    inputs,
58                    output,
59                    ..
60                } = item_fn.sig;
61                let inputs: Vec<Parameter> = inputs
62                    .clone()
63                    .into_iter()
64                    .map(|x| x.try_into().expect("Failed to convert Parameter"))
65                    .collect();
66                let output: Option<Type> = match output {
67                    syn::ReturnType::Default => None,
68                    syn::ReturnType::Type(_x, y) => {
69                        Some(Type::try_from(*y).expect("Failed to convert from ReturnType::Type"))
70                    }
71                };
72                Self {
73                    attributes: Attributes {
74                        attributes: item_fn
75                            .attrs
76                            .into_iter()
77                            .map(|x| x.parse_meta().expect("Failed to parse Meta").into())
78                            .collect(),
79                    },
80                    visibility: Visibility::from(item_fn.vis),
81                    asyncness: match asyncness {
82                        Some(_x) => Some(Async),
83                        None => None,
84                    },
85                    identifier: ident.into(),
86                    inputs,
87                    output,
88                }
89            }
90        }
91    };
92}
93
94impl_function!(ItemFn);
95impl_function!(ImplItemMethod);
96
97#[cfg(test)]
98mod test {
99    use super::{Async, Function, ImplItemMethod, ItemFn, Type};
100    use crate::ir::{
101        Attribute, Attributes, Identifier, Literal, Parameter, Reference, ReferenceKind, Visibility,
102    };
103    use quote::quote;
104    use syn::parse_quote::parse;
105
106    #[test]
107    fn function() {
108        assert_eq!(
109            Function::from(parse::<ItemFn>(quote! {fn test() {}})),
110            Function {
111                attributes: Attributes { attributes: vec![] },
112                visibility: Visibility::Inherited,
113                asyncness: None,
114                identifier: Identifier::new("test"),
115                inputs: vec![],
116                output: None
117            }
118        );
119    }
120
121    #[test]
122    fn function_impl() {
123        assert_eq!(
124            Function::from(parse::<ImplItemMethod>(quote! {fn test() {}})),
125            Function {
126                attributes: Attributes { attributes: vec![] },
127                visibility: Visibility::Inherited,
128                asyncness: None,
129                identifier: Identifier::new("test"),
130                inputs: vec![],
131                output: None
132            }
133        );
134    }
135
136    #[test]
137    fn function_input() {
138        assert_eq!(
139            Function::from(parse::<ItemFn>(quote! {fn test(a: String, b: String) {}})),
140            Function {
141                attributes: Attributes { attributes: vec![] },
142                visibility: Visibility::Inherited,
143                asyncness: None,
144                identifier: Identifier::new("test"),
145                inputs: vec![
146                    Parameter {
147                        identifier: Identifier::new("a"),
148                        type_: Type::Compound(Identifier::new("String").into())
149                    },
150                    Parameter {
151                        identifier: Identifier::new("b"),
152                        type_: Type::Compound(Identifier::new("String").into())
153                    },
154                ],
155                output: None
156            }
157        );
158    }
159
160    #[test]
161    fn function_output() {
162        assert_eq!(
163            Function::from(parse::<ItemFn>(quote! {fn test() -> String {}})),
164            Function {
165                attributes: Attributes { attributes: vec![] },
166                visibility: Visibility::Inherited,
167                asyncness: None,
168                identifier: Identifier::new("test"),
169                inputs: vec![],
170                output: Some(Type::Compound(Identifier::new("String").into()))
171            }
172        );
173    }
174
175    #[test]
176    fn function_input_output() {
177        assert_eq!(
178            Function::from(parse::<ItemFn>(
179                quote! {fn test(a: String, b: &String, c: &mut String) -> &String {}}
180            )),
181            Function {
182                attributes: Attributes { attributes: vec![] },
183                visibility: Visibility::Inherited,
184                asyncness: None,
185                identifier: Identifier::new("test"),
186                inputs: vec![
187                    Parameter {
188                        identifier: Identifier::new("a"),
189                        type_: Type::Compound(Identifier::new("String").into())
190                    },
191                    Parameter {
192                        identifier: Identifier::new("b"),
193                        type_: Type::Reference(Reference {
194                            kind: ReferenceKind::Borrow,
195                            is_constant: true,
196                            type_: Box::new(Type::Compound(Identifier::new("String").into()))
197                        })
198                    },
199                    Parameter {
200                        identifier: Identifier::new("c"),
201                        type_: Type::Reference(Reference {
202                            kind: ReferenceKind::Borrow,
203                            is_constant: false,
204                            type_: Box::new(Type::Compound(Identifier::new("String").into()))
205                        })
206                    },
207                ],
208                output: Some(Type::Reference(Reference {
209                    kind: ReferenceKind::Borrow,
210                    is_constant: true,
211                    type_: Box::new(Type::Compound(Identifier::new("String").into()))
212                }))
213            }
214        );
215    }
216
217    #[test]
218    fn function_attribute() {
219        assert_eq!(
220            Function::from(parse::<ItemFn>(quote! {
221                #[test(a = "b")]
222                fn test() {}
223            })),
224            Function {
225                attributes: Attributes {
226                    attributes: vec![Attribute::Group(
227                        Identifier::new("test"),
228                        Attributes {
229                            attributes: vec![Attribute::Named(
230                                Identifier::new("a"),
231                                Literal::String(String::from("b"))
232                            )]
233                        }
234                    )]
235                },
236                visibility: Visibility::Inherited,
237                asyncness: None,
238                identifier: Identifier::new("test"),
239                inputs: vec![],
240                output: None
241            }
242        );
243    }
244
245    #[test]
246    fn function_async() {
247        assert_eq!(
248            Function::from(parse::<ItemFn>(quote! {async fn test() {}})),
249            Function {
250                attributes: Attributes { attributes: vec![] },
251                visibility: Visibility::Inherited,
252                asyncness: Some(Async),
253                identifier: Identifier::new("test"),
254                inputs: vec![],
255                output: None
256            }
257        );
258    }
259
260    #[test]
261    fn function_complete() {
262        assert_eq!(
263            Function::from(parse::<ItemFn>(quote! {
264            #[test(a = "b")]
265                async fn test(a: String, b: &String, c: &mut String) -> &String {}
266            })),
267            Function {
268                attributes: Attributes {
269                    attributes: vec![Attribute::Group(
270                        Identifier::new("test"),
271                        Attributes {
272                            attributes: vec![Attribute::Named(
273                                Identifier::new("a"),
274                                Literal::String(String::from("b"))
275                            )]
276                        }
277                    )]
278                },
279                visibility: Visibility::Inherited,
280                asyncness: Some(Async),
281                identifier: Identifier::new("test"),
282                inputs: vec![
283                    Parameter {
284                        identifier: Identifier::new("a"),
285                        type_: Type::Compound(Identifier::new("String").into())
286                    },
287                    Parameter {
288                        identifier: Identifier::new("b"),
289                        type_: Type::Reference(Reference {
290                            kind: ReferenceKind::Borrow,
291                            is_constant: true,
292                            type_: Box::new(Type::Compound(Identifier::new("String").into()))
293                        })
294                    },
295                    Parameter {
296                        identifier: Identifier::new("c"),
297                        type_: Type::Reference(Reference {
298                            kind: ReferenceKind::Borrow,
299                            is_constant: false,
300                            type_: Box::new(Type::Compound(Identifier::new("String").into()))
301                        })
302                    },
303                ],
304                output: Some(Type::Reference(Reference {
305                    kind: ReferenceKind::Borrow,
306                    is_constant: true,
307                    type_: Box::new(Type::Compound(Identifier::new("String").into()))
308                }))
309            }
310        );
311    }
312
313    #[test]
314    fn function_pub() {
315        assert_eq!(
316            Function::from(parse::<ImplItemMethod>(quote! {pub fn test() {}})),
317            Function {
318                attributes: Attributes { attributes: vec![] },
319                visibility: Visibility::Public,
320                asyncness: None,
321                identifier: Identifier::new("test"),
322                inputs: vec![],
323                output: None
324            }
325        );
326    }
327}