Skip to content

Commit 61a9957

Browse files
committed
Make Path::interpolate() more useful by returning an actual PathBuf (GitoxideLabs#331)
That way users will want to call it or something like it in order to make use of the `Path`, which otherwise is just a bunch of bytes which aren't very useful at all in standard Rust. Keeping the `Path` wrapper type is probably the right choice as it matches the current API and allows to get paths explicitly which can then be interpolated in just another step.
1 parent 56341d5 commit 61a9957

File tree

2 files changed

+29
-34
lines changed

2 files changed

+29
-34
lines changed

git-config/src/values.rs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ quick_error! {
289289
Utf8Conversion(what: &'static str, err: git_features::path::Utf8Error) {
290290
display("Ill-formed UTF-8 in {}", what)
291291
context(what: &'static str, err: git_features::path::Utf8Error) -> (what, err)
292+
source(err)
292293
}
293294
UsernameConversion(err: std::str::Utf8Error) {
294295
display("Ill-formed UTF-8 in username")
@@ -334,10 +335,10 @@ impl<'a> From<Cow<'a, [u8]>> for Path<'a> {
334335
}
335336
}
336337

337-
impl Path<'_> {
338-
/// Interpolates path value
338+
impl<'a> Path<'a> {
339+
/// Interpolates this path into a file system path.
339340
///
340-
/// Path value can be given a string that begins with `~/` or `~user/` or `%(prefix)/`
341+
/// If this path starts with `~/` or `~user/` or `%(prefix)/`
341342
/// - `~/` is expanded to the value of `$HOME` on unix based systems. On windows, `SHGetKnownFolderPath` is used.
342343
/// See also [dirs](https://crates.io/crates/dirs).
343344
/// - `~user/` to the specified user’s home directory, e.g `~alice` might get expanded to `/home/alice` on linux.
@@ -346,51 +347,50 @@ impl Path<'_> {
346347
/// optionally provided by the caller through `git_install_dir`.
347348
///
348349
/// Any other, non-empty path value is returned unchanged and error is returned in case of an empty path value.
349-
pub fn interpolate(self, git_install_dir: Option<&std::path::Path>) -> Result<Self, PathError> {
350+
pub fn interpolate(self, git_install_dir: Option<&std::path::Path>) -> Result<Cow<'a, std::path::Path>, PathError> {
350351
if self.is_empty() {
351352
return Err(PathError::Missing { what: "path" });
352353
}
353354

354355
const PREFIX: &[u8] = b"%(prefix)/";
355-
const SLASH: u8 = b'/';
356356
if self.starts_with(PREFIX) {
357357
let mut expanded = git_features::path::into_bytes(git_install_dir.ok_or(PathError::Missing {
358358
what: "git install dir",
359359
})?)
360360
.context("git install dir")?
361361
.into_owned();
362-
let (_prefix, val) = self.split_at(PREFIX.len() - 1);
362+
let (_prefix, val) = self.split_at(PREFIX.len() - "/".len());
363363
expanded.extend(val);
364-
Ok(Path {
365-
value: Cow::Owned(expanded),
366-
})
364+
Ok(git_features::path::from_byte_vec(expanded)
365+
.context("prefix-expanded path")?
366+
.into())
367367
} else if self.starts_with(b"~/") {
368368
let home_path = dirs::home_dir().ok_or(PathError::Missing { what: "home dir" })?;
369369
let mut expanded = git_features::path::into_bytes(home_path)
370370
.context("home dir")?
371371
.into_owned();
372-
let (_prefix, val) = self.split_at(SLASH.len());
372+
let (_prefix, val) = self.split_at("~".len());
373373
expanded.extend(val);
374374
let expanded = git_features::path::convert::to_unix_separators(expanded);
375-
Ok(Path { value: expanded })
376-
} else if self.starts_with(b"~") && self.contains(&SLASH) {
377-
self.interpolate_user(SLASH)
375+
Ok(git_features::path::from_bytes(expanded).context("tilde expanded path")?)
376+
} else if self.starts_with(b"~") && self.contains(&b'/') {
377+
self.interpolate_user()
378378
} else {
379-
Ok(self)
379+
Ok(git_features::path::from_bytes(self.value).context("unexpanded path")?)
380380
}
381381
}
382382

383383
#[cfg(target_os = "windows")]
384-
fn interpolate_user(self, _slash: u8) -> Result<Self, PathError> {
384+
fn interpolate_user(self) -> Result<Self, PathError> {
385385
Err(PathError::UserInterpolationUnsupported)
386386
}
387387

388388
#[cfg(not(target_os = "windows"))]
389-
fn interpolate_user(self, slash: u8) -> Result<Self, PathError> {
390-
let (_prefix, val) = self.split_at(slash.len());
389+
fn interpolate_user(self) -> Result<Cow<'a, std::path::Path>, PathError> {
390+
let (_prefix, val) = self.split_at('/'.len());
391391
let i = val
392392
.iter()
393-
.position(|&e| e == slash)
393+
.position(|&e| e == b'/')
394394
.ok_or(PathError::Missing { what: "/" })?;
395395
let (username, val) = val.split_at(i);
396396
let username = std::str::from_utf8(username)?;
@@ -400,9 +400,9 @@ impl Path<'_> {
400400
.dir;
401401
let mut expanded = home.as_bytes().to_owned();
402402
expanded.extend(val);
403-
Ok(Path {
404-
value: Cow::Owned(expanded),
405-
})
403+
Ok(git_features::path::from_byte_vec(expanded)
404+
.context("tilded user expanded path")?
405+
.into())
406406
}
407407
}
408408

@@ -1553,24 +1553,24 @@ mod path {
15531553
fn prefix_interpolated() {
15541554
let val = Cow::Borrowed(&b"%(prefix)/foo/bar"[..]);
15551555
let git_install_dir = "/tmp/git";
1556-
let expected = format!("{}/foo/bar", git_install_dir);
1556+
let expected = &std::path::PathBuf::from(format!("{}/foo/bar", git_install_dir));
15571557
assert_eq!(
15581558
&*Path::from(val)
15591559
.interpolate(Some(std::path::Path::new(git_install_dir)))
15601560
.unwrap(),
1561-
expected.as_bytes()
1561+
expected
15621562
);
15631563
}
15641564

15651565
#[test]
15661566
fn disabled_prefix_interpoldation() {
1567-
let path = &b"./%(prefix)/foo/bar"[..];
1567+
let path = "./%(prefix)/foo/bar";
15681568
let git_install_dir = "/tmp/git";
15691569
assert_eq!(
1570-
&*Path::from(Cow::Borrowed(path))
1570+
&*Path::from(Cow::Borrowed(path.as_bytes()))
15711571
.interpolate(Some(std::path::Path::new(git_install_dir)))
15721572
.unwrap(),
1573-
path
1573+
std::path::Path::new(path)
15741574
);
15751575
}
15761576

@@ -1587,7 +1587,7 @@ mod path {
15871587
let expected = format!("{}/foo/bar", home);
15881588
assert_eq!(
15891589
Path::from(Cow::Borrowed(path)).interpolate(None).unwrap().as_ref(),
1590-
expected.as_bytes()
1590+
std::path::Path::new(&expected)
15911591
);
15921592
}
15931593

@@ -1609,7 +1609,7 @@ mod path {
16091609
let expected = format!("{}/foo/bar", home);
16101610
assert_eq!(
16111611
&*Path::from(Cow::Borrowed(path.as_bytes())).interpolate(None).unwrap(),
1612-
expected.as_bytes()
1612+
std::path::Path::new(&expected)
16131613
);
16141614
}
16151615
}

git-config/tests/integration_tests/file_integeration_test.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,7 @@ fn get_value_for_all_provided_values() -> crate::Result {
8686
"~/tmp".as_bytes(),
8787
"no interpolation occours when querying a path due to lack of context"
8888
);
89-
assert_eq!(
90-
actual.interpolate(None).unwrap(),
91-
git_config::values::Path {
92-
value: Cow::Owned(format!("{}/tmp", home).into_bytes())
93-
}
94-
);
89+
assert_eq!(actual.interpolate(None).unwrap(), Path::new(&format!("{}/tmp", home)));
9590

9691
Ok(())
9792
}

0 commit comments

Comments
 (0)