Skip to content

Commit 6c3608c

Browse files
cruesslerByron
authored andcommitted
feat!: add gix-fs::stack::ToNormalPathComponents.
1 parent 544cdaf commit 6c3608c

File tree

3 files changed

+57
-18
lines changed

3 files changed

+57
-18
lines changed

Cargo.lock

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

gix-fs/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
88
description = "A crate providing file system specific utilities to `gitoxide`"
99
authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]
1010
edition = "2021"
11-
rust-version = "1.70"
11+
rust-version = "1.74"
1212
include = ["src/**/*", "LICENSE-*"]
1313

1414
[lib]
@@ -21,6 +21,7 @@ serde = ["dep:serde"]
2121
[dependencies]
2222
gix-features = { version = "^0.40.0", path = "../gix-features", features = ["fs-read-dir"] }
2323
gix-utils = { version = "^0.1.14", path = "../gix-utils" }
24+
thiserror = "2.0.0"
2425
serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"] }
2526

2627
# For `Capabilities` to assure parallel operation works.

gix-fs/src/stack.rs

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
1+
use std::ffi::OsStr;
12
use std::path::{Component, Path, PathBuf};
23

34
use crate::Stack;
45

6+
///
7+
pub mod to_normal_path_components {
8+
use std::ffi::OsString;
9+
10+
/// The error used in [`ToNormalPathComponents::to_normal_path_components()`](super::ToNormalPathComponents::to_normal_path_components()).
11+
#[derive(Debug, thiserror::Error)]
12+
#[allow(missing_docs)]
13+
pub enum Error {
14+
#[error("Input path \"{path}\" contains relative or absolute components", path = std::path::Path::new(.0.as_os_str()).display())]
15+
NotANormalComponent(OsString),
16+
}
17+
}
18+
19+
/// Obtain an iterator over `OsStr`-components which are normal, none-relative and not absolute.
20+
pub trait ToNormalPathComponents {
21+
/// Return an iterator over the normal components of a path, without the separator.
22+
// TODO(MSRV): turn this into `impl Iterator` once MSRV is 1.75 or higher
23+
fn to_normal_path_components(
24+
&self,
25+
) -> Box<dyn Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> + '_>;
26+
}
27+
28+
impl ToNormalPathComponents for &Path {
29+
fn to_normal_path_components(
30+
&self,
31+
) -> Box<dyn Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> + '_> {
32+
Box::new(self.components().map(|component| match component {
33+
Component::Normal(os_str) => Ok(os_str),
34+
_ => Err(to_normal_path_components::Error::NotANormalComponent(
35+
self.as_os_str().to_owned(),
36+
)),
37+
}))
38+
}
39+
}
40+
541
/// Access
642
impl Stack {
743
/// Returns the top-level path of the stack.
@@ -62,8 +98,13 @@ impl Stack {
6298
/// `relative` paths are terminal, so point to their designated file or directory.
6399
/// The path is also expected to be normalized, and should not contain extra separators, and must not contain `..`
64100
/// or have leading or trailing slashes (or additionally backslashes on Windows).
65-
pub fn make_relative_path_current(&mut self, relative: &Path, delegate: &mut dyn Delegate) -> std::io::Result<()> {
66-
if self.valid_components != 0 && relative.as_os_str().is_empty() {
101+
pub fn make_relative_path_current(
102+
&mut self,
103+
relative: impl ToNormalPathComponents,
104+
delegate: &mut dyn Delegate,
105+
) -> std::io::Result<()> {
106+
let mut components = relative.to_normal_path_components().peekable();
107+
if self.valid_components != 0 && components.peek().is_none() {
67108
return Err(std::io::Error::new(
68109
std::io::ErrorKind::Other,
69110
"empty inputs are not allowed",
@@ -73,15 +114,19 @@ impl Stack {
73114
delegate.push_directory(self)?;
74115
}
75116

76-
let mut components = relative.components().peekable();
77117
let mut existing_components = self.current_relative.components();
78118
let mut matching_components = 0;
79119
while let (Some(existing_comp), Some(new_comp)) = (existing_components.next(), components.peek()) {
80-
if existing_comp == *new_comp {
81-
components.next();
82-
matching_components += 1;
83-
} else {
84-
break;
120+
match new_comp {
121+
Ok(new_comp) => {
122+
if existing_comp.as_os_str() == *new_comp {
123+
components.next();
124+
matching_components += 1;
125+
} else {
126+
break;
127+
}
128+
}
129+
Err(err) => return Err(std::io::Error::other(format!("{err}"))),
85130
}
86131
}
87132

@@ -100,15 +145,7 @@ impl Stack {
100145
}
101146

102147
while let Some(comp) = components.next() {
103-
if !matches!(comp, Component::Normal(_)) {
104-
return Err(std::io::Error::new(
105-
std::io::ErrorKind::Other,
106-
format!(
107-
"Input path \"{}\" contains relative or absolute components",
108-
relative.display()
109-
),
110-
));
111-
}
148+
let comp = comp.map_err(std::io::Error::other)?;
112149
let is_last_component = components.peek().is_none();
113150
self.current_is_directory = !is_last_component;
114151
self.current.push(comp);

0 commit comments

Comments
 (0)