1use crate::ir::{Attributes, Identifier, Parameter, Type};
2use std::convert::{TryFrom, TryInto};
3use syn::{ImplItemMethod, ItemFn};
4
5#[derive(Debug, PartialEq, Copy, Clone)]
6pub struct Async;
8
9#[derive(Debug, PartialEq, Clone, Copy)]
10pub enum Visibility {
12 Public,
14 Crate,
16 Restricted,
18 Inherited,
20}
21
22#[derive(Debug, PartialEq, Clone)]
23pub struct Function {
25 pub attributes: Attributes,
27 pub visibility: Visibility,
29 pub asyncness: Option<Async>,
31 pub identifier: Identifier,
33 pub inputs: Vec<Parameter>,
35 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}