1use std::cmp::max;
17use std::ops::Range;
18
19use unic_ucd_bidi::bidi_class::abbr_names::*;
20use unic_ucd_bidi::BidiClass;
21
22use super::level::Level;
23
24pub type LevelRun = Range<usize>;
28
29#[derive(Debug, PartialEq)]
31pub struct IsolatingRunSequence {
32 pub runs: Vec<LevelRun>,
33 pub sos: BidiClass, pub eos: BidiClass, }
36
37pub fn isolating_run_sequences(
45 para_level: Level,
46 original_classes: &[BidiClass],
47 levels: &[Level],
48) -> Vec<IsolatingRunSequence> {
49 let runs = level_runs(levels, original_classes);
50
51 let mut sequences = Vec::with_capacity(runs.len());
54
55 let mut stack = vec![Vec::new()];
58
59 for run in runs {
60 assert!(run.len() > 0);
61 assert!(!stack.is_empty());
62
63 let start_class = original_classes[run.start];
64 let end_class = original_classes[run.end - 1];
65
66 let mut sequence = if start_class == PDI && stack.len() > 1 {
67 stack.pop().unwrap()
69 } else {
70 Vec::new()
72 };
73
74 sequence.push(run);
75
76 if matches!(end_class, RLI | LRI | FSI) {
77 stack.push(sequence);
79 } else {
80 sequences.push(sequence);
82 }
83 }
84 sequences.extend(stack.into_iter().rev().filter(|seq| !seq.is_empty()));
86
87 sequences
90 .into_iter()
91 .map(|sequence: Vec<LevelRun>| {
92 assert!(!sequence.is_empty());
93
94 let start_of_seq = sequence[0].start;
95 let end_of_seq = sequence[sequence.len() - 1].end;
96 let seq_level = levels[start_of_seq];
97
98 #[cfg(test)]
99 for run in sequence.clone() {
100 for idx in run {
101 if not_removed_by_x9(&original_classes[idx]) {
102 assert_eq!(seq_level, levels[idx]);
103 }
104 }
105 }
106
107 let pred_level = match original_classes[..start_of_seq]
109 .iter()
110 .rposition(not_removed_by_x9)
111 {
112 Some(idx) => levels[idx],
113 None => para_level,
114 };
115
116 let succ_level = if matches!(original_classes[end_of_seq - 1], RLI | LRI | FSI) {
118 para_level
119 } else {
120 match original_classes[end_of_seq..]
121 .iter()
122 .position(not_removed_by_x9)
123 {
124 Some(idx) => levels[end_of_seq + idx],
125 None => para_level,
126 }
127 };
128
129 IsolatingRunSequence {
130 runs: sequence,
131 sos: max(seq_level, pred_level).bidi_class(),
132 eos: max(seq_level, succ_level).bidi_class(),
133 }
134 })
135 .collect()
136}
137
138fn level_runs(levels: &[Level], original_classes: &[BidiClass]) -> Vec<LevelRun> {
142 assert_eq!(levels.len(), original_classes.len());
143
144 let mut runs = Vec::new();
145 if levels.is_empty() {
146 return runs;
147 }
148
149 let mut current_run_level = levels[0];
150 let mut current_run_start = 0;
151 for i in 1..levels.len() {
152 if !removed_by_x9(original_classes[i]) && levels[i] != current_run_level {
153 runs.push(current_run_start..i);
155 current_run_level = levels[i];
156 current_run_start = i;
157 }
158 }
159 runs.push(current_run_start..levels.len());
160
161 runs
162}
163
164pub fn removed_by_x9(class: BidiClass) -> bool {
168 matches!(class, RLE | LRE | RLO | LRO | PDF | BN)
169}
170
171pub fn not_removed_by_x9(class: &BidiClass) -> bool {
173 !removed_by_x9(*class)
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_level_runs() {
182 assert_eq!(level_runs(&Level::vec(&[]), &[]), &[]);
183 assert_eq!(
184 level_runs(&Level::vec(&[0, 0, 0, 1, 1, 2, 0, 0]), &[L; 8]),
185 &[0..3, 3..5, 5..6, 6..8]
186 );
187 }
188
189 #[cfg_attr(rustfmt, rustfmt_skip)]
191 #[test]
192 fn test_isolating_run_sequences() {
193
194 let classes = &[L, RLE, L, PDF, RLE, L, PDF, L];
198 let levels = &[0, 1, 1, 1, 1, 1, 1, 0];
199 let para_level = Level::ltr();
200 let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
201 sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
202 assert_eq!(
203 sequences.iter().map(|s| s.runs.clone()).collect::<Vec<_>>(),
204 vec![vec![0..2], vec![2..7], vec![7..8]]
205 );
206
207 let classes = &[L, RLI, L, PDI, RLI, L, PDI, L];
211 let levels = &[0, 0, 1, 0, 0, 1, 0, 0];
212 let para_level = Level::ltr();
213 let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
214 sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
215 assert_eq!(
216 sequences.iter().map(|s| s.runs.clone()).collect::<Vec<_>>(),
217 vec![vec![0..2, 3..5, 6..8], vec![2..3], vec![5..6]]
218 );
219
220 let classes = &[L, RLI, L, LRI, L, RLE, L, PDF, L, PDI, L, PDI, L];
224 let levels = &[0, 0, 1, 1, 2, 3, 3, 3, 2, 1, 1, 0, 0];
225 let para_level = Level::ltr();
226 let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
227 sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
228 assert_eq!(
229 sequences.iter().map(|s| s.runs.clone()).collect::<Vec<_>>(),
230 vec![vec![0..2, 11..13], vec![2..4, 9..11], vec![4..6], vec![6..8], vec![8..9]]
231 );
232 }
233
234 #[cfg_attr(rustfmt, rustfmt_skip)]
236 #[test]
237 fn test_isolating_run_sequences_sos_and_eos() {
238
239 let classes = &[L, RLE, L, LRE, L, PDF, L, PDF, RLE, L, PDF, L];
243 let levels = &[0, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0];
244 let para_level = Level::ltr();
245 let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
246 sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
247
248 assert_eq!(
250 &sequences[0],
251 &IsolatingRunSequence {
252 runs: vec![0..2],
253 sos: L,
254 eos: R,
255 }
256 );
257
258 assert_eq!(
260 &sequences[1],
261 &IsolatingRunSequence {
262 runs: vec![2..4],
263 sos: R,
264 eos: L,
265 }
266 );
267
268 assert_eq!(
270 &sequences[2],
271 &IsolatingRunSequence {
272 runs: vec![4..6],
273 sos: L,
274 eos: L,
275 }
276 );
277
278 assert_eq!(
280 &sequences[3],
281 &IsolatingRunSequence {
282 runs: vec![6..11],
283 sos: L,
284 eos: R,
285 }
286 );
287
288 assert_eq!(
290 &sequences[4],
291 &IsolatingRunSequence {
292 runs: vec![11..12],
293 sos: R,
294 eos: L,
295 }
296 );
297
298 let classes = &[L, RLI, L, LRI, L, PDI, L, PDI, RLI, L, PDI, L];
302 let levels = &[0, 0, 1, 1, 2, 1, 1, 0, 0, 1, 0, 0];
303 let para_level = Level::ltr();
304 let mut sequences = isolating_run_sequences(para_level, classes, &Level::vec(levels));
305 sequences.sort_by(|a, b| a.runs[0].clone().cmp(b.runs[0].clone()));
306
307 assert_eq!(
309 &sequences[0],
310 &IsolatingRunSequence {
311 runs: vec![0..2, 7..9, 10..12],
312 sos: L,
313 eos: L,
314 }
315 );
316
317 assert_eq!(
319 &sequences[1],
320 &IsolatingRunSequence {
321 runs: vec![2..4, 5..7],
322 sos: R,
323 eos: R,
324 }
325 );
326
327 assert_eq!(
329 &sequences[2],
330 &IsolatingRunSequence {
331 runs: vec![4..5],
332 sos: L,
333 eos: L,
334 }
335 );
336
337 assert_eq!(
339 &sequences[3],
340 &IsolatingRunSequence {
341 runs: vec![9..10],
342 sos: R,
343 eos: R,
344 }
345 );
346 }
347
348 #[test]
349 fn test_removed_by_x9() {
350 let rem_classes = &[RLE, LRE, RLO, LRO, PDF, BN];
351 let not_classes = &[L, RLI, AL, LRI, PDI];
352 for x in rem_classes {
353 assert_eq!(removed_by_x9(*x), true);
354 }
355 for x in not_classes {
356 assert_eq!(removed_by_x9(*x), false);
357 }
358 }
359
360 #[test]
361 fn test_not_removed_by_x9() {
362 let non_x9_classes = &[
363 L, R, AL, EN, ES, ET, AN, CS, NSM, B, S, WS, ON, LRI, RLI, FSI, PDI,
364 ];
365 for x in non_x9_classes {
366 assert_eq!(not_removed_by_x9(&x), true);
367 }
368 }
369}