]> git.proxmox.com Git - cargo.git/blob - vendor/unicode-normalization/src/recompose.rs
New upstream version 0.33.0
[cargo.git] / vendor / unicode-normalization / src / recompose.rs
1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use decompose::Decompositions;
12 use smallvec::SmallVec;
13 use std::fmt::{self, Write};
14
15 #[derive(Clone)]
16 enum RecompositionState {
17 Composing,
18 Purging(usize),
19 Finished(usize),
20 }
21
22 /// External iterator for a string recomposition's characters.
23 #[derive(Clone)]
24 pub struct Recompositions<I> {
25 iter: Decompositions<I>,
26 state: RecompositionState,
27 buffer: SmallVec<[char; 4]>,
28 composee: Option<char>,
29 last_ccc: Option<u8>,
30 }
31
32 #[inline]
33 pub fn new_canonical<I: Iterator<Item=char>>(iter: I) -> Recompositions<I> {
34 Recompositions {
35 iter: super::decompose::new_canonical(iter),
36 state: self::RecompositionState::Composing,
37 buffer: SmallVec::new(),
38 composee: None,
39 last_ccc: None,
40 }
41 }
42
43 #[inline]
44 pub fn new_compatible<I: Iterator<Item=char>>(iter: I) -> Recompositions<I> {
45 Recompositions {
46 iter: super::decompose::new_compatible(iter),
47 state: self::RecompositionState::Composing,
48 buffer: SmallVec::new(),
49 composee: None,
50 last_ccc: None,
51 }
52 }
53
54 impl<I: Iterator<Item=char>> Iterator for Recompositions<I> {
55 type Item = char;
56
57 #[inline]
58 fn next(&mut self) -> Option<char> {
59 use self::RecompositionState::*;
60
61 loop {
62 match self.state {
63 Composing => {
64 for ch in self.iter.by_ref() {
65 let ch_class = super::char::canonical_combining_class(ch);
66 let k = match self.composee {
67 None => {
68 if ch_class != 0 {
69 return Some(ch);
70 }
71 self.composee = Some(ch);
72 continue;
73 },
74 Some(k) => k,
75 };
76 match self.last_ccc {
77 None => {
78 match super::char::compose(k, ch) {
79 Some(r) => {
80 self.composee = Some(r);
81 continue;
82 }
83 None => {
84 if ch_class == 0 {
85 self.composee = Some(ch);
86 return Some(k);
87 }
88 self.buffer.push(ch);
89 self.last_ccc = Some(ch_class);
90 }
91 }
92 }
93 Some(l_class) => {
94 if l_class >= ch_class {
95 // `ch` is blocked from `composee`
96 if ch_class == 0 {
97 self.composee = Some(ch);
98 self.last_ccc = None;
99 self.state = Purging(0);
100 return Some(k);
101 }
102 self.buffer.push(ch);
103 self.last_ccc = Some(ch_class);
104 continue;
105 }
106 match super::char::compose(k, ch) {
107 Some(r) => {
108 self.composee = Some(r);
109 continue;
110 }
111 None => {
112 self.buffer.push(ch);
113 self.last_ccc = Some(ch_class);
114 }
115 }
116 }
117 }
118 }
119 self.state = Finished(0);
120 if self.composee.is_some() {
121 return self.composee.take();
122 }
123 }
124 Purging(next) => {
125 match self.buffer.get(next).cloned() {
126 None => {
127 self.buffer.clear();
128 self.state = Composing;
129 }
130 s => {
131 self.state = Purging(next + 1);
132 return s
133 }
134 }
135 }
136 Finished(next) => {
137 match self.buffer.get(next).cloned() {
138 None => {
139 self.buffer.clear();
140 return self.composee.take()
141 }
142 s => {
143 self.state = Finished(next + 1);
144 return s
145 }
146 }
147 }
148 }
149 }
150 }
151 }
152
153 impl<I: Iterator<Item=char> + Clone> fmt::Display for Recompositions<I> {
154 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155 for c in self.clone() {
156 f.write_char(c)?;
157 }
158 Ok(())
159 }
160 }