Skip to content

Commit 7591f37

Browse files
committed
Use gix_fs::stack::ToNormalPathComponents everywhere.
1 parent 569c186 commit 7591f37

File tree

15 files changed

+110
-71
lines changed

15 files changed

+110
-71
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gitoxide-core/src/repository/attributes/query.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ pub struct Options {
88
}
99

1010
pub(crate) mod function {
11-
use std::{borrow::Cow, io, path::Path};
12-
1311
use anyhow::bail;
1412
use gix::bstr::BStr;
13+
use std::borrow::Cow;
14+
use std::{io, path::Path};
1515

1616
use crate::{
1717
is_dir_to_mode,
@@ -44,7 +44,7 @@ pub(crate) mod function {
4444
.ok()
4545
.map(|m| is_dir_to_mode(m.is_dir()));
4646

47-
let entry = cache.at_entry(path.as_slice(), mode)?;
47+
let entry = cache.at_entry(&path, mode)?;
4848
if !entry.matching_attributes(&mut matches) {
4949
continue;
5050
}

gitoxide-core/src/repository/exclude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub fn query(
4848
.metadata()
4949
.ok()
5050
.map(|m| is_dir_to_mode(m.is_dir()));
51-
let entry = cache.at_entry(path.as_slice(), mode)?;
51+
let entry = cache.at_entry(&path, mode)?;
5252
let match_ = entry
5353
.matching_exclude_pattern()
5454
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));

gix-fs/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ doctest = false
1919
serde = ["dep:serde"]
2020

2121
[dependencies]
22+
bstr = "1.5.0"
23+
gix-path = { version = "^0.10.14", path = "../gix-path" }
2224
gix-features = { version = "^0.40.0", path = "../gix-features", features = ["fs-read-dir"] }
2325
gix-utils = { version = "^0.1.14", path = "../gix-utils" }
2426
thiserror = "2.0.0"

gix-fs/src/stack.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
use crate::Stack;
2+
use bstr::{BStr, BString, ByteSlice};
13
use std::ffi::OsStr;
24
use std::path::{Component, Path, PathBuf};
35

4-
use crate::Stack;
5-
66
///
77
pub mod to_normal_path_components {
88
use std::ffi::OsString;
@@ -13,6 +13,8 @@ pub mod to_normal_path_components {
1313
pub enum Error {
1414
#[error("Input path \"{path}\" contains relative or absolute components", path = std::path::Path::new(.0.as_os_str()).display())]
1515
NotANormalComponent(OsString),
16+
#[error("Could not convert to UTF8 or from UTF8 due to ill-formed input")]
17+
IllegalUtf8,
1618
}
1719
}
1820

@@ -33,6 +35,47 @@ impl ToNormalPathComponents for &Path {
3335
}
3436
}
3537

38+
impl ToNormalPathComponents for PathBuf {
39+
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
40+
self.components().map(|component| match component {
41+
Component::Normal(os_str) => Ok(os_str),
42+
_ => Err(to_normal_path_components::Error::NotANormalComponent(
43+
self.as_os_str().to_owned(),
44+
)),
45+
})
46+
}
47+
}
48+
49+
impl ToNormalPathComponents for &BStr {
50+
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
51+
self.split(|b| *b == b'/').filter(|c| !c.is_empty()).map(|component| {
52+
gix_path::try_from_byte_slice(component.as_bstr())
53+
.map_err(|_| to_normal_path_components::Error::IllegalUtf8)
54+
.map(Path::as_os_str)
55+
})
56+
}
57+
}
58+
59+
impl ToNormalPathComponents for &str {
60+
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
61+
self.split('/').filter(|c| !c.is_empty()).map(|component| {
62+
gix_path::try_from_byte_slice(component.as_bytes())
63+
.map_err(|_| to_normal_path_components::Error::IllegalUtf8)
64+
.map(Path::as_os_str)
65+
})
66+
}
67+
}
68+
69+
impl ToNormalPathComponents for &BString {
70+
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
71+
self.split(|b| *b == b'/').filter(|c| !c.is_empty()).map(|component| {
72+
gix_path::try_from_byte_slice(component.as_bstr())
73+
.map_err(|_| to_normal_path_components::Error::IllegalUtf8)
74+
.map(Path::as_os_str)
75+
})
76+
}
77+
}
78+
3679
/// Access
3780
impl Stack {
3881
/// Returns the top-level path of the stack.

gix-fs/tests/fs/stack.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ fn empty_paths_are_noop_if_no_path_was_pushed_before() {
182182
let mut s = Stack::new(root.clone());
183183

184184
let mut r = Record::default();
185-
s.make_relative_path_current("".as_ref(), &mut r).unwrap();
185+
s.make_relative_path_current("", &mut r).unwrap();
186186
assert_eq!(
187187
s.current_relative().to_string_lossy(),
188188
"",
@@ -196,7 +196,7 @@ fn relative_components_are_invalid() {
196196
let mut s = Stack::new(root.clone());
197197

198198
let mut r = Record::default();
199-
let err = s.make_relative_path_current("a/..".as_ref(), &mut r).unwrap_err();
199+
let err = s.make_relative_path_current(p("a/.."), &mut r).unwrap_err();
200200
assert_eq!(
201201
err.to_string(),
202202
format!(
@@ -205,7 +205,7 @@ fn relative_components_are_invalid() {
205205
)
206206
);
207207

208-
s.make_relative_path_current("a/./b".as_ref(), &mut r)
208+
s.make_relative_path_current(p("a/./b"), &mut r)
209209
.expect("dot is ignored");
210210
assert_eq!(
211211
r,
@@ -221,7 +221,7 @@ fn relative_components_are_invalid() {
221221
if cfg!(windows) { r".\a\b" } else { "./a/b" },
222222
"dot is silently ignored"
223223
);
224-
s.make_relative_path_current("a//b/".as_ref(), &mut r)
224+
s.make_relative_path_current(p("a//b/"), &mut r)
225225
.expect("multiple-slashes are ignored");
226226
assert_eq!(
227227
r,
@@ -240,19 +240,19 @@ fn absolute_paths_are_invalid() -> crate::Result {
240240
let mut s = Stack::new(root.clone());
241241

242242
let mut r = Record::default();
243-
let err = s.make_relative_path_current("/".as_ref(), &mut r).unwrap_err();
243+
let err = s.make_relative_path_current(p("/"), &mut r).unwrap_err();
244244
assert_eq!(
245245
err.to_string(),
246246
r#"Input path "/" contains relative or absolute components"#,
247247
"a leading slash is always considered absolute"
248248
);
249-
s.make_relative_path_current("a/".as_ref(), &mut r)?;
249+
s.make_relative_path_current(p("a/"), &mut r)?;
250250
assert_eq!(
251251
s.current(),
252252
p("./a/"),
253253
"trailing slashes aren't a problem at this stage, as they cannot cause a 'breakout'"
254254
);
255-
s.make_relative_path_current(r"b\".as_ref(), &mut r)?;
255+
s.make_relative_path_current(p(r"b\"), &mut r)?;
256256
assert_eq!(
257257
s.current(),
258258
p(r"./b\"),
@@ -314,10 +314,10 @@ fn delegate_calls_are_consistent() -> crate::Result {
314314
let mut s = Stack::new(root.clone());
315315

316316
assert_eq!(s.current(), root);
317-
assert_eq!(s.current_relative(), Path::new(""));
317+
assert_eq!(s.current_relative(), p(""));
318318

319319
let mut r = Record::default();
320-
s.make_relative_path_current("a/b".as_ref(), &mut r)?;
320+
s.make_relative_path_current("a/b", &mut r)?;
321321
let mut dirs = vec![root.clone(), root.join("a")];
322322
assert_eq!(
323323
r,
@@ -329,7 +329,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
329329
"it pushes the root-directory first, then the intermediate one"
330330
);
331331

332-
s.make_relative_path_current("a/b2".as_ref(), &mut r)?;
332+
s.make_relative_path_current("a/b2", &mut r)?;
333333
assert_eq!(
334334
r,
335335
Record {
@@ -340,7 +340,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
340340
"dirs remain the same as b2 is a leaf/file, hence the new `push`"
341341
);
342342

343-
s.make_relative_path_current("c/d/e".as_ref(), &mut r)?;
343+
s.make_relative_path_current("c/d/e", &mut r)?;
344344
dirs.pop();
345345
dirs.extend([root.join("c"), root.join("c").join("d")]);
346346
assert_eq!(
@@ -354,7 +354,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
354354
);
355355

356356
dirs.push(root.join("c").join("d").join("x"));
357-
s.make_relative_path_current("c/d/x/z".as_ref(), &mut r)?;
357+
s.make_relative_path_current("c/d/x/z", &mut r)?;
358358
assert_eq!(
359359
r,
360360
Record {
@@ -366,8 +366,8 @@ fn delegate_calls_are_consistent() -> crate::Result {
366366
);
367367

368368
dirs.drain(1..).count();
369-
s.make_relative_path_current("f".as_ref(), &mut r)?;
370-
assert_eq!(s.current_relative(), Path::new("f"));
369+
s.make_relative_path_current("f", &mut r)?;
370+
assert_eq!(s.current_relative(), p("f"));
371371
assert_eq!(
372372
r,
373373
Record {
@@ -379,7 +379,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
379379
);
380380

381381
dirs.push(root.join("x"));
382-
s.make_relative_path_current("x/z".as_ref(), &mut r)?;
382+
s.make_relative_path_current("x/z", &mut r)?;
383383
assert_eq!(
384384
r,
385385
Record {
@@ -391,7 +391,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
391391
);
392392

393393
dirs.push(root.join("x").join("z"));
394-
s.make_relative_path_current("x/z/a".as_ref(), &mut r)?;
394+
s.make_relative_path_current("x/z/a", &mut r)?;
395395
assert_eq!(
396396
r,
397397
Record {
@@ -404,7 +404,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
404404

405405
dirs.push(root.join("x").join("z").join("a"));
406406
dirs.push(root.join("x").join("z").join("a").join("b"));
407-
s.make_relative_path_current("x/z/a/b/c".as_ref(), &mut r)?;
407+
s.make_relative_path_current("x/z/a/b/c", &mut r)?;
408408
assert_eq!(
409409
r,
410410
Record {
@@ -416,7 +416,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
416416
);
417417

418418
dirs.drain(1 /*root*/ + 1 /*x*/ + 1 /*x/z*/ ..).count();
419-
s.make_relative_path_current("x/z".as_ref(), &mut r)?;
419+
s.make_relative_path_current("x/z", &mut r)?;
420420
assert_eq!(
421421
r,
422422
Record {
@@ -432,15 +432,15 @@ fn delegate_calls_are_consistent() -> crate::Result {
432432
"the stack is state so keeps thinking it's a directory which is consistent. Git does it differently though."
433433
);
434434

435-
let err = s.make_relative_path_current("".as_ref(), &mut r).unwrap_err();
435+
let err = s.make_relative_path_current(p(""), &mut r).unwrap_err();
436436
assert_eq!(
437437
err.to_string(),
438438
"empty inputs are not allowed",
439439
"this is to protect us from double-counting the root path next time a component is pushed, \
440440
and besides that really shouldn't happen"
441441
);
442442

443-
s.make_relative_path_current("leaf".as_ref(), &mut r)?;
443+
s.make_relative_path_current("leaf", &mut r)?;
444444
dirs.drain(1..).count();
445445
assert_eq!(
446446
r,
@@ -452,7 +452,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
452452
"reset as much as possible, with just a leaf-component and the root directory"
453453
);
454454

455-
s.make_relative_path_current("a//b".as_ref(), &mut r)?;
455+
s.make_relative_path_current(p("a//b"), &mut r)?;
456456
dirs.push(root.join("a"));
457457
assert_eq!(
458458
r,
@@ -466,7 +466,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
466466

467467
#[cfg(not(windows))]
468468
{
469-
s.make_relative_path_current(r"\/b".as_ref(), &mut r)?;
469+
s.make_relative_path_current(r"\/b", &mut r)?;
470470
dirs.pop();
471471
dirs.push(root.join(r"\"));
472472
assert_eq!(
@@ -479,7 +479,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
479479
"a backslash is a normal character outside of Windows, so it's fine to have it as component"
480480
);
481481

482-
s.make_relative_path_current(r"\".as_ref(), &mut r)?;
482+
s.make_relative_path_current(r"\", &mut r)?;
483483
assert_eq!(
484484
r,
485485
Record {
@@ -494,7 +494,7 @@ fn delegate_calls_are_consistent() -> crate::Result {
494494
r"a backslash can also be a valid leaf component - here we only popped the 'b', leaving the \ 'directory'"
495495
);
496496

497-
s.make_relative_path_current(r"\\".as_ref(), &mut r)?;
497+
s.make_relative_path_current(r"\\", &mut r)?;
498498
dirs.pop();
499499
assert_eq!(
500500
r,

gix-status/src/stack.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
use crate::SymlinkCheck;
12
use bstr::BStr;
3+
use gix_fs::stack::ToNormalPathComponents;
24
use gix_fs::Stack;
35
use std::borrow::Cow;
46
use std::path::{Path, PathBuf};
57

6-
use crate::SymlinkCheck;
7-
88
impl SymlinkCheck {
99
/// Create a new stack that starts operating at `root`.
1010
pub fn new(root: PathBuf) -> Self {
@@ -24,7 +24,7 @@ impl SymlinkCheck {
2424
/// ### Note
2525
///
2626
/// On windows, no verification is performed, instead only the combined path is provided as usual.
27-
pub fn verified_path(&mut self, relative_path: &Path) -> std::io::Result<&Path> {
27+
pub fn verified_path(&mut self, relative_path: impl ToNormalPathComponents) -> std::io::Result<&Path> {
2828
self.inner.make_relative_path_current(relative_path, &mut Delegate)?;
2929
Ok(self.inner.current())
3030
}
@@ -34,7 +34,7 @@ impl SymlinkCheck {
3434
/// For convenience, this incarnation is tuned to be easy to use with Git paths, i.e. slash-separated `BString` path.
3535
pub fn verified_path_allow_nonexisting(&mut self, relative_path: &BStr) -> std::io::Result<Cow<'_, Path>> {
3636
let rela_path = gix_path::try_from_bstr(relative_path).map_err(std::io::Error::other)?;
37-
if let Err(err) = self.verified_path(&rela_path) {
37+
if let Err(err) = self.verified_path(rela_path.as_ref()) {
3838
if err.kind() == std::io::ErrorKind::NotFound {
3939
Ok(Cow::Owned(self.inner.root().join(rela_path)))
4040
} else {

0 commit comments

Comments
 (0)