The file as vendored does not conform to our source formatting
conventions. Let's reformat it so it does.
- skip-blame automated code reformatting
( )
The file as vendored does not conform to our source formatting
conventions. Let's reformat it so it does.
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| Path | Packages | |||
|---|---|---|---|---|
| M | rust/hg-core/src/utils/path.rs (49 lines) |
| /// | /// | ||||
| /// Unlike [`fs::canonicalize`], do not follow symlinks. | /// Unlike [`fs::canonicalize`], do not follow symlinks. | ||||
| /// | /// | ||||
| /// This function does not access the filesystem. Therefore it can behave | /// This function does not access the filesystem. Therefore it can behave | ||||
| /// differently from the kernel or other library functions in corner cases. | /// differently from the kernel or other library functions in corner cases. | ||||
| /// For example: | /// For example: | ||||
| /// | /// | ||||
| /// - On some systems with symlink support, `foo/bar/..` and `foo` can be | /// - On some systems with symlink support, `foo/bar/..` and `foo` can be | ||||
| /// different as seen by the kernel, if `foo/bar` is a symlink. This | /// different as seen by the kernel, if `foo/bar` is a symlink. This function | ||||
| /// function always returns `foo` in this case. | /// always returns `foo` in this case. | ||||
| /// - On Windows, the official normalization rules are much more complicated. | /// - On Windows, the official normalization rules are much more complicated. | ||||
| /// See https://github.com/rust-lang/rust/pull/47363#issuecomment-357069527. | /// See https://github.com/rust-lang/rust/pull/47363#issuecomment-357069527. | ||||
| /// For example, this function cannot translate "drive relative" path like | /// For example, this function cannot translate "drive relative" path like | ||||
| /// "X:foo" to an absolute path. | /// "X:foo" to an absolute path. | ||||
| /// | /// | ||||
| /// Return an error if `std::env::current_dir()` fails or if this function | /// Return an error if `std::env::current_dir()` fails or if this function | ||||
| /// fails to produce an absolute path. | /// fails to produce an absolute path. | ||||
| pub fn absolute(path: impl AsRef<Path>) -> io::Result<PathBuf> { | pub fn absolute(path: impl AsRef<Path>) -> io::Result<PathBuf> { | ||||
| io::ErrorKind::Other, | io::ErrorKind::Other, | ||||
| format!("cannot get absoltue path from {:?}", path), | format!("cannot get absoltue path from {:?}", path), | ||||
| )); | )); | ||||
| } | } | ||||
| let mut result = PathBuf::new(); | let mut result = PathBuf::new(); | ||||
| for component in path.components() { | for component in path.components() { | ||||
| match component { | match component { | ||||
| Component::Normal(_) | Component::RootDir | Component::Prefix(_) => { | Component::Normal(_) | ||||
| | Component::RootDir | |||||
| | Component::Prefix(_) => { | |||||
| result.push(component); | result.push(component); | ||||
| } | } | ||||
| Component::ParentDir => { | Component::ParentDir => { | ||||
| result.pop(); | result.pop(); | ||||
| } | } | ||||
| Component::CurDir => (), | Component::CurDir => (), | ||||
| } | } | ||||
| } | } | ||||
| Ok(result) | Ok(result) | ||||
| } | } | ||||
| /// Remove the file pointed by `path`. | /// Remove the file pointed by `path`. | ||||
| #[cfg(unix)] | #[cfg(unix)] | ||||
| pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> { | pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> { | ||||
| fs_remove_file(path)?; | fs_remove_file(path)?; | ||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| /// Remove the file pointed by `path`. | /// Remove the file pointed by `path`. | ||||
| /// | /// | ||||
| /// On Windows, removing a file can fail for various reasons, including if the file is memory | /// On Windows, removing a file can fail for various reasons, including if the | ||||
| /// mapped. This can happen when the repository is accessed concurrently while a background task is | /// file is memory mapped. This can happen when the repository is accessed | ||||
| /// trying to remove a packfile. To solve this, we can rename the file before trying to remove it. | /// concurrently while a background task is trying to remove a packfile. To | ||||
| /// solve this, we can rename the file before trying to remove it. | |||||
| /// If the remove operation fails, a future repack will clean it up. | /// If the remove operation fails, a future repack will clean it up. | ||||
| #[cfg(not(unix))] | #[cfg(not(unix))] | ||||
| pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> { | pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> { | ||||
| let path = path.as_ref(); | let path = path.as_ref(); | ||||
| let extension = path | let extension = path | ||||
| .extension() | .extension() | ||||
| .and_then(|ext| ext.to_str()) | .and_then(|ext| ext.to_str()) | ||||
| .map_or(".to-delete".to_owned(), |ext| ".".to_owned() + ext + "-tmp"); | .map_or(".to-delete".to_owned(), |ext| ".".to_owned() + ext + "-tmp"); | ||||
| let dest_path = Builder::new() | let dest_path = Builder::new() | ||||
| .prefix("") | .prefix("") | ||||
| .suffix(&extension) | .suffix(&extension) | ||||
| .rand_bytes(8) | .rand_bytes(8) | ||||
| .tempfile_in(path.parent().unwrap())? | .tempfile_in(path.parent().unwrap())? | ||||
| .into_temp_path(); | .into_temp_path(); | ||||
| rename(path, &dest_path)?; | rename(path, &dest_path)?; | ||||
| // Ignore errors when removing the file, it will be cleaned up at a later time. | // Ignore errors when removing the file, it will be cleaned up at a later | ||||
| // time. | |||||
| let _ = fs_remove_file(dest_path); | let _ = fs_remove_file(dest_path); | ||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| /// Create the directory and ignore failures when a directory of the same name already exists. | /// Create the directory and ignore failures when a directory of the same name | ||||
| /// already exists. | |||||
| pub fn create_dir(path: impl AsRef<Path>) -> io::Result<()> { | pub fn create_dir(path: impl AsRef<Path>) -> io::Result<()> { | ||||
| match fs::create_dir(path.as_ref()) { | match fs::create_dir(path.as_ref()) { | ||||
| Ok(()) => Ok(()), | Ok(()) => Ok(()), | ||||
| Err(e) => { | Err(e) => { | ||||
| if e.kind() == ErrorKind::AlreadyExists && path.as_ref().is_dir() { | if e.kind() == ErrorKind::AlreadyExists && path.as_ref().is_dir() { | ||||
| Ok(()) | Ok(()) | ||||
| } else { | } else { | ||||
| Err(e) | Err(e) | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /// Expand the user's home directory and any environment variables references in | /// Expand the user's home directory and any environment variables references | ||||
| /// the given path. | /// in the given path. | ||||
| /// | /// | ||||
| /// This function is designed to emulate the behavior of Mercurial's `util.expandpath` | /// This function is designed to emulate the behavior of Mercurial's | ||||
| /// function, which in turn uses Python's `os.path.expand{user,vars}` functions. This | /// `util.expandpath` function, which in turn uses Python's | ||||
| /// results in behavior that is notably different from the default expansion behavior | /// `os.path.expand{user,vars}` functions. This results in behavior that is | ||||
| /// of the `shellexpand` crate. In particular: | /// notably different from the default expansion behavior of the `shellexpand` | ||||
| /// crate. In particular: | |||||
| /// | /// | ||||
| /// - If a reference to an environment variable is missing or invalid, the reference | /// - If a reference to an environment variable is missing or invalid, the | ||||
| /// is left unchanged in the resulting path rather than emitting an error. | /// reference is left unchanged in the resulting path rather than emitting an | ||||
| /// error. | |||||
| /// | /// | ||||
| /// - Home directory expansion explicitly happens after environment variable | /// - Home directory expansion explicitly happens after environment variable | ||||
| /// expansion, meaning that if an environment variable is expanded into a | /// expansion, meaning that if an environment variable is expanded into a | ||||
| /// string starting with a tilde (`~`), the tilde will be expanded into the | /// string starting with a tilde (`~`), the tilde will be expanded into the | ||||
| /// user's home directory. | /// user's home directory. | ||||
| /// | |||||
| pub fn expand_path(path: impl AsRef<str>) -> PathBuf { | pub fn expand_path(path: impl AsRef<str>) -> PathBuf { | ||||
| expand_path_impl(path.as_ref(), |k| env::var(k).ok(), dirs::home_dir) | expand_path_impl(path.as_ref(), |k| env::var(k).ok(), dirs::home_dir) | ||||
| } | } | ||||
| /// Same as `expand_path` but explicitly takes closures for environment variable | /// Same as `expand_path` but explicitly takes closures for environment | ||||
| /// and home directory lookup for the sake of testability. | /// variable and home directory lookup for the sake of testability. | ||||
| fn expand_path_impl<E, H>(path: &str, getenv: E, homedir: H) -> PathBuf | fn expand_path_impl<E, H>(path: &str, getenv: E, homedir: H) -> PathBuf | ||||
| where | where | ||||
| E: FnMut(&str) -> Option<String>, | E: FnMut(&str) -> Option<String>, | ||||
| H: FnOnce() -> Option<PathBuf>, | H: FnOnce() -> Option<PathBuf>, | ||||
| { | { | ||||
| // The shellexpand crate does not expand Windows environment variables | // The shellexpand crate does not expand Windows environment variables | ||||
| // like `%PROGRAMDATA%`. We'd like to expand them too. So let's do some | // like `%PROGRAMDATA%`. We'd like to expand them too. So let's do some | ||||
| // pre-processing. | // pre-processing. | ||||
| } | } | ||||
| #[cfg(unix)] | #[cfg(unix)] | ||||
| mod unix { | mod unix { | ||||
| use super::*; | use super::*; | ||||
| #[test] | #[test] | ||||
| fn test_absolute_fullpath() { | fn test_absolute_fullpath() { | ||||
| assert_eq!(absolute("/a/./b\\c/../d/.").unwrap(), Path::new("/a/d")); | assert_eq!( | ||||
| absolute("/a/./b\\c/../d/.").unwrap(), | |||||
| Path::new("/a/d") | |||||
| ); | |||||
| assert_eq!(absolute("/a/../../../../b").unwrap(), Path::new("/b")); | assert_eq!(absolute("/a/../../../../b").unwrap(), Path::new("/b")); | ||||
| assert_eq!(absolute("/../../..").unwrap(), Path::new("/")); | assert_eq!(absolute("/../../..").unwrap(), Path::new("/")); | ||||
| assert_eq!(absolute("/../../../").unwrap(), Path::new("/")); | assert_eq!(absolute("/../../../").unwrap(), Path::new("/")); | ||||
| assert_eq!( | assert_eq!( | ||||
| absolute("//foo///bar//baz").unwrap(), | absolute("//foo///bar//baz").unwrap(), | ||||
| Path::new("/foo/bar/baz") | Path::new("/foo/bar/baz") | ||||
| ); | ); | ||||
| assert_eq!(absolute("//").unwrap(), Path::new("/")); | assert_eq!(absolute("//").unwrap(), Path::new("/")); | ||||