Skip to content

Commit a071ce8

Browse files
committed
Don't interpolate on Path creation due to missing context (#331)
The lack of context would make %(prefix)/… paths generally fail which makes the API prone to misuse. This probably means that another API needs to be added to facilitate obtaining paths while providing a way to pass the context, too. For now having the `Path` type as intermediary seems sufficient, but also might point towards other a more approach.
1 parent 0666a35 commit a071ce8

File tree

2 files changed

+32
-27
lines changed

2 files changed

+32
-27
lines changed

git-config/src/values.rs

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,10 @@ impl<'a> AsRef<[u8]> for Path<'a> {
327327
}
328328
}
329329

330-
impl<'a> TryFrom<Cow<'a, [u8]>> for Path<'a> {
331-
type Error = PathError;
332-
330+
impl<'a> From<Cow<'a, [u8]>> for Path<'a> {
333331
#[inline]
334-
fn try_from(c: Cow<'a, [u8]>) -> Result<Self, Self::Error> {
335-
Self::interpolate(c, None)
332+
fn from(value: Cow<'a, [u8]>) -> Self {
333+
Path { value }
336334
}
337335
}
338336

@@ -348,51 +346,48 @@ impl Path<'_> {
348346
/// optionally provided by the caller through `git_install_dir`.
349347
///
350348
/// Any other, non-empty path value is returned unchanged and error is returned in case of an empty path value.
351-
pub fn interpolate<'a>(
352-
path: Cow<'a, [u8]>,
353-
git_install_dir: Option<&'a std::path::Path>,
354-
) -> Result<Path<'a>, PathError> {
355-
if path.is_empty() {
349+
pub fn interpolate(self, git_install_dir: Option<&std::path::Path>) -> Result<Self, PathError> {
350+
if self.is_empty() {
356351
return Err(PathError::Missing { what: "path" });
357352
}
358353

359354
const PREFIX: &[u8] = b"%(prefix)/";
360355
const SLASH: u8 = b'/';
361-
if path.starts_with(PREFIX) {
356+
if self.starts_with(PREFIX) {
362357
let mut expanded = git_features::path::into_bytes(git_install_dir.ok_or(PathError::Missing {
363358
what: "git install dir",
364359
})?)
365360
.context("git install dir")?
366361
.into_owned();
367-
let (_prefix, val) = path.split_at(PREFIX.len() - 1);
362+
let (_prefix, val) = self.split_at(PREFIX.len() - 1);
368363
expanded.extend(val);
369364
Ok(Path {
370365
value: Cow::Owned(expanded),
371366
})
372-
} else if path.starts_with(b"~/") {
367+
} else if self.starts_with(b"~/") {
373368
let home_path = dirs::home_dir().ok_or(PathError::Missing { what: "home dir" })?;
374369
let mut expanded = git_features::path::into_bytes(home_path)
375370
.context("home dir")?
376371
.into_owned();
377-
let (_prefix, val) = path.split_at(SLASH.len());
372+
let (_prefix, val) = self.split_at(SLASH.len());
378373
expanded.extend(val);
379374
let expanded = git_features::path::convert::to_unix_separators(expanded);
380375
Ok(Path { value: expanded })
381-
} else if path.starts_with(b"~") && path.contains(&SLASH) {
382-
Self::interpolate_user(path, SLASH)
376+
} else if self.starts_with(b"~") && self.contains(&SLASH) {
377+
self.interpolate_user(SLASH)
383378
} else {
384-
Ok(Path { value: path })
379+
Ok(self)
385380
}
386381
}
387382

388383
#[cfg(target_os = "windows")]
389-
fn interpolate_user(_val: Cow<[u8]>, _slash: u8) -> Result<Path, PathError> {
384+
fn interpolate_user(self, _slash: u8) -> Result<Self, PathError> {
390385
Err(PathError::UserInterpolationUnsupported)
391386
}
392387

393388
#[cfg(not(target_os = "windows"))]
394-
fn interpolate_user(val: Cow<[u8]>, slash: u8) -> Result<Path, PathError> {
395-
let (_prefix, val) = val.split_at(slash.len());
389+
fn interpolate_user(self, slash: u8) -> Result<Self, PathError> {
390+
let (_prefix, val) = self.split_at(slash.len());
396391
let i = val
397392
.iter()
398393
.position(|&e| e == slash)
@@ -1549,7 +1544,7 @@ mod path {
15491544
#[test]
15501545
fn empty_is_error() {
15511546
assert!(matches!(
1552-
Path::try_from(Cow::Borrowed("".as_bytes())),
1547+
Path::from(Cow::Borrowed("".as_bytes())).interpolate(None),
15531548
Err(PathError::Missing { what: "path" })
15541549
));
15551550
}
@@ -1560,7 +1555,9 @@ mod path {
15601555
let git_install_dir = "/tmp/git";
15611556
let expected = format!("{}/foo/bar", git_install_dir);
15621557
assert_eq!(
1563-
&*Path::interpolate(val, Some(std::path::Path::new(git_install_dir))).unwrap(),
1558+
&*Path::from(val)
1559+
.interpolate(Some(std::path::Path::new(git_install_dir)))
1560+
.unwrap(),
15641561
expected.as_bytes()
15651562
);
15661563
}
@@ -1570,7 +1567,9 @@ mod path {
15701567
let path = &b"./%(prefix)/foo/bar"[..];
15711568
let git_install_dir = "/tmp/git";
15721569
assert_eq!(
1573-
&*Path::interpolate(Cow::Borrowed(path), Some(std::path::Path::new(git_install_dir))).unwrap(),
1570+
&*Path::from(Cow::Borrowed(path))
1571+
.interpolate(Some(std::path::Path::new(git_install_dir)))
1572+
.unwrap(),
15741573
path
15751574
);
15761575
}
@@ -1587,7 +1586,7 @@ mod path {
15871586
let home = home.replace("\\", "/");
15881587
let expected = format!("{}/foo/bar", home);
15891588
assert_eq!(
1590-
Path::try_from(Cow::Borrowed(path)).unwrap().as_ref(),
1589+
Path::from(Cow::Borrowed(path)).interpolate(None).unwrap().as_ref(),
15911590
expected.as_bytes()
15921591
);
15931592
}
@@ -1596,7 +1595,7 @@ mod path {
15961595
#[test]
15971596
fn user_interpolated() {
15981597
assert!(matches!(
1599-
Path::try_from(Cow::Borrowed(&b"~baz/foo/bar"[..])),
1598+
Path::from(Cow::Borrowed(&b"~baz/foo/bar"[..])).interpolate(None),
16001599
Err(PathError::UserInterpolationUnsupported)
16011600
));
16021601
}
@@ -1609,7 +1608,7 @@ mod path {
16091608
let home = std::env::var("HOME").unwrap();
16101609
let expected = format!("{}/foo/bar", home);
16111610
assert_eq!(
1612-
&*Path::try_from(Cow::Borrowed(path.as_bytes())).unwrap(),
1611+
&*Path::from(Cow::Borrowed(path.as_bytes())).interpolate(None).unwrap(),
16131612
expected.as_bytes()
16141613
);
16151614
}

git-config/tests/integration_tests/file_integeration_test.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,14 @@ fn get_value_for_all_provided_values() -> crate::Result {
8080
.to_owned();
8181
#[cfg(target_os = "windows")]
8282
let home = home.replace("\\", "/");
83+
let actual = file.value::<git_config::values::Path>("core", None, "location")?;
8384
assert_eq!(
84-
file.value::<git_config::values::Path>("core", None, "location")?,
85+
&*actual,
86+
"~/tmp".as_bytes(),
87+
"no interpolation occours when querying a path due to lack of context"
88+
);
89+
assert_eq!(
90+
actual.interpolate(None).unwrap(),
8591
git_config::values::Path {
8692
value: Cow::Owned(format!("{}/tmp", home).into_bytes())
8793
}

0 commit comments

Comments
 (0)