trident_client/
utils.rs

1use crate::error::Error;
2
3use crate::constants::*;
4use fehler::throws;
5use std::path::Path;
6use std::path::PathBuf;
7use tokio::fs;
8
9#[macro_export]
10macro_rules! construct_path {
11    ($root:expr, $($component:expr),*) => {
12        {
13            let mut path = $root.to_owned();
14            $(path = path.join($component);)*
15            path
16        }
17    };
18}
19#[macro_export]
20macro_rules! load_template {
21    ($file:expr) => {
22        include_str!(concat!(env!("CARGO_MANIFEST_DIR"), $file))
23    };
24}
25
26#[throws]
27pub async fn create_directory_all(path: &PathBuf) {
28    match path.exists() {
29        true => {}
30        false => {
31            fs::create_dir_all(path).await?;
32        }
33    };
34}
35
36#[throws]
37pub async fn create_file(root: &PathBuf, path: &PathBuf, content: &str) {
38    let file = path.strip_prefix(root)?.to_str().unwrap_or_default();
39
40    match path.exists() {
41        true => {
42            println!("{SKIP} [{file}] already exists")
43        }
44        false => {
45            fs::write(path, content).await?;
46            println!("{FINISH} [{file}] created");
47        }
48    };
49}
50
51#[throws]
52pub fn get_fuzz_id(fuzz_dir_path: &Path) -> i32 {
53    if fuzz_dir_path.exists() {
54        if fuzz_dir_path.read_dir()?.next().is_none() {
55            0
56        } else {
57            let entries = fuzz_dir_path.read_dir()?;
58            let mut max_num = -1;
59            for entry in entries {
60                let entry = entry?;
61                let file_name = entry.file_name().into_string().unwrap_or_default();
62                if file_name.starts_with("fuzz_") {
63                    let stripped = file_name.strip_prefix("fuzz_").unwrap_or_default();
64                    let num = stripped.parse::<i32>()?;
65                    max_num = max_num.max(num);
66                }
67            }
68            max_num + 1
69        }
70    } else {
71        0
72    }
73}
74
75/// Creates .fuzz-artifacts directory if it doesn't exist
76#[throws]
77pub async fn ensure_fuzz_artifacts_dir() -> PathBuf {
78    let artifacts_dir = PathBuf::from(".fuzz-artifacts");
79    create_directory_all(&artifacts_dir).await?;
80    artifacts_dir
81}
82
83/// Generates a unique filename in .fuzz-artifacts directory
84/// If the base filename already exists, appends a readable timestamp to make it unique
85#[throws]
86pub async fn generate_unique_fuzz_filename(
87    base_name: &str,
88    fuzz_test_name: &str,
89    extension: &str,
90) -> PathBuf {
91    let artifacts_dir = ensure_fuzz_artifacts_dir().await?;
92    let base_filename = format!("{}_{}.{}", base_name, fuzz_test_name, extension);
93    let mut target_path = artifacts_dir.join(&base_filename);
94
95    // If file already exists, append a readable timestamp to make it unique
96    if target_path.exists() {
97        use chrono::DateTime;
98        use chrono::Local;
99
100        // Try different timestamp formats until we find a unique one
101        let now: DateTime<Local> = Local::now();
102
103        // First try: YYYY-MM-DD_HH-MM-SS format
104        let timestamp = now.format("%Y-%m-%d_%H-%M-%S").to_string();
105        let unique_filename = format!(
106            "{}_{}-{}.{}",
107            base_name, fuzz_test_name, timestamp, extension
108        );
109        target_path = artifacts_dir.join(&unique_filename);
110
111        // If that still exists (very unlikely), add milliseconds
112        if target_path.exists() {
113            let timestamp_with_ms = now.format("%Y-%m-%d_%H-%M-%S-%3f").to_string();
114            let unique_filename = format!(
115                "{}_{}-{}.{}",
116                base_name, fuzz_test_name, timestamp_with_ms, extension
117            );
118            target_path = artifacts_dir.join(&unique_filename);
119        }
120    }
121
122    target_path
123}