diff options
author | gennyble <gen@nyble.dev> | 2023-08-24 18:21:45 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2023-08-24 18:21:45 -0500 |
commit | e79a61a9372db9e52f8268ce58e8af677d48a348 (patch) | |
tree | fb65724f1a843f5abffe7fffe38e778e2cf32cea | |
download | whenwasit-e79a61a9372db9e52f8268ce58e8af677d48a348.tar.gz whenwasit-e79a61a9372db9e52f8268ce58e8af677d48a348.zip |
works pretty not bad
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .rustfmt.toml | 1 | ||||
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | Cargo.toml | 9 | ||||
-rw-r--r-- | readme.md | 2 | ||||
-rw-r--r-- | src/main.rs | 126 |
6 files changed, 155 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6616951 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" + +[[package]] +name = "whenwasit" +version = "0.1.0" +dependencies = [ + "camino", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5bdc231 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "whenwasit" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +camino = "1.1.6" diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..50c4119 --- /dev/null +++ b/readme.md @@ -0,0 +1,2 @@ +little thing meant to be called in a git-hook as to keep track of +file creation and modification dates, which are important to me. \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b62247e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,126 @@ +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<P: AsRef<Utf8Path>>(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<S: AsRef<str>>(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<SystemTime>, + modified: Option<SystemTime>, + accessed: Option<SystemTime>, +} + +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<Duration> { + self.created + .map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) + } + + pub fn modified(&self) -> Option<Duration> { + self.modified + .map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) + } + + pub fn accessed(&self) -> Option<Duration> { + self.accessed + .map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) + } +} |