From 053103715e17129d93ac7bd5167043dd9ee407a7 Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Wed, 13 Aug 2025 08:16:57 +0530 Subject: [PATCH 01/13] glob-tilde-expansion #149 --- src/lib.rs | 64 +++++++++++++++++++++++++++++++++++++++++++--------- src/utils.rs | 14 ++++++++++++ 2 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/utils.rs diff --git a/src/lib.rs b/src/lib.rs index 133a17a..0ea4ea7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,8 @@ extern crate doc_comment; #[cfg(test)] doctest!("../README.md"); +mod utils; + use std::cmp; use std::cmp::Ordering; use std::error::Error; @@ -179,6 +181,7 @@ pub fn glob(pattern: &str) -> Result { /// /// Paths are yielded in alphabetical order. pub fn glob_with(pattern: &str, options: MatchOptions) -> Result { + use utils::{get_home_dir, get_user_name}; #[cfg(windows)] fn check_windows_verbatim(p: &Path) -> bool { match p.components().next() { @@ -211,7 +214,34 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result { + if let Some(mut v) = home_dir { + v.push_str( + pattern + .strip_prefix("~") + .unwrap() + .strip_prefix(&user) + .unwrap(), + ); + new_pattern = v; + } + } + _ => {} + }; + } + } + } + let pattern = new_pattern.as_str(); let mut components = Path::new(pattern).components().peekable(); loop { match components.peek() { @@ -1050,7 +1080,7 @@ fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { /// Configuration options to modify the behaviour of `Pattern::matches_with(..)`. #[allow(missing_copy_implementations)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MatchOptions { /// Whether or not patterns should be matched in a case-sensitive manner. /// This currently only considers upper/lower case relationships between @@ -1069,6 +1099,21 @@ pub struct MatchOptions { /// conventionally considered hidden on Unix systems and it might be /// desirable to skip them when listing files. pub require_literal_leading_dot: bool, + + /// Whether or not tilde expansion should be performed. if home directory + /// or user name cannot be determined, then no tilde expansion is performed. + pub glob_tilde_expansion: bool, +} + +impl Default for MatchOptions { + fn default() -> Self { + Self { + case_sensitive: true, + require_literal_separator: false, + require_literal_leading_dot: false, + glob_tilde_expansion: false, + } + } } impl MatchOptions { @@ -1086,16 +1131,13 @@ impl MatchOptions { /// } /// ``` /// - /// # Note - /// The behavior of this method doesn't match `default()`'s. This returns - /// `case_sensitive` as `true` while `default()` does it as `false`. - // FIXME: Consider unity the behavior with `default()` in a next major release. pub fn new() -> Self { - Self { - case_sensitive: true, - require_literal_separator: false, - require_literal_leading_dot: false, - } + Self::default() + } + /// Enables or disables tilde (`~`) expansion in glob patterns + pub fn glob_tilde_expansion(mut self, v: bool) -> Self { + self.glob_tilde_expansion = v; + self } } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..2570d41 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,14 @@ +#[inline(always)] +pub(crate) fn get_home_dir() -> Option { + std::env::home_dir().map(|v| v.to_string_lossy().to_string()) +} + +// This function is required when `glob_tilde_expansion` field of `glob::MatchOptions` is +// set `true` and pattern starts with `~` ollowed by any char expect `/` +pub(crate) fn get_user_name() -> Option { + if cfg!(target_os = "linux") { + std::env::var("USER").ok() + } else { + None + } +} From d047d76025143625a73f953e7988e6ee2d464630 Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Wed, 13 Aug 2025 10:15:54 +0530 Subject: [PATCH 02/13] fix: comment --- src/lib.rs | 5 ++++- src/utils.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0ea4ea7..2ed09f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1128,9 +1128,12 @@ impl MatchOptions { /// case_sensitive: true, /// require_literal_separator: false, /// require_literal_leading_dot: false + /// glob_tilde_expansion: false, /// } /// ``` - /// + /// # Note + /// The behavior of this method doesn't match `default()`'s. This returns + /// `case_sensitive` as `true` while `default()` does it as `false`. pub fn new() -> Self { Self::default() } diff --git a/src/utils.rs b/src/utils.rs index 2570d41..78dacd9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,7 +4,7 @@ pub(crate) fn get_home_dir() -> Option { } // This function is required when `glob_tilde_expansion` field of `glob::MatchOptions` is -// set `true` and pattern starts with `~` ollowed by any char expect `/` +// set `true` and pattern starts with `~` followed by any char expect `/` pub(crate) fn get_user_name() -> Option { if cfg!(target_os = "linux") { std::env::var("USER").ok() From 86419239fb661e3138573cc6ec81541a3a6dbe39 Mon Sep 17 00:00:00 2001 From: Biswajit Thakur <63996316+BiswajitThakur@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:29:02 +0530 Subject: [PATCH 03/13] Update src/utils.rs Co-authored-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> --- src/utils.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index 78dacd9..105c1bf 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,9 +6,8 @@ pub(crate) fn get_home_dir() -> Option { // This function is required when `glob_tilde_expansion` field of `glob::MatchOptions` is // set `true` and pattern starts with `~` followed by any char expect `/` pub(crate) fn get_user_name() -> Option { - if cfg!(target_os = "linux") { - std::env::var("USER").ok() - } else { - None - } + #[cfg(not(target_os = "windows"))] + return std::env::var("USER").ok(); + #[cfg(target_os = "windows")] + std::env::var("USERNAME").ok() } From ddeeec64af060ded3b3658c2ee7cb058f19f679f Mon Sep 17 00:00:00 2001 From: Biswajit Thakur <63996316+BiswajitThakur@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:31:13 +0530 Subject: [PATCH 04/13] Update src/utils.rs Co-authored-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 105c1bf..4fdac3c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,6 @@ #[inline(always)] pub(crate) fn get_home_dir() -> Option { - std::env::home_dir().map(|v| v.to_string_lossy().to_string()) + std::env::home_dir().and_then(|v| v.to_str().map(String::from)) } // This function is required when `glob_tilde_expansion` field of `glob::MatchOptions` is From fabeb160ec9e1c0425f4faca71407351af924d5a Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Wed, 13 Aug 2025 22:09:27 +0530 Subject: [PATCH 05/13] refactor: remove unnecessary fn call --- src/lib.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2ed09f9..2912291 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -225,15 +225,9 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result { - if let Some(mut v) = home_dir { - v.push_str( - pattern - .strip_prefix("~") - .unwrap() - .strip_prefix(&user) - .unwrap(), - ); - new_pattern = v; + if let Some(mut p) = home_dir { + p.push_str(v); + new_pattern = p; } } _ => {} From 3e8799b4268f9ba3f3d6096e34495c6a14da68b5 Mon Sep 17 00:00:00 2001 From: Biswajit Thakur <63996316+BiswajitThakur@users.noreply.github.com> Date: Wed, 13 Aug 2025 22:41:08 +0530 Subject: [PATCH 06/13] Update src/utils.rs Co-authored-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 4fdac3c..0b623ca 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -7,7 +7,7 @@ pub(crate) fn get_home_dir() -> Option { // set `true` and pattern starts with `~` followed by any char expect `/` pub(crate) fn get_user_name() -> Option { #[cfg(not(target_os = "windows"))] - return std::env::var("USER").ok(); + { std::env::var("USER").ok() } #[cfg(target_os = "windows")] std::env::var("USERNAME").ok() } From e535db6b0e4a23965c0fa541b7b5493f6195f2ec Mon Sep 17 00:00:00 2001 From: Biswajit Thakur <63996316+BiswajitThakur@users.noreply.github.com> Date: Thu, 14 Aug 2025 08:02:29 +0530 Subject: [PATCH 07/13] Update src/utils.rs Co-authored-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 0b623ca..4fdac3c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -7,7 +7,7 @@ pub(crate) fn get_home_dir() -> Option { // set `true` and pattern starts with `~` followed by any char expect `/` pub(crate) fn get_user_name() -> Option { #[cfg(not(target_os = "windows"))] - { std::env::var("USER").ok() } + return std::env::var("USER").ok(); #[cfg(target_os = "windows")] std::env::var("USERNAME").ok() } From d5dd7a8c0565f82432ed019f6074976b12f0096d Mon Sep 17 00:00:00 2001 From: Biswajit Thakur <63996316+BiswajitThakur@users.noreply.github.com> Date: Thu, 14 Aug 2025 09:11:56 +0530 Subject: [PATCH 08/13] Apply suggestion from @reneleonhardt Co-authored-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> --- src/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.rs b/src/utils.rs index 4fdac3c..c954460 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,6 @@ #[inline(always)] pub(crate) fn get_home_dir() -> Option { + #[allow(deprecated)] std::env::home_dir().and_then(|v| v.to_str().map(String::from)) } From be3ae79194dcfc9bc2ea5a02fb195823aa4880df Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Thu, 14 Aug 2025 09:18:45 +0530 Subject: [PATCH 09/13] removed-glob_tilde_expansion-method-from-MatcherOption --- src/lib.rs | 5 ----- src/main.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 src/main.rs diff --git a/src/lib.rs b/src/lib.rs index 2912291..44c66de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1131,11 +1131,6 @@ impl MatchOptions { pub fn new() -> Self { Self::default() } - /// Enables or disables tilde (`~`) expansion in glob patterns - pub fn glob_tilde_expansion(mut self, v: bool) -> Self { - self.glob_tilde_expansion = v; - self - } } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3d7813d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,9 @@ +extern crate glob; +use glob::{glob_with, MatchOptions}; + +fn main() { + let options = MatchOptions::new().glob_tilde_expansion(true); + for v in glob_with("~eagle/*", options).unwrap() { + println!("> {:?}", v.unwrap()); + } +} From 498392c4263ebbda456db1b8ddb3bf38c91365c6 Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Thu, 14 Aug 2025 09:20:30 +0530 Subject: [PATCH 10/13] . --- src/main.rs | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/main.rs diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 3d7813d..0000000 --- a/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -extern crate glob; -use glob::{glob_with, MatchOptions}; - -fn main() { - let options = MatchOptions::new().glob_tilde_expansion(true); - for v in glob_with("~eagle/*", options).unwrap() { - println!("> {:?}", v.unwrap()); - } -} From 63f96b3be23d6a49485752e16ef3a56cc8061619 Mon Sep 17 00:00:00 2001 From: Biswajit Thakur <63996316+BiswajitThakur@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:29:33 +0530 Subject: [PATCH 11/13] Update src/utils.rs Co-authored-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index c954460..e6aaa89 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,6 @@ #[inline(always)] pub(crate) fn get_home_dir() -> Option { - #[allow(deprecated)] + #[allow(deprecated)] std::env::home_dir().and_then(|v| v.to_str().map(String::from)) } From 0cb91c6bfd09ec700a7cdcfea8dacd004ed9c868 Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Fri, 15 Aug 2025 10:36:16 +0530 Subject: [PATCH 12/13] return error if ~ followed by not / or not user name or failed to determine home dir or user name --- src/lib.rs | 36 +++++++++++++++++++++++++----------- src/utils.rs | 6 ++---- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 44c66de..c51a371 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -215,23 +215,37 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result v, + None => { + return Err(PatternError { + pos: 0, + msg: "Failed to determine home directory.", + }) } - } else if pattern.starts_with("~") { + }; + if pattern == "~" || pattern.starts_with("~/") { + new_pattern = pattern.replacen("~", &home_dir, 1); + } else { if let Some(user) = get_user_name() { match pattern.strip_prefix("~").unwrap().strip_prefix(&user) { Some(v) if v.starts_with("/") || v.is_empty() => { - if let Some(mut p) = home_dir { - p.push_str(v); - new_pattern = p; - } + home_dir.push_str(v); + new_pattern = home_dir; + } + _ => { + return Err(PatternError { + pos: 1, + msg: "'~' not followed by user name.", + }) } - _ => {} }; + } else { + return Err(PatternError { + pos: 1, + msg: "Failed to determine user name.", + }); } } } diff --git a/src/utils.rs b/src/utils.rs index e6aaa89..3eb238e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -7,8 +7,6 @@ pub(crate) fn get_home_dir() -> Option { // This function is required when `glob_tilde_expansion` field of `glob::MatchOptions` is // set `true` and pattern starts with `~` followed by any char expect `/` pub(crate) fn get_user_name() -> Option { - #[cfg(not(target_os = "windows"))] - return std::env::var("USER").ok(); - #[cfg(target_os = "windows")] - std::env::var("USERNAME").ok() + let varname = if cfg!(windows) { "USERNAME" } else { "USER" }; + std::env::var(varname).ok() } From 589787a0d366f9bef6dcdfdcf23f571ad3c68f0a Mon Sep 17 00:00:00 2001 From: BiswajitThakur Date: Fri, 15 Aug 2025 10:57:31 +0530 Subject: [PATCH 13/13] fix: err msg --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c51a371..402a6ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,7 +237,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result { return Err(PatternError { pos: 1, - msg: "'~' not followed by user name.", + msg: "'~' not followed by '/' or user name.", }) } };