summaryrefslogtreecommitdiff
path: root/rola-vcs/src/tools/dir_search.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rola-vcs/src/tools/dir_search.rs')
-rw-r--r--rola-vcs/src/tools/dir_search.rs54
1 files changed, 54 insertions, 0 deletions
diff --git a/rola-vcs/src/tools/dir_search.rs b/rola-vcs/src/tools/dir_search.rs
new file mode 100644
index 0000000..3d32c70
--- /dev/null
+++ b/rola-vcs/src/tools/dir_search.rs
@@ -0,0 +1,54 @@
+use std::path::PathBuf;
+
+pub enum DirSearchPattern<'a> {
+ File(&'a str),
+ Dir(&'a str),
+}
+
+/// Searches upward from the given path towards parent directories.
+/// If any ancestor directory contains a file or directory matching the pattern,
+/// returns that ancestor directory's path.
+pub fn dir_search_prev(path: impl Into<PathBuf>, pattern: DirSearchPattern) -> Option<PathBuf> {
+ let mut current: PathBuf = path.into();
+ // Canonicalize the path if possible to ensure absolute traversal
+ if let Ok(canonical) = current.canonicalize() {
+ current = canonical;
+ } else {
+ // If canonicalization fails (e.g. path does not exist yet),
+ // try to make it absolute using current dir
+ if current.is_relative()
+ && let Ok(cwd) = std::env::current_dir() {
+ current = cwd.join(&current);
+ }
+ }
+
+ loop {
+ // Check if the current directory exists and is a directory
+ if current.is_dir() {
+ let has_match = match &pattern {
+ DirSearchPattern::File(name) => {
+ let mut entry = current.clone();
+ entry.push(name);
+ entry.is_file()
+ }
+ DirSearchPattern::Dir(name) => {
+ let mut entry = current.clone();
+ entry.push(name);
+ entry.is_dir()
+ }
+ };
+
+ if has_match {
+ return Some(current);
+ }
+ }
+
+ // Try to go to the parent directory
+ if !current.pop() {
+ // pop() returns false when there's no parent
+ break;
+ }
+ }
+
+ None
+}