From c3b0393d013f1c05437b8d85ec9fcc6032c3032c Mon Sep 17 00:00:00 2001 From: Ryan Lopopolo Date: Mon, 31 Aug 2020 21:56:03 -0700 Subject: [PATCH] Implement PartialOrd and PartialEq for UTF-8 strings and FFI Strings This PR adds many more `PartialEq` and `PartialOrd` implementations for `Path`, `PathBuf`, `OsStr`, `OsString`, and `Component`. The Rust Standard Library guarantees that these types are freely and cheaply constructable from UTF-8 `String` and `&str`, but comparing these types with `&str` literals is not ergonomic. One use case I'd like to enable is filtering out paths containing a "test" directory while walking a tree and inspecting the path with the `components()` iterator. Example: ```rust let is_test_source = relative_source .components() .any(|component| component.as_os_str() == OsStr::new("test")); ``` New core trait impls: `OsString`: - `impl PartialEq for OsString` and reflexive - `impl PartialEq> for OsString` and reflexive - `impl PartialEq> for OsString` and reflexive - `impl PartialEq> for OsString` and reflexive - `impl PartialEq> for OsString` and reflexive - `impl PartialOrd for OsString` and reflexive - `impl PartialOrd> for OsString` and reflexive - `impl PartialOrd> for OsString` and reflexive - `impl PartialOrd> for OsString` and reflexive - `impl PartialOrd> for OsString` and reflexive `OsStr`: - `impl PartialEq for OsStr` and reflexive - `impl PartialEq> for OsStr` and reflexive - `impl PartialEq> for OsStr` and reflexive - `impl PartialEq> for OsStr` and reflexive - `impl PartialEq> for OsStr` and reflexive - `impl PartialOrd for OsStr` and reflexive - `impl PartialOrd> for OsStr` and reflexive - `impl PartialOrd> for OsStr` and reflexive - `impl PartialOrd> for OsStr` and reflexive - `impl PartialOrd> for OsStr` and reflexive `Component`: - `impl PartialEq for Component` and reflexive - `impl PartialEq<&'_ OsStr> for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq<&'_ Path> for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialEq> for Component` and reflexive - `impl PartialOrd for Component` and reflexive - `impl PartialOrd<&'_ OsStr> for Component` and reflexive - `impl PartialOrd for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd for Component` and reflexive - `impl PartialOrd<&'_ Path> for Component` and reflexive - `impl PartialOrd for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd for Component` and reflexive - `impl PartialOrd for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd> for Component` and reflexive - `impl PartialOrd> for Component` and reflexive `PathBuf`: - `impl PartialEq for PathBuf` and reflexive - `impl PartialEq<&'_ str> for PathBuf` and reflexive - `impl PartialEq for PathBuf` and reflexive - `impl PartialEq> for PathBuf` and reflexive - `impl PartialEq> for PathBuf` and reflexive - `impl PartialEq> for PathBuf` and reflexive - `impl PartialEq> for PathBuf` and reflexive - `impl PartialOrd for PathBuf` and reflexive - `impl PartialOrd<&'_ str> for PathBuf` and reflexive - `impl PartialOrd for PathBuf` and reflexive - `impl PartialOrd> for PathBuf` and reflexive - `impl PartialOrd> for PathBuf` and reflexive - `impl PartialOrd> for PathBuf` and reflexive - `impl PartialOrd> for PathBuf` and reflexive `Path`: - `impl PartialEq for Path` and reflexive - `impl PartialEq<&'_ str> for Path` and reflexive - `impl PartialEq for Path` and reflexive - `impl PartialEq> for Path` and reflexive - `impl PartialEq> for Path` and reflexive - `impl PartialEq> for Path` and reflexive - `impl PartialEq> for Path` and reflexive - `impl PartialOrd for Path` and reflexive - `impl PartialOrd<&'_ str> for Path` and reflexive - `impl PartialOrd for Path` and reflexive - `impl PartialOrd> for Path` and reflexive - `impl PartialOrd> for Path` and reflexive - `impl PartialOrd> for Path` and reflexive - `impl PartialOrd> for Path` and reflexive Fixes #71700. --- library/std/src/ffi/os_str.rs | 51 ++++++++++ library/std/src/path.rs | 184 ++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index e0be6d1c836ae..121e9734915ad 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -1051,6 +1051,57 @@ impl_cmp!(Cow<'a, OsStr>, OsStr); impl_cmp!(Cow<'a, OsStr>, &'b OsStr); impl_cmp!(Cow<'a, OsStr>, OsString); +macro_rules! impl_cmp_str { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let s: &str = other.as_ref(); + *self == *OsStr::new(s) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let s: &str = self.as_ref(); + *OsStr::new(s) == *other + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let s: &str = other.as_ref(); + ::partial_cmp(self.as_ref(), OsStr::new(s)) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let s: &str = self.as_ref(); + ::partial_cmp(OsStr::new(s), other.as_ref()) + } + } + }; +} + +impl_cmp_str!(OsString, String); +impl_cmp_str!(OsString, Cow<'a, str>); +impl_cmp_str!(OsString, Box); +impl_cmp_str!(OsString, Rc); +impl_cmp_str!(OsString, Arc); +impl_cmp_str!(OsStr, String); +impl_cmp_str!(OsStr, Cow<'a, str>); +impl_cmp_str!(OsStr, Box); +impl_cmp_str!(OsStr, Rc); +impl_cmp_str!(OsStr, Arc); + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for OsStr { #[inline] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index d71e89d0eee68..e769f7496f3b3 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -535,6 +535,135 @@ impl AsRef for Component<'_> { } } +macro_rules! impl_component_cmp { + ($other:ty) => { + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$other> for Component<'_> { + #[inline] + fn eq(&self, other: &$other) -> bool { + ::eq(self.as_os_str(), other.as_ref()) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq> for $other { + #[inline] + fn eq(&self, other: &'_ Component<'_>) -> bool { + ::eq(self.as_ref(), other.as_os_str()) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$other> for Component<'_> { + #[inline] + fn partial_cmp(&self, other: &$other) -> Option { + ::partial_cmp(self.as_os_str(), other.as_ref()) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd> for $other { + #[inline] + fn partial_cmp(&self, other: &Component<'_>) -> Option { + ::partial_cmp(self.as_ref(), other.as_os_str()) + } + } + }; +} + +impl_component_cmp!(OsStr); +impl_component_cmp!(&'a OsStr); +impl_component_cmp!(OsString); +impl_component_cmp!(Cow<'a, OsStr>); +impl_component_cmp!(Box); + +macro_rules! impl_component_cmp_path { + ($other:ty) => { + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$other> for Component<'_> { + #[inline] + fn eq(&self, other: &$other) -> bool { + ::eq(self.as_os_str(), other.as_os_str()) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq> for $other { + #[inline] + fn eq(&self, other: &'_ Component<'_>) -> bool { + ::eq(self.as_os_str(), other.as_os_str()) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$other> for Component<'_> { + #[inline] + fn partial_cmp(&self, other: &$other) -> Option { + ::partial_cmp(self.as_os_str(), other.as_os_str()) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd> for $other { + #[inline] + fn partial_cmp(&self, other: &Component<'_>) -> Option { + ::partial_cmp(self.as_os_str(), other.as_os_str()) + } + } + }; +} + +impl_component_cmp_path!(Path); +impl_component_cmp_path!(&'a Path); +impl_component_cmp_path!(PathBuf); +impl_component_cmp_path!(Cow<'a, Path>); +impl_component_cmp_path!(Box); + +macro_rules! impl_component_cmp_str { + ($other:ty) => { + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$other> for Component<'_> { + #[inline] + fn eq(&self, other: &$other) -> bool { + self.as_os_str() == other + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq> for $other { + #[inline] + fn eq(&self, other: &Component<'_>) -> bool { + self == other.as_os_str() + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$other> for Component<'_> { + #[inline] + fn partial_cmp(&self, other: &$other) -> Option { + let s: &str = other.as_ref(); + ::partial_cmp(self.as_os_str(), OsStr::new(s)) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd> for $other { + #[inline] + fn partial_cmp(&self, other: &Component<'_>) -> Option { + let s: &str = self.as_ref(); + ::partial_cmp(OsStr::new(s), other.as_os_str()) + } + } + }; +} + +impl_component_cmp_str!(str); +impl_component_cmp_str!(String); +impl_component_cmp_str!(Cow<'a, str>); +impl_component_cmp_str!(Box); +impl_component_cmp_str!(Rc); +impl_component_cmp_str!(Arc); + /// An iterator over the [`Component`]s of a [`Path`]. /// /// This `struct` is created by the [`components`] method on [`Path`]. @@ -2727,6 +2856,61 @@ impl_cmp_os_str!(Cow<'a, Path>, OsStr); impl_cmp_os_str!(Cow<'a, Path>, &'b OsStr); impl_cmp_os_str!(Cow<'a, Path>, OsString); +macro_rules! impl_cmp_str { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &str = other.as_ref(); + *self == *OsStr::new(other) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let s: &str = self.as_ref(); + *OsStr::new(s) == *other + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let s: &str = other.as_ref(); + ::partial_cmp(self.as_os_str(), OsStr::new(s)) + } + } + + #[stable(feature = "path_os_str_cmp_str", since = "1.48.0")] + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let s: &str = self.as_ref(); + ::partial_cmp(OsStr::new(s), other.as_os_str()) + } + } + }; +} + +impl_cmp_str!(PathBuf, str); +impl_cmp_str!(PathBuf, &'a str); +impl_cmp_str!(PathBuf, String); +impl_cmp_str!(PathBuf, Cow<'a, str>); +impl_cmp_str!(PathBuf, Box); +impl_cmp_str!(PathBuf, Rc); +impl_cmp_str!(PathBuf, Arc); +impl_cmp_str!(Path, str); +impl_cmp_str!(Path, &'a str); +impl_cmp_str!(Path, String); +impl_cmp_str!(Path, Cow<'a, str>); +impl_cmp_str!(Path, Box); +impl_cmp_str!(Path, Rc); +impl_cmp_str!(Path, Arc); + #[stable(since = "1.7.0", feature = "strip_prefix")] impl fmt::Display for StripPrefixError { #[allow(deprecated, deprecated_in_future)]