1
use std::{fs::File, path::{Path, PathBuf}};
2

            
3
use log::info;
4
use merc_utilities::MercError;
5

            
6
/// A utility for dumping files, mostly used for testing and debugging
7
/// 
8
/// # Details
9
/// 
10
/// The given name is used to create a dedicated directory for the output files,
11
/// this is especially useful for files dumped from (random) tests. 
12
/// 
13
/// Uses the `MERC_DUMP=1` environment variable to enable or disable dumping files
14
/// to disk, to avoid unnecessary writes during normal runs. In combination with
15
/// `MERC_SEED` we can reproduce specific tests cases for random runs.
16
pub struct DumpFiles {
17
    // None when dumping is disabled.
18
    directory: Option<PathBuf>,
19
}
20

            
21
impl DumpFiles {
22
    /// Creates a new `DumpFiles` instance with the given directory as output.
23
8400
    pub fn new(directory: &str) -> Self {
24
8400
        if let Ok(dump_dir) = std::env::var("MERC_DUMP") {
25
            // Check if the directory is an absolute path
26
            if !Path::new(dump_dir.as_str()).is_absolute() {
27
                panic!("MERC_DUMP must be an absolute path, because tests write relative to their source file.");
28
            }
29

            
30
            Self {
31
                directory: Some(Path::new(&dump_dir).join(directory)),
32
            }
33
        } else {
34
            // Dumping disabled.
35
8400
            Self { directory: None }
36
        }
37
8400
    }
38

            
39
    /// Dumps a file with the given filename suffix by calling the provided function
40
    /// to write the contents.
41
1600
    pub fn dump<F>(&mut self, filename: &str, mut write: F) -> Result<(), MercError>
42
1600
    where
43
1600
        F: FnMut(&mut File) -> Result<(), MercError>,
44
    {
45
1600
        if let Some(directory) = &self.directory {
46
            // Ensure the dump directory exists.
47
            let _ = std::fs::create_dir_all(directory);
48

            
49
            let path = Path::new(&directory).join(filename);
50
            let mut file = File::create(&path)?;
51
            write(&mut file)?;
52

            
53
            info!("Dumped file: {}", path.to_string_lossy());
54
        } else {
55
1600
            info!("No MERC_DUMP set, skipping dump: {}", filename);
56
        }
57
1600
        Ok(())
58
1600
    }
59
}