use quote::{format_ident, quote}; use std::env; use std::fs; use std::path::{Path, PathBuf}; fn generate_test_for_file(path: PathBuf) -> String { let contents = fs::read_to_string(&path).expect("Unable to read input"); let mut concrete_stuff: Option = None; // Start iterating over lines and processing directives.... let mut lines = contents.lines(); while let Some(line) = lines.next() { let line = match line.strip_prefix("//") { Some(line) => line, None => break, }; let line = line.trim(); if line == "concrete:" { let mut concrete = String::new(); while let Some(line) = lines.next() { let line = match line.strip_prefix("// | ") { Some(line) => line, None => break, }; concrete.push_str(line); concrete.push_str("\n"); } concrete_stuff = Some(concrete); } } let concrete_comparison = if let Some(concrete) = concrete_stuff { let display_path = path.display().to_string(); quote! { crate::assert_concrete(&_tree, #concrete, #display_path) } } else { quote! {} }; let name = format_ident!("{}", path.file_stem().unwrap().to_string_lossy()); let test_method = quote! { fn #name() { let (_tree, _lines) = fine::parser::parse(#contents); #concrete_comparison; } }; let syntax_tree = syn::parse2(test_method).unwrap(); prettyplease::unparse(&syntax_tree) } fn process_directory(output: &mut String, path: T) where T: AsRef, { let fine_ext: std::ffi::OsString = "fine".into(); let path = path.as_ref(); for entry in std::fs::read_dir(path).expect("Unable to read directory") { match entry { Ok(dirent) => { let file_type = dirent.file_type().unwrap(); if file_type.is_dir() { let file_name = dirent.file_name(); let file_name = file_name.to_string_lossy().to_owned(); output.push_str(&format!("mod {file_name} {{\n")); process_directory(output, dirent.path()); output.push_str("}\n\n"); } else if file_type.is_file() { if dirent.path().extension() == Some(&fine_ext) { output.push_str(&format!("// {}\n", dirent.path().display())); output.push_str("#[test]\n"); output.push_str(&generate_test_for_file(dirent.path())); output.push_str("\n\n"); } } else { eprintln!("Skipping symlink: {}", path.display()); } } Err(e) => eprintln!("Unable to read directory entry: {:?}", e), } } } fn main() { println!("cargo:rerun-if-changed=./tests"); let mut test_source = String::new(); process_directory(&mut test_source, "./tests"); let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("generated_tests.rs"); fs::write(dest_path, test_source).unwrap(); }