about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2023-08-24 21:45:25 -0500
committergennyble <gen@nyble.dev>2023-08-24 21:45:25 -0500
commit544aae00aa8d589a2b2817373611d21aa70eec0e (patch)
tree7cd78a83c8370284efc1554ceb2fd062bf6fcda0
parent96ba35059a31dcf12f78587ee76bdea7262fe16b (diff)
downloadwhenwasit-544aae00aa8d589a2b2817373611d21aa70eec0e.tar.gz
whenwasit-544aae00aa8d589a2b2817373611d21aa70eec0e.zip
whenwasignore?
-rw-r--r--.whenwasit-ignore5
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml1
-rw-r--r--readme.md9
-rw-r--r--src/main.rs114
5 files changed, 134 insertions, 11 deletions
diff --git a/.whenwasit-ignore b/.whenwasit-ignore
new file mode 100644
index 0000000..ad1130c
--- /dev/null
+++ b/.whenwasit-ignore
@@ -0,0 +1,5 @@
+# it's line a .gitignore but it's dumber.
+
+# ignore the top-level target and .git directories.
+target
+.git
diff --git a/Cargo.lock b/Cargo.lock
index 6616951..3aa373f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,8 +9,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
 
 [[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
 name = "whenwasit"
 version = "0.1.0"
 dependencies = [
  "camino",
+ "getopts",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 5bdc231..351971e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,4 @@ edition = "2021"
 
 [dependencies]
 camino = "1.1.6"
+getopts = "0.2.21"
diff --git a/readme.md b/readme.md
index 5345195..734e81f 100644
--- a/readme.md
+++ b/readme.md
@@ -1,8 +1,15 @@
-`whenwasit path`  
+```
+Usage: whenwasit [options] PATH
+```
+
 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.
 dumps csv output on stdout, errors abort with a panic.
 
+You can use the flag `--ignore` to stop it from descending into
+directories. The flag takes a path to a file that contains a list
+of directory to ignore, relative to the path given to whenwasit.
+
 **CSV Output Defintion:**  
 data is output on `stdout` in a CSV format. because there are
 varying ways to make a CSV, it's described in detail below.
diff --git a/src/main.rs b/src/main.rs
index 0dc9899..0d560e6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,28 +1,110 @@
 use std::{
-	fs::Metadata,
+	fs::{File, Metadata},
+	io::{BufRead, BufReader},
 	time::{Duration, SystemTime},
 };
 
 use camino::{Utf8Path, Utf8PathBuf};
+use getopts::Options;
+
+struct Context {
+	root: Utf8PathBuf,
+	ignores: Vec<String>,
+}
+
+impl Context {
+	pub fn new(root: Utf8PathBuf) -> Self {
+		Self {
+			root,
+			ignores: vec![],
+		}
+	}
+
+	pub fn ignore_file(&mut self, ignore_path: Utf8PathBuf) {
+		let file = File::open(ignore_path).unwrap();
+		let mut bufread = BufReader::new(file);
+
+		let mut line = String::new();
+		loop {
+			if bufread.read_line(&mut line).unwrap() == 0 {
+				break;
+			}
+
+			if line.starts_with("\\#") || line.starts_with("/") {
+				self.ignores.push(String::from((&line[1..]).trim()));
+			} else if line.starts_with("#") || line.is_empty() || line.trim().is_empty() {
+				()
+			} else {
+				self.ignores.push(line.trim().to_owned());
+			}
+
+			line.clear();
+		}
+	}
+
+	pub fn is_file_ignored<P: AsRef<Utf8Path>>(&self, path: P) -> bool {
+		match path.as_ref().strip_prefix(&self.root) {
+			Ok(rel) => {
+				for ignore in &self.ignores {
+					if rel.starts_with(ignore) {
+						return true;
+					}
+				}
+
+				false
+			}
+			Err(_) => false,
+		}
+	}
+}
 
 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 mut opts = Options::new();
+	#[rustfmt::skip]
+	opts.optopt("", "ignore", "file of paths to ignore, one per line", "PATH");
+	opts.optflag("h", "help", "print this help thing");
+
+	let matches = match opts.parse(std::env::args()) {
+		Ok(m) => m,
+		Err(e) => {
+			eprintln!("{e}");
+			return;
+		}
+	};
+
+	if matches.opt_present("h") {
+		println!("{}", opts.usage("Usage: whenwasit [options] PATH"));
+		return;
+	}
 
-	let path: Utf8PathBuf = std::env::args().nth(1).unwrap().parse().unwrap();
-	process(&path, &path)
+	let root = match matches.free.get(1) {
+		None => {
+			println!("expected path");
+			println!("{}", opts.usage("Usage: whenwasit [options] PATH"));
+			return;
+		}
+		Some(p) => Utf8PathBuf::from(p),
+	};
+
+	let mut context = Context::new(root);
+
+	if let Some(ignore) = matches.opt_str("ignore") {
+		let ignore = Utf8PathBuf::from(ignore);
+		context.ignore_file(ignore);
+	}
+
+	process(&context, &context.root)
 }
 
 /// Loop through the provided directory printing CSV rows. The `root_path` is
 /// used to make printed paths relative to it.
 ///
 /// Does two passes: First pass prints files. Second pass recurs, printing directories.
-fn process(root_path: &Utf8Path, path: &Utf8Path) {
+fn process(ctx: &Context, path: &Utf8Path) {
 	//TODO: do not panic, please
 	let this_meta = std::fs::metadata(&path).unwrap();
 	let this_times = Times::metadata(&this_meta);
-	row(root_path, &path, &this_times);
+	row(&ctx.root, &path, &this_times);
 
 	for entry in path.read_dir_utf8().unwrap() {
 		let entry = entry.unwrap();
@@ -30,9 +112,14 @@ fn process(root_path: &Utf8Path, path: &Utf8Path) {
 		match entry.file_type() {
 			Err(_) => panic!(),
 			Ok(ft) if ft.is_file() => {
+				let path = entry.path();
+				if ctx.is_file_ignored(path) {
+					continue;
+				}
+
 				let meta = entry.metadata().unwrap();
 				let times = Times::metadata(&meta);
-				row(root_path, entry.path(), &times)
+				row(&ctx.root, path, &times)
 			}
 			Ok(_) => {}
 		}
@@ -43,7 +130,14 @@ fn process(root_path: &Utf8Path, path: &Utf8Path) {
 
 		match entry.file_type() {
 			Err(_) => panic!(),
-			Ok(ft) if ft.is_dir() => process(root_path, entry.path()),
+			Ok(ft) if ft.is_dir() => {
+				let path = entry.path();
+				if ctx.is_file_ignored(path) {
+					continue;
+				}
+
+				process(ctx, entry.path())
+			}
 			Ok(_) => {}
 		}
 	}