Skip to content

Commit af6ae1c

Browse files
committed
add util::GlobMatcherExt
1 parent 799e2a8 commit af6ae1c

File tree

5 files changed

+40
-21
lines changed

5 files changed

+40
-21
lines changed

crates/core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ enum-map = { workspace = true }
100100
enum-map-derive = "0.17.0"
101101
enumset = { version = "1.1.5", features = ["serde"] }
102102
gethostname = "0.5.0"
103+
globset = "0.4.15"
103104
humantime = "2.1.0"
104105
itertools = "0.13.0"
105106
quick_cache = "0.6.9"
@@ -124,7 +125,6 @@ xattr = "1"
124125
anyhow = { workspace = true }
125126
expect-test = "1.5.0"
126127
flate2 = "1.0.35"
127-
globset = "0.4.15"
128128
insta = { version = "1.41.1", features = ["redactions", "ron"] }
129129
mockall = "0.13"
130130
pretty_assertions = "1.4.1"

crates/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub(crate) mod progress;
115115
/// Structs which are saved in JSON or binary format in the repository
116116
pub mod repofile;
117117
pub(crate) mod repository;
118+
pub mod util;
118119
/// Virtual File System support - allows to act on the repository like on a file system
119120
pub mod vfs;
120121

crates/core/src/util.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use globset::GlobMatcher;
2+
3+
/// Extend `globset::GlobMatcher` to allow mathing on unix paths (on every platform)
4+
pub trait GlobMatcherExt {
5+
/// Match on unix paths, i.e. paths which are available as `&[u8]`
6+
fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool;
7+
}
8+
9+
impl GlobMatcherExt for GlobMatcher {
10+
// This is a hacky implementation, espeically for windows where we convert lossily
11+
// into an utf8 string and match on the windows path given by that string.
12+
// Note: `GlobMatcher` internally converts into a `&[u8]` to perform the matching
13+
// TODO: Use https://github.com/BurntSushi/ripgrep/pull/2955 once it is available.
14+
#[cfg(not(windows))]
15+
fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool {
16+
use std::{ffi::OsStr, os::unix::ffi::OsStrExt, path::PathBuf};
17+
18+
let path = PathBuf::from(OsStr::from_bytes(path.as_ref()));
19+
self.is_match(&path)
20+
}
21+
#[cfg(windows)]
22+
fn is_unix_match(&self, path: impl AsRef<[u8]>) -> bool {
23+
use std::{ffi::OsStr, path::Path};
24+
25+
let string: &str = &String::from_utf8_lossy(path.as_ref());
26+
let path = Path::new(OsStr::new(string));
27+
self.is_match(path)
28+
}
29+
}

crates/core/tests/integration/find.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
// don't warn about try_from paths conversion on unix
22
#![allow(clippy::unnecessary_fallible_conversions)]
33

4-
use std::{
5-
path::{Path, PathBuf},
6-
str::FromStr,
7-
};
4+
use std::{path::PathBuf, str::FromStr};
85

96
use anyhow::Result;
107
use globset::Glob;
@@ -13,6 +10,7 @@ use rstest::rstest;
1310

1411
use rustic_core::{
1512
repofile::{Node, SnapshotFile},
13+
util::GlobMatcherExt,
1614
BackupOptions, FindMatches, FindNode,
1715
};
1816
use typed_path::UnixPath;
@@ -39,9 +37,8 @@ fn test_find(tar_gz_testdata: Result<TestSource>, set_up_repo: Result<RepoOpen>)
3937
assert_with_win("find-nodes-not-found", not_found);
4038
// test non-existing match
4139
let glob = Glob::new("not_existing")?.compile_matcher();
42-
let not_found = repo.find_matching_nodes(vec![snapshot.tree], &|path, _| {
43-
glob.is_match(PathBuf::try_from(path).unwrap())
44-
})?;
40+
let not_found =
41+
repo.find_matching_nodes(vec![snapshot.tree], &|path, _| glob.is_unix_match(path))?;
4542
assert_debug_snapshot!("find-matching-nodes-not-found", not_found);
4643

4744
// test existing path
@@ -51,21 +48,15 @@ fn test_find(tar_gz_testdata: Result<TestSource>, set_up_repo: Result<RepoOpen>)
5148
// test existing match
5249
let glob = Glob::new("testfile")?.compile_matcher();
5350
let match_func = |path: &UnixPath, _: &Node| {
54-
glob.is_match(PathBuf::try_from(path).unwrap())
55-
|| path
56-
.file_name()
57-
.is_some_and(|f| glob.is_match(Path::new(&String::from_utf8(f.to_vec()).unwrap())))
51+
glob.is_unix_match(path) || path.file_name().is_some_and(|f| glob.is_unix_match(f))
5852
};
5953
let FindMatches { paths, matches, .. } =
6054
repo.find_matching_nodes(vec![snapshot.tree], &match_func)?;
6155
assert_debug_snapshot!("find-matching-existing", (paths, matches));
6256
// test existing match
6357
let glob = Glob::new("testfile*")?.compile_matcher();
6458
let match_func = |path: &UnixPath, _: &Node| {
65-
glob.is_match(PathBuf::try_from(path).unwrap())
66-
|| path
67-
.file_name()
68-
.is_some_and(|f| glob.is_match(Path::new(&String::from_utf8(f.to_vec()).unwrap())))
59+
glob.is_unix_match(path) || path.file_name().is_some_and(|f| glob.is_unix_match(f))
6960
};
7061
let FindMatches { paths, matches, .. } =
7162
repo.find_matching_nodes(vec![snapshot.tree], &match_func)?;

examples/find/examples/find.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! `ls` example
22
use globset::Glob;
33
use rustic_backend::BackendOptions;
4-
use rustic_core::{FindMatches, Repository, RepositoryOptions};
4+
use rustic_core::{util::GlobMatcherExt, FindMatches, Repository, RepositoryOptions};
55
use simplelog::{Config, LevelFilter, SimpleLogger};
6-
use std::{error::Error, path::PathBuf};
6+
use std::error::Error;
77

88
// don't warn about try_from paths conversion on unix
99
#[allow(clippy::unnecessary_fallible_conversions)]
@@ -32,9 +32,7 @@ fn main() -> Result<(), Box<dyn Error>> {
3232
paths,
3333
nodes,
3434
matches,
35-
} = repo.find_matching_nodes(tree_ids, &|path, _| {
36-
glob.is_match(PathBuf::try_from(path).unwrap())
37-
})?;
35+
} = repo.find_matching_nodes(tree_ids, &|path, _| glob.is_unix_match(path))?;
3836
for (snap, matches) in snapshots.iter().zip(matches) {
3937
println!("results in {snap:?}");
4038
for (path_idx, node_idx) in matches {

0 commit comments

Comments
 (0)