summaryrefslogtreecommitdiffstats
path: root/ignore/src/pathutil.rs
diff options
context:
space:
mode:
authorAndrew Gallant <jamslam@gmail.com>2016-10-11 19:57:09 -0400
committerAndrew Gallant <jamslam@gmail.com>2016-10-29 20:48:59 -0400
commitd79add341ba4be10bb3459877318b9c5a30f5db3 (patch)
treea6c5222c63d53522635bc847c6ac2cf2e000ff7f /ignore/src/pathutil.rs
parent12b2b1f6242e0c9082e93111ffef24a93fea5f6e (diff)
Move all gitignore matching to separate crate.
This PR introduces a new sub-crate, `ignore`, which primarily provides a fast recursive directory iterator that respects ignore files like gitignore and other configurable filtering rules based on globs or even file types. This results in a substantial source of complexity moved out of ripgrep's core and into a reusable component that others can now (hopefully) benefit from. While much of the ignore code carried over from ripgrep's core, a substantial portion of it was rewritten with the following goals in mind: 1. Reuse matchers built from gitignore files across directory iteration. 2. Design the matcher data structure to be amenable for parallelizing directory iteration. (Indeed, writing the parallel iterator is the next step.) Fixes #9, #44, #45
Diffstat (limited to 'ignore/src/pathutil.rs')
-rw-r--r--ignore/src/pathutil.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/ignore/src/pathutil.rs b/ignore/src/pathutil.rs
new file mode 100644
index 00000000..bfd43de3
--- /dev/null
+++ b/ignore/src/pathutil.rs
@@ -0,0 +1,108 @@
+use std::ffi::OsStr;
+use std::path::Path;
+
+/// Returns true if and only if this file path is considered to be hidden.
+#[cfg(unix)]
+pub fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
+ use std::os::unix::ffi::OsStrExt;
+
+ if let Some(name) = file_name(path.as_ref()) {
+ name.as_bytes().get(0) == Some(&b'.')
+ } else {
+ false
+ }
+}
+
+/// Returns true if and only if this file path is considered to be hidden.
+#[cfg(not(unix))]
+pub fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
+ if let Some(name) = file_name(path.as_ref()) {
+ name.to_str().map(|s| s.starts_with(".")).unwrap_or(false)
+ } else {
+ false
+ }
+}
+
+/// Strip `prefix` from the `path` and return the remainder.
+///
+/// If `path` doesn't have a prefix `prefix`, then return `None`.
+#[cfg(unix)]
+pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
+ prefix: &'a P,
+ path: &'a Path,
+) -> Option<&'a Path> {
+ use std::os::unix::ffi::OsStrExt;
+
+ let prefix = prefix.as_ref().as_os_str().as_bytes();
+ let path = path.as_os_str().as_bytes();
+ if prefix.len() > path.len() || prefix != &path[0..prefix.len()] {
+ None
+ } else {
+ Some(&Path::new(OsStr::from_bytes(&path[prefix.len()..])))
+ }
+}
+
+/// Strip `prefix` from the `path` and return the remainder.
+///
+/// If `path` doesn't have a prefix `prefix`, then return `None`.
+#[cfg(not(unix))]
+pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
+ prefix: &'a P,
+ path: &'a Path,
+) -> Option<&'a Path> {
+ path.strip_prefix(prefix).ok()
+}
+
+/// Returns true if this file path is just a file name. i.e., Its parent is
+/// the empty string.
+#[cfg(unix)]
+pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
+ use std::os::unix::ffi::OsStrExt;
+ use memchr::memchr;
+
+ let path = path.as_ref().as_os_str().as_bytes();
+ memchr(b'/', path).is_none()
+}
+
+/// Returns true if this file path is just a file name. i.e., Its parent is
+/// the empty string.
+#[cfg(not(unix))]
+pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
+ path.as_ref().parent().map(|p| p.as_os_str().is_empty()).unwrap_or(false)
+}
+
+/// The final component of the path, if it is a normal file.
+///
+/// If the path terminates in ., .., or consists solely of a root of prefix,
+/// file_name will return None.
+#[cfg(unix)]
+pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
+ path: &'a P,
+) -> Option<&'a OsStr> {
+ use std::os::unix::ffi::OsStrExt;
+ use memchr::memrchr;
+
+ let path = path.as_ref().as_os_str().as_bytes();
+ if path.is_empty() {
+ return None;
+ } else if path.len() == 1 && path[0] == b'.' {
+ return None;
+ } else if path.last() == Some(&b'.') {
+ return None;
+ } else if path.len() >= 2 && &path[path.len() - 2..] == &b".."[..] {
+ return None;
+ }
+ let last_slash = memrchr(b'/', path).map(|i| i + 1).unwrap_or(0);
+ Some(OsStr::from_bytes(&path[last_slash..]))
+}
+
+/// The final component of the path, if it is a normal file.
+///
+/// If the path terminates in ., .., or consists solely of a root of prefix,
+/// file_name will return None.
+#[cfg(not(unix))]
+pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
+ path: &'a P,
+) -> Option<&'a OsStr> {
+ path.as_ref().file_name()
+}