diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index e9c3b040dc50b..45501d3a937da 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -852,6 +852,9 @@ impl char { /// `Lowercase` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. /// + /// For any `c: char`, `c.is_lowercase()` implies `c.to_lowercase() == c`. + /// However, the reverse is not necessarily true, even for cased characters. + /// /// [Unicode Standard]: https://www.unicode.org/versions/latest/ /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt @@ -896,6 +899,9 @@ impl char { /// (Character Properties) of the [Unicode Standard] and specified in the [Unicode Character /// Database][ucd] [`UnicodeData.txt`]. /// + /// For any `c: char`, `c.is_titlecase()` implies `c.to_titlecase() == c`. + /// However, the reverse is not usually true, even for cased characters. + /// /// [Unicode Standard]: https://www.unicode.org/versions/latest/ /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`UnicodeData.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt @@ -928,6 +934,9 @@ impl char { /// `Uppercase` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. /// + /// For any `c: char`, `c.is_uppercase()` implies `c.to_uppercase() == c`. + /// However, the reverse is not necessarily true, even for cased characters. + /// /// [Unicode Standard]: https://www.unicode.org/versions/latest/ /// [ucd]: https://www.unicode.org/reports/tr44/ /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt @@ -1139,7 +1148,8 @@ impl char { } /// Returns an iterator that yields the lowercase mapping of this `char` as one or more - /// `char`s. + /// `char`s. The iterator also has implementations of [`Display`][core::fmt::Display], + /// [`PartialEq`], and [`Eq`]. /// /// If this `char` does not have a lowercase mapping, the iterator yields the same `char`. /// @@ -1197,6 +1207,13 @@ impl char { /// // convert into themselves. /// assert_eq!('山'.to_lowercase().to_string(), "山"); /// ``` + /// + /// Check if a string is in lowercase: + /// + /// ``` + /// let s = "abcde\u{0301} 山"; + /// assert!(s.chars().all(|c| c.to_lowercase() == c)); + /// ``` #[must_use = "this returns the lowercased character as a new iterator, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1206,7 +1223,8 @@ impl char { } /// Returns an iterator that yields the titlecase mapping of this `char` as one or more - /// `char`s. + /// `char`s. The iterator also has implementations of [`Display`][core::fmt::Display], + /// [`PartialEq`], and [`Eq`]. /// /// This is usually, but not always, equivalent to the uppercase mapping /// returned by [`Self::to_uppercase`]. Prefer this method when seeking to capitalize @@ -1274,6 +1292,21 @@ impl char { /// assert_eq!('山'.to_titlecase().to_string(), "山"); /// ``` /// + /// Check if a word is in titlecase: + /// + /// ``` + /// #![feature(titlecase)] + /// let word = "Dross"; + /// let mut chars = word.chars(); + /// let first_cased_char = chars.find(|c| c.is_cased()); + /// let word_is_in_titlecase = if let Some(f) = first_cased_char { + /// f.to_titlecase() == f && chars.all(|c| c.to_lowercase() == c) + /// } else { + /// true + /// }; + /// assert!(word_is_in_titlecase); + /// ``` + /// /// # Note on locale /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: @@ -1309,7 +1342,8 @@ impl char { } /// Returns an iterator that yields the uppercase mapping of this `char` as one or more - /// `char`s. + /// `char`s. The iterator also has implementations of [`Display`][core::fmt::Display], + /// [`PartialEq`], and [`Eq`]. /// /// Prefer this method when converting a word into ALL CAPS, but consider [`Self::to_titlecase`] /// instead if you seek to capitalize Only The First Letter. @@ -1374,6 +1408,13 @@ impl char { /// assert_eq!('山'.to_uppercase().to_string(), "山"); /// ``` /// + /// Check if a string is in uppercase: + /// + /// ``` + /// let s = "ABCDE\u{0301} 山"; + /// assert!(s.chars().all(|c| c.to_uppercase() == c)); + /// ``` + /// /// # Note on locale /// /// In Turkish and Azeri, the equivalent of 'i' in Latin has five forms instead of two: diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 3231c4193064c..f80a850dc3e04 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -369,6 +369,7 @@ macro_rules! casemappingiter_impls { #[$fusedstab:meta] #[$exactstab:meta] #[$displaystab:meta] + #[$eqstab:meta] $(#[$attr:meta])* $ITER_NAME:ident ) => { @@ -468,6 +469,41 @@ macro_rules! casemappingiter_impls { fmt::Display::fmt(&self.0, f) } } + + #[$eqstab] + impl PartialEq for $ITER_NAME { + #[inline] + fn eq(&self, other: &ToUppercase) -> bool { + self.0 == other.0 + } + } + + #[unstable(feature = "titlecase", issue = "153892")] + impl PartialEq for $ITER_NAME { + #[inline] + fn eq(&self, other: &ToTitlecase) -> bool { + self.0 == other.0 + } + } + + #[$eqstab] + impl PartialEq for $ITER_NAME { + #[inline] + fn eq(&self, other: &ToLowercase) -> bool { + self.0 == other.0 + } + } + + #[$eqstab] + impl PartialEq for $ITER_NAME { + #[inline] + fn eq(&self, other: &char) -> bool { + self.0 == *other + } + } + + #[$eqstab] + impl Eq for $ITER_NAME {} } } @@ -477,6 +513,7 @@ casemappingiter_impls! { #[stable(feature = "fused", since = "1.26.0")] #[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] #[stable(feature = "char_struct_display", since = "1.16.0")] + #[stable(feature = "iter_partialeq", since = "CURRENT_RUSTC_VERSION")] /// Returns an iterator that yields the uppercase equivalent of a `char`. /// /// This `struct` is created by the [`to_uppercase`] method on [`char`]. See @@ -492,6 +529,7 @@ casemappingiter_impls! { #[unstable(feature = "titlecase", issue = "153892")] #[unstable(feature = "titlecase", issue = "153892")] #[unstable(feature = "titlecase", issue = "153892")] + #[unstable(feature = "titlecase", issue = "153892")] /// Returns an iterator that yields the titlecase equivalent of a `char`. /// /// This `struct` is created by the [`to_titlecase`] method on [`char`]. See @@ -507,6 +545,7 @@ casemappingiter_impls! { #[stable(feature = "fused", since = "1.26.0")] #[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] #[stable(feature = "char_struct_display", since = "1.16.0")] + #[stable(feature = "iter_partialeq", since = "CURRENT_RUSTC_VERSION")] /// Returns an iterator that yields the lowercase equivalent of a `char`. /// /// This `struct` is created by the [`to_lowercase`] method on [`char`]. See @@ -622,6 +661,22 @@ impl fmt::Display for CaseMappingIter { } } +impl PartialEq for CaseMappingIter { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0.as_slice() == other.0.as_slice() + } +} + +impl Eq for CaseMappingIter {} + +impl PartialEq for CaseMappingIter { + #[inline] + fn eq(&self, other: &char) -> bool { + self.0.as_slice() == &[*other] + } +} + /// The error type returned when a checked char conversion fails. #[stable(feature = "u8_from_char", since = "1.59.0")] #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index 877017f682c97..655686b3646fe 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -56,10 +56,26 @@ fn test_is_cased() { fn test_char_case() { for c in '\0'..='\u{10FFFF}' { match c.case() { - None => assert!(!c.is_cased()), - Some(CharCase::Lower) => assert!(c.is_lowercase()), - Some(CharCase::Upper) => assert!(c.is_uppercase()), - Some(CharCase::Title) => assert!(c.is_titlecase()), + None => { + assert!( + !c.is_cased() && !c.is_lowercase() && !c.is_titlecase() && !c.is_uppercase() + ); + assert_eq!(c.to_lowercase(), c); + assert_eq!(c.to_uppercase(), c); + assert_eq!(c.to_titlecase(), c); + } + Some(CharCase::Lower) => { + assert!(c.is_cased() && c.is_lowercase() && !c.is_titlecase() && !c.is_uppercase()); + assert_eq!(c.to_lowercase(), c) + } + Some(CharCase::Upper) => { + assert!(c.is_cased() && !c.is_lowercase() && !c.is_titlecase() && c.is_uppercase()); + assert_eq!(c.to_uppercase(), c) + } + Some(CharCase::Title) => { + assert!(c.is_cased() && !c.is_lowercase() && c.is_titlecase() && !c.is_uppercase()); + assert_eq!(c.to_titlecase(), c) + } } } }