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// }