icu_collections/
iterator_utils.rs1use crate::codepointtrie::CodePointMapRange;
6
7pub(crate) struct RangeListIteratorCoalescer<I, T> {
10 iter: I,
11 peek: Option<CodePointMapRange<T>>,
12}
13
14impl<I, T: Eq> RangeListIteratorCoalescer<I, T>
15where
16 I: Iterator<Item = CodePointMapRange<T>>,
17{
18 pub fn new(iter: I) -> Self {
19 Self { iter, peek: None }
20 }
21}
22
23impl<I, T: Eq> Iterator for RangeListIteratorCoalescer<I, T>
24where
25 I: Iterator<Item = CodePointMapRange<T>>,
26{
27 type Item = CodePointMapRange<T>;
28
29 fn next(&mut self) -> Option<Self::Item> {
30 let mut ret = if let Some(peek) = self.peek.take() {
33 peek
34 } else {
35 self.iter.next()?
37 };
38
39 #[expect(clippy::while_let_on_iterator)]
41 while let Some(next) = self.iter.next() {
43 if *next.range.start() == ret.range.end() + 1 && next.value == ret.value {
44 ret.range = *ret.range.start()..=*next.range.end();
46 } else {
47 self.peek = Some(next);
50 return Some(ret);
51 }
52 }
53
54 Some(ret)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::codepointinvlist::CodePointInversionListBuilder;
62 use core::fmt::Debug;
63 use icu_properties::props::{BinaryProperty, EnumeratedProperty};
64 use icu_properties::{CodePointMapData, CodePointSetData};
65
66 fn test_set<P: BinaryProperty>(name: &str) {
67 let mut builder = CodePointInversionListBuilder::new();
68 let mut builder_complement = CodePointInversionListBuilder::new();
69
70 for range in CodePointSetData::new::<P>().iter_ranges() {
71 builder.add_range32(range)
72 }
73
74 for range in CodePointSetData::new::<P>().iter_ranges_complemented() {
75 builder_complement.add_range32(range)
76 }
77
78 builder.complement();
79 let set1 = builder.build();
80 let set2 = builder_complement.build();
81 assert_eq!(set1, set2, "Set {name} failed to complement correctly");
82 }
83
84 fn test_map<T: EnumeratedProperty + Debug>(value: T, name: &str) {
85 let mut builder = CodePointInversionListBuilder::new();
86 let mut builder_complement = CodePointInversionListBuilder::new();
87
88 for range in CodePointMapData::<T>::new().iter_ranges_for_value(value) {
89 builder.add_range32(range)
90 }
91
92 for range in CodePointMapData::<T>::new().iter_ranges_for_value_complemented(value) {
93 builder_complement.add_range32(range)
94 }
95
96 builder.complement();
97 let set1 = builder.build();
98 let set2 = builder_complement.build();
99 assert_eq!(
100 set1, set2,
101 "Map {name} failed to complement correctly with value {value:?}"
102 );
103 }
104
105 #[test]
106 fn test_complement_sets() {
107 use icu_properties::props::*;
108 test_set::<AsciiHexDigit>("ASCII_Hex_Digit");
111 test_set::<Alnum>("Alnum");
112 test_set::<Alphabetic>("Alphabetic");
113 test_set::<BidiControl>("Bidi_Control");
114 test_set::<BidiMirrored>("Bidi_Mirrored");
115 test_set::<Blank>("Blank");
116 test_set::<Cased>("Cased");
117 test_set::<CaseIgnorable>("Case_Ignorable");
118 test_set::<FullCompositionExclusion>("Full_Composition_Exclusion");
119 test_set::<ChangesWhenCasefolded>("Changes_When_Casefolded");
120 test_set::<ChangesWhenCasemapped>("Changes_When_Casemapped");
121 test_set::<ChangesWhenNfkcCasefolded>("Changes_When_NFKC_Casefolded");
122 test_set::<ChangesWhenLowercased>("Changes_When_Lowercased");
123 test_set::<ChangesWhenTitlecased>("Changes_When_Titlecased");
124 test_set::<ChangesWhenUppercased>("Changes_When_Uppercased");
125 test_set::<Dash>("Dash");
126 test_set::<Deprecated>("Deprecated");
127 test_set::<DefaultIgnorableCodePoint>("Default_Ignorable_Code_Point");
128 test_set::<Diacritic>("Diacritic");
129 test_set::<EmojiModifierBase>("Emoji_Modifier_Base");
130 test_set::<EmojiComponent>("Emoji_Component");
131 test_set::<EmojiModifier>("Emoji_Modifier");
132 test_set::<Emoji>("Emoji");
133 test_set::<EmojiPresentation>("Emoji_Presentation");
134 test_set::<Extender>("Extender");
135 test_set::<ExtendedPictographic>("Extended_Pictographic");
136 test_set::<Graph>("Graph");
137 test_set::<GraphemeBase>("Grapheme_Base");
138 test_set::<GraphemeExtend>("Grapheme_Extend");
139 test_set::<GraphemeLink>("Grapheme_Link");
140 test_set::<HexDigit>("Hex_Digit");
141 test_set::<Hyphen>("Hyphen");
142 test_set::<IdContinue>("Id_Continue");
143 test_set::<Ideographic>("Ideographic");
144 test_set::<IdStart>("Id_Start");
145 test_set::<IdsBinaryOperator>("Ids_Binary_Operator");
146 test_set::<IdsTrinaryOperator>("Ids_Trinary_Operator");
147 test_set::<JoinControl>("Join_Control");
148 test_set::<LogicalOrderException>("Logical_Order_Exception");
149 test_set::<Lowercase>("Lowercase");
150 test_set::<Math>("Math");
151 test_set::<NoncharacterCodePoint>("Noncharacter_Code_Point");
152 test_set::<PatternSyntax>("Pattern_Syntax");
153 test_set::<PatternWhiteSpace>("Pattern_White_Space");
154 test_set::<PrependedConcatenationMark>("Prepended_Concatenation_Mark");
155 test_set::<Print>("Print");
156 test_set::<QuotationMark>("Quotation_Mark");
157 test_set::<Radical>("Radical");
158 test_set::<RegionalIndicator>("Regional_Indicator");
159 test_set::<SoftDotted>("Soft_Dotted");
160 test_set::<SentenceTerminal>("Sentence_Terminal");
161 test_set::<TerminalPunctuation>("Terminal_Punctuation");
162 test_set::<UnifiedIdeograph>("Unified_Ideograph");
163 test_set::<Uppercase>("Uppercase");
164 test_set::<VariationSelector>("Variation_Selector");
165 test_set::<WhiteSpace>("White_Space");
166 test_set::<Xdigit>("Xdigit");
167 test_set::<XidContinue>("XID_Continue");
168 test_set::<XidStart>("XID_Start");
169 }
170
171 #[test]
172 fn test_complement_maps() {
173 use icu_properties::props::{GeneralCategory, Script};
174 test_map(GeneralCategory::UppercaseLetter, "gc");
175 test_map(GeneralCategory::OtherPunctuation, "gc");
176 test_map(Script::Devanagari, "script");
177 test_map(Script::Latin, "script");
178 test_map(Script::Common, "script");
179 }
180}