use std::{
ffi::OsString,
fs::{DirEntry, File},
io::{BufRead, BufReader, Write},
path::Path,
};
pub const FEATURE_PRETTY_ERRORS: bool = cfg!(feature = "pretty_errors");
pub const FEATURE_MODULE_DISAMBIGUATION: bool = cfg!(feature = "module_disambiguation");
pub const NR_FEATURES: usize =
0 + FEATURE_PRETTY_ERRORS as usize + FEATURE_MODULE_DISAMBIGUATION as usize;
const FEATURES: [&'static str; NR_FEATURES] = get_features();
const fn get_features() -> [&'static str; NR_FEATURES]
{
#[allow(unused_mut)]
let mut features: [&'static str; NR_FEATURES] = [""; NR_FEATURES];
#[cfg(feature = "pretty_errors")]
{
features[0] = "pretty_errors";
}
#[cfg(feature = "module_disambiguation")]
{
features[FEATURE_PRETTY_ERRORS as usize] = "module_disambiguation";
}
features
}
pub struct ExpansionTester<'a>
{
dir: &'a str,
testing_dir: &'a str,
source_dirs: Vec<(&'a str, Vec<Box<dyn Fn(&DirEntry, &dyn AsRef<Path>)>>)>,
}
impl<'a> ExpansionTester<'a>
{
pub fn new(home_dir: &'a str, testing_dir: &'a str) -> Self
{
Self {
dir: home_dir,
testing_dir,
source_dirs: Vec::new(),
}
}
pub fn add_source_dir(
&mut self,
dir: &'a str,
actions: Vec<Box<dyn Fn(&DirEntry, &dyn AsRef<Path>)>>,
)
{
self.source_dirs.push((dir, actions));
}
pub fn execute_tests(&self)
{
let testing_dir = self.dir.to_owned() + "/" + self.testing_dir;
let _ = std::fs::remove_dir_all(&testing_dir);
std::fs::create_dir_all(&testing_dir).unwrap();
for (source_dir, actions) in self.source_dirs.iter()
{
let source_dir_path = self.dir.to_owned() + "/" + source_dir;
if let Ok(files) = std::fs::read_dir(&source_dir_path)
{
for file in files
{
if let Ok(file) = file
{
for action in actions.iter()
{
action(&file, &testing_dir);
}
}
else
{
panic!("Error accessing source file: {:?}", file)
}
}
}
}
let mut args: Vec<&str> = Vec::new();
let mut features = String::new();
if NR_FEATURES > 0
{
args.push("--features");
for f in FEATURES.iter()
{
features.push_str(f);
features.push(',');
}
args.push(features.as_str());
}
macrotest::expand_without_refresh_args(testing_dir + "/*.rs", args.as_slice());
}
pub fn copy_with_prefix(prefix: &str) -> Box<dyn Fn(&DirEntry, &dyn AsRef<Path>)>
{
let prefix = OsString::from(prefix);
Box::new(move |file, destination| {
let mut destination_file = destination.as_ref().to_path_buf();
let mut file_name = prefix.clone();
file_name.push(file.file_name());
destination_file.push(file_name);
std::fs::copy(&file.path(), &destination_file).unwrap();
})
}
pub fn copy() -> Box<dyn Fn(&DirEntry, &dyn AsRef<Path>)>
{
Self::copy_with_prefix("")
}
pub fn duplicate_for_inline() -> Box<dyn Fn(&DirEntry, &dyn AsRef<Path>)>
{
Box::new(|file, destination| {
let mut inline_file_name = OsString::from("inline_");
inline_file_name.push(file.file_name());
let mut dest_file_path = destination.as_ref().to_path_buf();
let mut dest_inline_file_path = destination.as_ref().to_path_buf();
dest_file_path.push(file.file_name());
dest_inline_file_path.push(inline_file_name);
let mut dest_file = File::create(dest_file_path).unwrap();
let mut dest_inline_file = File::create(dest_inline_file_path).unwrap();
for line in BufReader::new(File::open(file.path()).unwrap()).lines()
{
let line = line.unwrap();
let line = line.trim();
match line
{
"#[duplicate::duplicate(" =>
{
dest_file
.write_all("#[duplicate::duplicate(".as_bytes())
.unwrap();
dest_inline_file
.write_all("duplicate::duplicate_inline!{\n[".as_bytes())
.unwrap();
},
")]//duplicate_end" =>
{
dest_file.write_all(")]".as_bytes()).unwrap();
dest_inline_file.write_all("]".as_bytes()).unwrap();
},
"//item_end" =>
{
dest_inline_file.write_all("}".as_bytes()).unwrap();
},
_ =>
{
dest_file.write_all(line.as_bytes()).unwrap();
dest_inline_file.write_all(line.as_bytes()).unwrap();
},
}
dest_file.write_all("\n".as_bytes()).unwrap();
dest_inline_file.write_all("\n".as_bytes()).unwrap();
}
})
}
}