Skip to content

Commit 0531037

Browse files
glob-tilde-expansion #149
1 parent 952da29 commit 0531037

File tree

2 files changed

+67
-11
lines changed

2 files changed

+67
-11
lines changed

src/lib.rs

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ extern crate doc_comment;
7070
#[cfg(test)]
7171
doctest!("../README.md");
7272

73+
mod utils;
74+
7375
use std::cmp;
7476
use std::cmp::Ordering;
7577
use std::error::Error;
@@ -179,6 +181,7 @@ pub fn glob(pattern: &str) -> Result<Paths, PatternError> {
179181
///
180182
/// Paths are yielded in alphabetical order.
181183
pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternError> {
184+
use utils::{get_home_dir, get_user_name};
182185
#[cfg(windows)]
183186
fn check_windows_verbatim(p: &Path) -> bool {
184187
match p.components().next() {
@@ -211,7 +214,34 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternE
211214

212215
// make sure that the pattern is valid first, else early return with error
213216
let _ = Pattern::new(pattern)?;
214-
217+
let mut new_pattern = pattern.to_owned();
218+
if options.glob_tilde_expansion {
219+
let home_dir = get_home_dir();
220+
if pattern == "~" || pattern.starts_with("~/") {
221+
if let Some(home) = home_dir {
222+
new_pattern = pattern.replacen("~", &home, 1);
223+
}
224+
} else if pattern.starts_with("~") {
225+
if let Some(user) = get_user_name() {
226+
match pattern.strip_prefix("~").unwrap().strip_prefix(&user) {
227+
Some(v) if v.starts_with("/") || v.is_empty() => {
228+
if let Some(mut v) = home_dir {
229+
v.push_str(
230+
pattern
231+
.strip_prefix("~")
232+
.unwrap()
233+
.strip_prefix(&user)
234+
.unwrap(),
235+
);
236+
new_pattern = v;
237+
}
238+
}
239+
_ => {}
240+
};
241+
}
242+
}
243+
}
244+
let pattern = new_pattern.as_str();
215245
let mut components = Path::new(pattern).components().peekable();
216246
loop {
217247
match components.peek() {
@@ -1050,7 +1080,7 @@ fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
10501080

10511081
/// Configuration options to modify the behaviour of `Pattern::matches_with(..)`.
10521082
#[allow(missing_copy_implementations)]
1053-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1083+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10541084
pub struct MatchOptions {
10551085
/// Whether or not patterns should be matched in a case-sensitive manner.
10561086
/// This currently only considers upper/lower case relationships between
@@ -1069,6 +1099,21 @@ pub struct MatchOptions {
10691099
/// conventionally considered hidden on Unix systems and it might be
10701100
/// desirable to skip them when listing files.
10711101
pub require_literal_leading_dot: bool,
1102+
1103+
/// Whether or not tilde expansion should be performed. if home directory
1104+
/// or user name cannot be determined, then no tilde expansion is performed.
1105+
pub glob_tilde_expansion: bool,
1106+
}
1107+
1108+
impl Default for MatchOptions {
1109+
fn default() -> Self {
1110+
Self {
1111+
case_sensitive: true,
1112+
require_literal_separator: false,
1113+
require_literal_leading_dot: false,
1114+
glob_tilde_expansion: false,
1115+
}
1116+
}
10721117
}
10731118

10741119
impl MatchOptions {
@@ -1086,16 +1131,13 @@ impl MatchOptions {
10861131
/// }
10871132
/// ```
10881133
///
1089-
/// # Note
1090-
/// The behavior of this method doesn't match `default()`'s. This returns
1091-
/// `case_sensitive` as `true` while `default()` does it as `false`.
1092-
// FIXME: Consider unity the behavior with `default()` in a next major release.
10931134
pub fn new() -> Self {
1094-
Self {
1095-
case_sensitive: true,
1096-
require_literal_separator: false,
1097-
require_literal_leading_dot: false,
1098-
}
1135+
Self::default()
1136+
}
1137+
/// Enables or disables tilde (`~`) expansion in glob patterns
1138+
pub fn glob_tilde_expansion(mut self, v: bool) -> Self {
1139+
self.glob_tilde_expansion = v;
1140+
self
10991141
}
11001142
}
11011143

src/utils.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#[inline(always)]
2+
pub(crate) fn get_home_dir() -> Option<String> {
3+
std::env::home_dir().map(|v| v.to_string_lossy().to_string())
4+
}
5+
6+
// This function is required when `glob_tilde_expansion` field of `glob::MatchOptions` is
7+
// set `true` and pattern starts with `~` ollowed by any char expect `/`
8+
pub(crate) fn get_user_name() -> Option<String> {
9+
if cfg!(target_os = "linux") {
10+
std::env::var("USER").ok()
11+
} else {
12+
None
13+
}
14+
}

0 commit comments

Comments
 (0)