Skip to content

Commit 60625a6

Browse files
authored
Rollup merge of #88858 - spektom:to_lower_upper_rev, r=dtolnay
Allow reverse iteration of lowercase'd/uppercase'd chars The PR implements `DoubleEndedIterator` trait for `ToLowercase` and `ToUppercase`. This enables reverse iteration of lowercase/uppercase variants of character sequences. One of use cases: determining whether a char sequence is a suffix of another one. Example: ```rust fn endswith_ignore_case(s1: &str, s2: &str) -> bool { for eob in s1 .chars() .flat_map(|c| c.to_lowercase()) .rev() .zip_longest(s2.chars().flat_map(|c| c.to_lowercase()).rev()) { match eob { EitherOrBoth::Both(c1, c2) => { if c1 != c2 { return false; } } EitherOrBoth::Left(_) => return true, EitherOrBoth::Right(_) => return false, } } true } ```
2 parents 34926f0 + 417b6f3 commit 60625a6

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

library/alloc/tests/str.rs

+31
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,37 @@ fn test_rev_iterator() {
11831183
assert_eq!(pos, v.len());
11841184
}
11851185

1186+
#[test]
1187+
fn test_to_lowercase_rev_iterator() {
1188+
let s = "AÖßÜ💩ΣΤΙΓΜΑΣDžfiİ";
1189+
let v = ['\u{307}', 'i', 'fi', 'dž', 'σ', 'α', 'μ', 'γ', 'ι', 'τ', 'σ', '💩', 'ü', 'ß', 'ö', 'a'];
1190+
1191+
let mut pos = 0;
1192+
let it = s.chars().flat_map(|c| c.to_lowercase()).rev();
1193+
1194+
for c in it {
1195+
assert_eq!(c, v[pos]);
1196+
pos += 1;
1197+
}
1198+
assert_eq!(pos, v.len());
1199+
}
1200+
1201+
#[test]
1202+
fn test_to_uppercase_rev_iterator() {
1203+
let s = "aößü💩στιγμαςDžfiᾀ";
1204+
let v =
1205+
['Ι', 'Ἀ', 'I', 'F', 'DŽ', 'Σ', 'Α', 'Μ', 'Γ', 'Ι', 'Τ', 'Σ', '💩', 'Ü', 'S', 'S', 'Ö', 'A'];
1206+
1207+
let mut pos = 0;
1208+
let it = s.chars().flat_map(|c| c.to_uppercase()).rev();
1209+
1210+
for c in it {
1211+
assert_eq!(c, v[pos]);
1212+
pos += 1;
1213+
}
1214+
assert_eq!(pos, v.len());
1215+
}
1216+
11861217
#[test]
11871218
#[cfg_attr(miri, ignore)] // Miri is too slow
11881219
fn test_chars_decoding() {

library/core/src/char/mod.rs

+34
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,13 @@ impl Iterator for ToLowercase {
393393
}
394394
}
395395

396+
#[stable(feature = "case_mapping_double_ended", since = "1.59.0")]
397+
impl DoubleEndedIterator for ToLowercase {
398+
fn next_back(&mut self) -> Option<char> {
399+
self.0.next_back()
400+
}
401+
}
402+
396403
#[stable(feature = "fused", since = "1.26.0")]
397404
impl FusedIterator for ToLowercase {}
398405

@@ -420,6 +427,13 @@ impl Iterator for ToUppercase {
420427
}
421428
}
422429

430+
#[stable(feature = "case_mapping_double_ended", since = "1.59.0")]
431+
impl DoubleEndedIterator for ToUppercase {
432+
fn next_back(&mut self) -> Option<char> {
433+
self.0.next_back()
434+
}
435+
}
436+
423437
#[stable(feature = "fused", since = "1.26.0")]
424438
impl FusedIterator for ToUppercase {}
425439

@@ -479,6 +493,26 @@ impl Iterator for CaseMappingIter {
479493
}
480494
}
481495

496+
impl DoubleEndedIterator for CaseMappingIter {
497+
fn next_back(&mut self) -> Option<char> {
498+
match *self {
499+
CaseMappingIter::Three(a, b, c) => {
500+
*self = CaseMappingIter::Two(a, b);
501+
Some(c)
502+
}
503+
CaseMappingIter::Two(b, c) => {
504+
*self = CaseMappingIter::One(b);
505+
Some(c)
506+
}
507+
CaseMappingIter::One(c) => {
508+
*self = CaseMappingIter::Zero;
509+
Some(c)
510+
}
511+
CaseMappingIter::Zero => None,
512+
}
513+
}
514+
}
515+
482516
impl fmt::Display for CaseMappingIter {
483517
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484518
match *self {

library/core/tests/char.rs

+6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ fn test_to_lowercase() {
103103
let iter: String = c.to_lowercase().collect();
104104
let disp: String = c.to_lowercase().to_string();
105105
assert_eq!(iter, disp);
106+
let iter_rev: String = c.to_lowercase().rev().collect();
107+
let disp_rev: String = disp.chars().rev().collect();
108+
assert_eq!(iter_rev, disp_rev);
106109
iter
107110
}
108111
assert_eq!(lower('A'), "a");
@@ -130,6 +133,9 @@ fn test_to_uppercase() {
130133
let iter: String = c.to_uppercase().collect();
131134
let disp: String = c.to_uppercase().to_string();
132135
assert_eq!(iter, disp);
136+
let iter_rev: String = c.to_uppercase().rev().collect();
137+
let disp_rev: String = disp.chars().rev().collect();
138+
assert_eq!(iter_rev, disp_rev);
133139
iter
134140
}
135141
assert_eq!(upper('a'), "A");

0 commit comments

Comments
 (0)