use std::{ fs::Metadata, time::{Duration, SystemTime}, }; use camino::{Utf8Path, Utf8PathBuf}; // CSV definition: // a row may contain any data, but comma are escaped with a backslash // and blackslash are escaped, too. there is no header. if a time // cannot be attained due to an error, it is left blank // data is in the format: path, creation-time, modification-time fn main() { // currently only accepts one argument, the path to the directory it will // recurse through and make the csv for. // spits the csv on stdout let path: Utf8PathBuf = std::env::args().nth(1).unwrap().parse().unwrap(); // behavior: // reads directories twice. we're trying to be memory efficient rather than // computationally. i'm not quite sure why. // first pass: read and output file information // second pass: read and outut directory information process(path) } fn process(path: Utf8PathBuf) { //TODO: do not panic, please let root_meta = std::fs::metadata(&path).unwrap(); let root_times = Times::metadata(&root_meta); row(&path, &root_times); for entry in path.read_dir_utf8().unwrap() { let entry = entry.unwrap(); match entry.file_type() { Err(_) => panic!(), Ok(ft) if ft.is_file() => { let meta = entry.metadata().unwrap(); let times = Times::metadata(&meta); row(entry.path(), ×) } Ok(_) => {} } } for entry in path.read_dir_utf8().unwrap() { let entry = entry.unwrap(); match entry.file_type() { Err(_) => panic!(), Ok(ft) if ft.is_dir() => process(entry.into_path()), Ok(_) => {} } } } fn row>(path: P, times: &Times) { println!( "{},{},{},{}", escape(path.as_ref()), times .created() .map(|d| d.as_secs().to_string()) .unwrap_or_default(), times .modified() .map(|d| d.as_secs().to_string()) .unwrap_or_default(), times .accessed() .map(|d| d.as_secs().to_string()) .unwrap_or_default() ); } fn escape>(raw: S) -> String { let raw = raw.as_ref(); let mut escaped = String::with_capacity(raw.len()); for c in raw.chars() { match c { ',' | '\\' => { escaped.push('\\'); escaped.push(c); } _ => escaped.push(c), } } escaped } struct Times { created: Option, modified: Option, accessed: Option, } impl Times { pub fn metadata(meta: &Metadata) -> Self { Self { created: meta.created().ok(), modified: meta.modified().ok(), accessed: meta.accessed().ok(), } } // EPOCH created pub fn created(&self) -> Option { self.created .map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) } pub fn modified(&self) -> Option { self.modified .map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) } pub fn accessed(&self) -> Option { self.accessed .map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) } }