Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions gix-config-value/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ impl Path {
err,
})?;
Ok(home_path.join(val))
} else if self.starts_with(b"~") && self.contains(&b'/') {
} else if self.starts_with(b"~") && self.len() > 1 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the newly recognized bare ~user form

When callers rely on this public API's rustdoc, they are still told that only ~user/ triggers named-user interpolation and that other nonempty values remain unchanged; Context::home_for_user likewise documents only ~name/. With this condition, bare ~user instead invokes the callback and can return Missing, so update both public descriptions to include the slashless form.

AGENTS.md reference: AGENTS.md:L147-L152

Useful? React with 👍 / 👎.

self.interpolate_user(home_for_user.ok_or(interpolate::Error::Missing {
what: "home for user lookup",
})?)
Expand All @@ -210,14 +210,18 @@ impl Path {

#[cfg(not(any(target_os = "windows", target_os = "android")))]
fn interpolate_user(self, home_for_user: fn(&str) -> Option<PathBuf>) -> Result<PathBuf, interpolate::Error> {
let (_prefix, val) = self.split_at("/".len());
let i = val
.iter()
.position(|&e| e == b'/')
.ok_or(interpolate::Error::Missing { what: "/" })?;
let (username, path_with_leading_slash) = val.split_at(i);
let (_prefix, val) = self.split_at("~".len());
// `git` takes everything up to the first `/` as the user name, and the whole
// remainder when there is no `/` at all, so `~user` is that user's home.
let (username, path_with_leading_slash) = match val.iter().position(|&e| e == b'/') {
Some(i) => val.split_at(i),
None => (val, &[][..]),
};
let username = std::str::from_utf8(username)?;
let home = home_for_user(username).ok_or(interpolate::Error::Missing { what: "pwd user info" })?;
if path_with_leading_slash.is_empty() {
return Ok(home);
}
let path_past_user_prefix =
gix_path::try_from_byte_slice(&path_with_leading_slash["/".len()..]).map_err(|err| {
interpolate::Error::Utf8Conversion {
Expand Down
11 changes: 11 additions & 0 deletions gix-config-value/tests/value/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ mod interpolate {
Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[test]
fn tilde_with_given_user_and_no_path() -> crate::Result {
// `git -c foo.bar='~root' config --type=path foo.bar` prints that user's home
// directory on git 2.50.1: everything past the `~` is the user name when there
// is no `/`, and no trailing slash is needed.
let home = std::env::current_dir()?;
assert_eq!(interpolate_without_context("~user")?, home.join("user"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add an invariant description to the new assertion

The new assert_eq! omits the required final description, so a regression reports only the compared paths rather than explaining that a bare ~user must resolve directly to that user's home directory. Add an assertion message stating that invariant.

AGENTS.md reference: AGENTS.md:L118-L121

Useful? React with 👍 / 👎.

Ok(())
}

fn interpolate_without_context(
path: impl AsRef<str>,
) -> Result<PathBuf, gix_config_value::path::interpolate::Error> {
Expand Down
Loading