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 println!("{:#?}", &value);
85 value
86 .functions
87 .into_iter()
88 .map(|function| function.sig)
89 .map(|mut signature| {
90 let name = signature.ident.clone();
91 let mut generics = get_generics(signature.generics.clone());
92 generics.append(&mut value.generics);
93 let return_type = signature.output.clone();
94
95 if let Some(self_type) = signature.receiver() {
96 TestedMethod::InstanceMethod {
97 name,
98 generics,
99 self_type: self_type.clone(),
100 args: {
101 signature.inputs.pop();
102 signature
103 .inputs
104 .clone()
105 .into_iter()
106 .map(|arg| {
107 if let FnArg::Receiver(_) = arg {
108 Err(signature.span().unwrap().error(
109 "An instance method cannot have 2 or more self parameters",
110 ))
111 } else if let FnArg::Typed(arg_type) = arg {
112 Ok(arg_type)
113 } else {
114 unreachable!(
115 "Fnarg is either `Receiver` or `Typed`, not something else"
116 );
117 }
118 })
119 .collect()
120 },
121 return_type,
122 }
123 } else {
124 TestedMethod::StaticMethod {
125 name,
126 generics,
127 args: signature
128 .inputs
129 .clone()
130 .into_iter()
131 .map(|arg| {
132 if let FnArg::Typed(arg_type) = arg {
133 Ok(arg_type)
134 } else {
135 Err(signature
136 .span()
137 .unwrap()
138 .error("Cannot have a self type in a static method."))
139 }
140 })
141 .collect(),
142 return_type,
143 }
144 }
145 })
146 .collect()
147 }
148}
149
150#[proc_macro_attribute]
151pub fn test_builder(attr: TokenStream, item: TokenStream) -> TokenStream {
152 let all_method: Vec<TestedMethod> = parse_macro_input!(item as TestedFns).into();
153
154 println!("{all_method:#?}");
155
156 // let all_expanded = all_method
157 // .iter()
158 // .map(|tested_method| match tested_method {
159 // TestedMethod::StaticMethod {
160 // name,
161 // generics,
162 // args,
163 // return_type,
164 // } => {
165 // let test_method_name = quote! {
166 // test_for_ #name _for_type_
167 // };
168 // }
169 // TestedMethod::InstanceMethod {
170 // name,
171 // generics,
172 // self_type,
173 // args,
174 // return_type,
175 // } => todo!(),
176 // })
177 // .collect::<Vec<TokenStream>>();
178
179 // let expanded = quote! {};
180
181 // TokenStream::from(expanded)
182 TokenStream::new()
183}
184
185mod sequence;
186
187// // #[cfg(test)]
188// mod tests {
189// #![allow(unused_imports)]
190// use crate::{test_builder, SequenceType};
191// use eager::eager;
192
193// //compile_error!(
194
195// //);
196
197// test_builder!(
198// @static_method SequenceType => new_arithmetic;
199// input_types = [
200// [u8] -> [SequenceType<u8>], [u16] -> [SequenceType<u16>],
201// [u32] -> [SequenceType<u32>], [u64] -> [SequenceType<u64>],
202// [u128] -> [SequenceType<u128>], [usize] -> [SequenceType<usize>],
203// [i8] -> [SequenceType<i8>], [i16] -> [SequenceType<i16>],
204// [i32] -> [SequenceType<i32>], [i64] -> [SequenceType<i64>],
205// [i128] -> [SequenceType<i128>], [isize] -> [SequenceType<isize>],
206// [f32] -> [SequenceType<f32>], [f64] -> [SequenceType<f64>]
207// ];
208// [
209// input_values = [1, 10, 6, 200];
210// expected_output_values = [
211// // 1, 2, 3, 4
212// // eager! { instance_of_type!(SequenceType::Arithmetic [1, 10, 6, 200]) }
213// SequenceType::Arithmetic { reason: 1 },
214// SequenceType::Arithmetic { reason: 10 },
215// SequenceType::Arithmetic { reason: 6 },
216// SequenceType::Arithmetic { reason: 200 }
217// ];
218// ]
219// );
220// }