Skip to content

Commit f5d701f

Browse files
committed
core::str::{chars_uppercase,chars_lowercase} iterators
They are based on new UnicodeConverter + UnicodeIterator internal API that supports context-sensitivity and char expansion.
1 parent 00ce472 commit f5d701f

File tree

8 files changed

+558
-0
lines changed

8 files changed

+558
-0
lines changed

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
#![feature(unicode_internals)]
145145
#![feature(unsize)]
146146
#![feature(std_internals)]
147+
#![feature(unicode_converter)]
147148
//
148149
// Language features:
149150
#![feature(allocator_internals)]

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#![feature(bench_black_box)]
4545
#![feature(strict_provenance)]
4646
#![feature(once_cell)]
47+
#![feature(unicode_converter)]
4748

4849
use std::collections::hash_map::DefaultHasher;
4950
use std::hash::{Hash, Hasher};

library/alloc/tests/str.rs

+102
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,108 @@ fn to_uppercase() {
17941794
assert_eq!("aéDžßfiᾀ".to_uppercase(), "AÉDŽSSFIἈΙ");
17951795
}
17961796

1797+
const CHARS_CONVERT_STRINGS: &[&str; 7] = &[
1798+
"aBcD",
1799+
"ὀδυσσεύς",
1800+
"ὈΔΥΣΣΕΎΣ",
1801+
"aößü💩στιγμαςDžfiᾀ",
1802+
"AÖßÜ💩ΣΤΙΓΜΑΣDžfiİ",
1803+
"İİİİİİİİİİİİİİİİİİİİİİİİ",
1804+
"i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇i̇",
1805+
];
1806+
1807+
// Run n times .next() then .next_back()
1808+
fn chars_fwdback<I: DoubleEndedIterator<Item = char>>(mut iter: I, n: usize) -> String {
1809+
let mut buf1 = String::new();
1810+
let mut buf2 = String::new();
1811+
for _ in 0..n {
1812+
if let Some(c) = iter.next() {
1813+
buf1.push(c);
1814+
} else {
1815+
break;
1816+
}
1817+
}
1818+
while let Some(c) = iter.next_back() {
1819+
buf2.push(c);
1820+
}
1821+
for c in buf2.chars().rev() {
1822+
buf1.push(c);
1823+
}
1824+
buf1
1825+
}
1826+
1827+
// Run n times .next_back() then .next()
1828+
fn chars_backfwd<I: DoubleEndedIterator<Item = char>>(mut iter: I, n: usize) -> String {
1829+
let mut buf1 = String::new();
1830+
let mut buf2 = String::new();
1831+
for _ in 0..n {
1832+
if let Some(c) = iter.next_back() {
1833+
buf2.push(c);
1834+
} else {
1835+
break;
1836+
}
1837+
}
1838+
while let Some(c) = iter.next() {
1839+
buf1.push(c);
1840+
}
1841+
for c in buf2.chars().rev() {
1842+
buf1.push(c);
1843+
}
1844+
buf1
1845+
}
1846+
1847+
#[test]
1848+
fn test_chars_uppercase() {
1849+
for s in CHARS_CONVERT_STRINGS {
1850+
let exp = s.to_uppercase();
1851+
assert_eq!(s.chars_uppercase().collect::<String>(), exp);
1852+
for i in 0..s.len() {
1853+
assert_eq!((i, &chars_fwdback(s.chars_uppercase(), i)), (i, &exp));
1854+
assert_eq!((i, &chars_backfwd(s.chars_uppercase(), i)), (i, &exp));
1855+
}
1856+
}
1857+
}
1858+
1859+
#[test]
1860+
fn test_chars_lowercase() {
1861+
for s in CHARS_CONVERT_STRINGS {
1862+
let exp = s.to_lowercase();
1863+
assert_eq!(s.chars_lowercase().collect::<String>(), exp);
1864+
for i in 0..s.len() {
1865+
assert_eq!((i, &chars_fwdback(s.chars_lowercase(), i)), (i, &exp));
1866+
assert_eq!((i, &chars_backfwd(s.chars_lowercase(), i)), (i, &exp));
1867+
}
1868+
}
1869+
}
1870+
1871+
#[test]
1872+
fn test_chars_uppercase_clone_debug() {
1873+
let mut iter = "abc".chars_uppercase();
1874+
assert_eq!(iter.next(), Some('A'));
1875+
assert_eq!(iter.next(), Some('B'));
1876+
let mut iterc = iter.clone();
1877+
assert_eq!(&format!("{:?}", &iterc), "CharsUppercase(['C'])");
1878+
assert_eq!(iter.next(), Some('C'));
1879+
assert_eq!(iter.next(), None);
1880+
assert_eq!(iterc.clone().last(), Some('C'));
1881+
assert_eq!(iterc.next(), Some('C'));
1882+
assert_eq!(iterc.next(), None);
1883+
}
1884+
1885+
#[test]
1886+
fn test_chars_lowercase_clone_debug() {
1887+
let mut iter = "ABC".chars_lowercase();
1888+
assert_eq!(iter.next(), Some('a'));
1889+
assert_eq!(iter.next(), Some('b'));
1890+
let mut iterc = iter.clone();
1891+
assert_eq!(&format!("{:?}", &iterc), "CharsLowercase(['c'])");
1892+
assert_eq!(iter.next(), Some('c'));
1893+
assert_eq!(iter.next(), None);
1894+
assert_eq!(iterc.clone().last(), Some('c'));
1895+
assert_eq!(iterc.next(), Some('c'));
1896+
assert_eq!(iterc.next(), None);
1897+
}
1898+
17971899
#[test]
17981900
fn test_into_string() {
17991901
// The only way to acquire a Box<str> in the first place is through a String, so just

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
#![feature(const_slice_from_ref)]
157157
#![feature(const_slice_index)]
158158
#![feature(const_is_char_boundary)]
159+
#![feature(unicode_converter)]
159160
//
160161
// Language features:
161162
#![feature(abi_unadjusted)]

0 commit comments

Comments
 (0)