diff options
author | Mo | 2024-02-24 15:59:11 +0000 |
---|---|---|
committer | GitHub | 2024-02-24 15:59:11 +0000 |
commit | 6db666fce1fb4627c06d147554b8e1eb9970619e (patch) | |
tree | 8ed202acf6936485c33f033e6af5f294dfad0da0 /helix-stdx | |
parent | ec9efdef3b2f613a86098058f5705e7863e375e2 (diff) |
Optimization of tilde expansion (#9709)
* Use next and avoid a redundant prefix strip
* Avoid allocations
Especially when `expand_tilde` is claled on a path
that doesn't contain a tilde.
* Add a test
* Use Into<Cow<…>>
* Put the expand_tilde test at the end of the file
* Remove unused importsw
Diffstat (limited to 'helix-stdx')
-rw-r--r-- | helix-stdx/src/path.rs | 59 |
1 files changed, 47 insertions, 12 deletions
diff --git a/helix-stdx/src/path.rs b/helix-stdx/src/path.rs index 5746657c..1dc4d0b2 100644 --- a/helix-stdx/src/path.rs +++ b/helix-stdx/src/path.rs @@ -1,6 +1,9 @@ pub use etcetera::home_dir; -use std::path::{Component, Path, PathBuf}; +use std::{ + borrow::Cow, + path::{Component, Path, PathBuf}, +}; use crate::env::current_working_dir; @@ -19,19 +22,22 @@ pub fn fold_home_dir(path: &Path) -> PathBuf { /// Expands tilde `~` into users home directory if available, otherwise returns the path /// unchanged. The tilde will only be expanded when present as the first component of the path /// and only slash follows it. -pub fn expand_tilde(path: impl AsRef<Path>) -> PathBuf { - let path = path.as_ref(); - let mut components = path.components().peekable(); - if let Some(Component::Normal(c)) = components.peek() { - if c == &"~" { - if let Ok(home) = home_dir() { - // it's ok to unwrap, the path starts with `~` - return home.join(path.strip_prefix("~").unwrap()); +pub fn expand_tilde<'a, P>(path: P) -> Cow<'a, Path> +where + P: Into<Cow<'a, Path>>, +{ + let path = path.into(); + let mut components = path.components(); + if let Some(Component::Normal(c)) = components.next() { + if c == "~" { + if let Ok(mut buf) = home_dir() { + buf.push(components); + return Cow::Owned(buf); } } } - path.to_path_buf() + path } /// Normalize a path without resolving symlinks. @@ -109,9 +115,9 @@ pub fn normalize(path: impl AsRef<Path>) -> PathBuf { /// This function is used instead of [`std::fs::canonicalize`] because we don't want to verify /// here if the path exists, just normalize it's components. pub fn canonicalize(path: impl AsRef<Path>) -> PathBuf { - let path = expand_tilde(path); + let path = expand_tilde(path.as_ref()); let path = if path.is_relative() { - current_working_dir().join(path) + Cow::Owned(current_working_dir().join(path)) } else { path }; @@ -183,3 +189,32 @@ pub fn get_truncated_path(path: impl AsRef<Path>) -> PathBuf { ret.push(file); ret } + +#[cfg(test)] +mod tests { + use std::{ + ffi::OsStr, + path::{Component, Path}, + }; + + use crate::path; + + #[test] + fn expand_tilde() { + for path in ["~", "~/foo"] { + let expanded = path::expand_tilde(Path::new(path)); + + let tilde = Component::Normal(OsStr::new("~")); + + let mut component_count = 0; + for component in expanded.components() { + // No tilde left. + assert_ne!(component, tilde); + component_count += 1; + } + + // The path was at least expanded to something. + assert_ne!(component_count, 0); + } + } +} |