@ -1,6 +1,9 @@
pub use etcetera ::home_dir ;
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 ;
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
/// 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
/// unchanged. The tilde will only be expanded when present as the first component of the path
/// and only slash follows it.
/// and only slash follows it.
pub fn expand_tilde ( path : impl AsRef < Path > ) -> PathBuf {
pub fn expand_tilde < ' a , P > ( path : P ) -> Cow < ' a , Path >
let path = path . as_ref ( ) ;
where
let mut components = path . components ( ) . peekable ( ) ;
P : Into < Cow < ' a , Path > > ,
if let Some ( Component ::Normal ( c ) ) = components . peek ( ) {
{
if c = = & "~" {
let path = path . into ( ) ;
if let Ok ( home ) = home_dir ( ) {
let mut components = path . components ( ) ;
// it's ok to unwrap, the path starts with `~`
if let Some ( Component ::Normal ( c ) ) = components . next ( ) {
return home . join ( path . strip_prefix ( "~" ) . unwrap ( ) ) ;
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.
/// 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
/// 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.
/// here if the path exists, just normalize it's components.
pub fn canonicalize ( path : impl AsRef < Path > ) -> PathBuf {
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 ( ) {
let path = if path . is_relative ( ) {
current_working_dir( ) . join ( path )
Cow::Owned ( current_working_dir( ) . join ( path ) )
} else {
} else {
path
path
} ;
} ;
@ -183,3 +189,32 @@ pub fn get_truncated_path(path: impl AsRef<Path>) -> PathBuf {
ret . push ( file ) ;
ret . push ( file ) ;
ret
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 ) ;
}
}
}