test_builder/
lib.rs

1#![deny(rustdoc::invalid_rust_codeblocks)]
2#![feature(let_chains)]
3#![feature(proc_macro_diagnostic)]
4#![recursion_limit = "256"]
5#![allow(unused)]
6
7use proc_macro::{Diagnostic, TokenStream};
8use quote::quote;
9use syn::{
10    parse::Parse, parse_macro_input, spanned::Spanned, FnArg, GenericParam, Generics, Ident,
11    ImplItem, ImplItemFn, ItemImpl, PatType, Receiver, ReturnType, TypeParam,
12};
13
14const FUNCTION_TEST_DATA_ATTRIBUTE_NAME: &str = "test_data";
15
16fn get_generics(generics: Generics) -> Vec<TypeParam> {
17    generics
18        .params
19        .into_iter()
20        .filter_map(|generic| {
21            if let GenericParam::Type(function) = generic {
22                Some(function)
23            } else {
24                None
25            }
26        })
27        .collect()
28}
29
30#[derive(Debug)]
31struct TestedFns {
32    generics: Vec<TypeParam>,
33    functions: Vec<ImplItemFn>,
34}
35
36impl Parse for TestedFns {
37    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
38        let tested_impl_block: ItemImpl = input.parse()?;
39        let generics = get_generics(tested_impl_block.generics);
40
41        let functions = tested_impl_block
42            .items
43            .into_iter()
44            .filter_map(|item| {
45                if let ImplItem::Fn(function) = item
46                    && function
47                        .attrs
48                        .iter()
49                        .all(|attr| attr.path().is_ident(FUNCTION_TEST_DATA_ATTRIBUTE_NAME))
50                {
51                    Some(function)
52                } else {
53                    None
54                }
55            })
56            .collect();
57
58        Ok(TestedFns {
59            generics,
60            functions,
61        })
62    }
63}
64
65#[derive(Debug)]
66enum TestedMethod {
67    StaticMethod {
68        name: Ident,
69        generics: Vec<TypeParam>,
70        args: Result<Vec<PatType>, Diagnostic>,
71        return_type: ReturnType,
72    },
73    InstanceMethod {
74        name: Ident,
75        generics: Vec<TypeParam>,
76        self_type: Receiver,
77        args: Result<Vec<PatType>, Diagnostic>,
78        return_type: ReturnType,
79    },
80}
81
82impl From<TestedFns> for Vec<TestedMethod> {
83    fn from(mut value: TestedFns) -> Self {
84        value
85            .functions
86            .into_iter()
87            .map(|function| function.sig)
88            .map(|mut signature| {
89                let name = signature.ident.clone();
90                let mut generics = get_generics(signature.generics.clone());
91                generics.append(&mut value.generics);
92                let return_type = signature.output.clone();
93
94                if let Some(self_type) = signature.receiver() {
95                    TestedMethod::InstanceMethod {
96                        name,
97                        generics,
98                        self_type: self_type.clone(),
99                        args: {
100                            signature.inputs.pop();
101                            signature
102                                .inputs
103                                .clone()
104                                .into_iter()
105                                .map(|arg| {
106                                    if let FnArg::Receiver(_) = arg {
107                                        Err(signature.span().unwrap().error(
108                                            "An instance method cannot have 2 or more self parameters",
109                                        ))
110                                    } else if let FnArg::Typed(arg_type) = arg {
111                                        Ok(arg_type)
112                                    } else {
113                                        unreachable!(
114                                            "Fnarg is either `Receiver` or `Typed`, not something else"
115                                        );
116                                    }
117                                })
118                                .collect()
119                        },
120                        return_type,
121                    }
122                } else {
123                    TestedMethod::StaticMethod {
124                        name,
125                        generics,
126                        args: signature
127                            .inputs
128                            .clone()
129                            .into_iter()
130                            .map(|arg| {
131                                if let FnArg::Typed(arg_type) = arg {
132                                    Ok(arg_type)
133                                } else {
134                                    Err(signature
135                                        .span()
136                                        .unwrap()
137                                        .error("Cannot have a self type in a static method."))
138                                }
139                            })
140                            .collect(),
141                        return_type,
142                    }
143                }
144            })
145            .collect()
146    }
147}
148
149#[proc_macro_attribute]
150pub fn test_builder(attr: TokenStream, item: TokenStream) -> TokenStream {
151    let all_method: Vec<TestedMethod> = parse_macro_input!(item as TestedFns).into();
152
153    println!("{all_method:#?}");
154
155    // let all_expanded = all_method
156    //     .iter()
157    //     .map(|tested_method| match tested_method {
158    //         TestedMethod::StaticMethod {
159    //             name,
160    //             generics,
161    //             args,
162    //             return_type,
163    //         } => {
164    //             let test_method_name = quote! {
165    //                 test_for_ #name _for_type_
166    //             };
167    //         }
168    //         TestedMethod::InstanceMethod {
169    //             name,
170    //             generics,
171    //             self_type,
172    //             args,
173    //             return_type,
174    //         } => todo!(),
175    //     })
176    //     .collect::<Vec<TokenStream>>();
177
178    // let expanded = quote! {};
179
180    // TokenStream::from(expanded)
181    TokenStream::new()
182}
183
184mod sequence;
185
186// // #[cfg(test)]
187// mod tests {
188//     #![allow(unused_imports)]
189//     use crate::{test_builder, SequenceType};
190//     use eager::eager;
191
192//     //compile_error!(
193
194//     //);
195
196//     test_builder!(
197//         @static_method SequenceType => new_arithmetic;
198//         input_types = [
199//             [u8] -> [SequenceType<u8>], [u16] -> [SequenceType<u16>],
200//             [u32] -> [SequenceType<u32>], [u64] -> [SequenceType<u64>],
201//             [u128] -> [SequenceType<u128>], [usize] -> [SequenceType<usize>],
202//             [i8] -> [SequenceType<i8>], [i16] -> [SequenceType<i16>],
203//             [i32] -> [SequenceType<i32>], [i64] -> [SequenceType<i64>],
204//             [i128] -> [SequenceType<i128>], [isize] -> [SequenceType<isize>],
205//             [f32] -> [SequenceType<f32>], [f64] -> [SequenceType<f64>]
206//         ];
207//         [
208//             input_values = [1, 10, 6, 200];
209//             expected_output_values = [
210//                 // 1, 2, 3, 4
211//                 // eager! { instance_of_type!(SequenceType::Arithmetic [1, 10, 6, 200]) }
212//                 SequenceType::Arithmetic { reason: 1 },
213//                 SequenceType::Arithmetic { reason: 10 },
214//                 SequenceType::Arithmetic { reason: 6 },
215//                 SequenceType::Arithmetic { reason: 200 }
216//             ];
217//         ]
218//     );
219// }