unicode_bidi_mirroring/
lib.rs

1/*!
2
3This library implements
4[Unicode Bidi Mirroring](https://unicode.org/reports/tr44/#BidiMirroring.txt) property detection.
5
6```rust
7use unicode_bidi_mirroring::*;
8
9assert_eq!(get_mirrored('A'), None);
10assert_eq!(get_mirrored('\u{2039}'), Some('\u{203A}'));
11assert_eq!(get_mirrored('\u{203A}'), Some('\u{2039}'));
12
13assert_eq!(is_mirroring('A'), false);
14assert_eq!(is_mirroring('\u{29C4}'), true);
15assert_eq!(is_mirroring('\u{22FF}'), true);
16```
17
18*/
19
20#![no_std]
21#![forbid(unsafe_code)]
22
23/// The Unicode version.
24pub const UNICODE_VERSION: (u8, u8, u8) = (14, 0, 0);
25
26const PAIRS: &[(char, char)] = &[
27    ('\u{0028}', '\u{0029}'),
28    ('\u{003C}', '\u{003E}'),
29    ('\u{005B}', '\u{005D}'),
30    ('\u{007B}', '\u{007D}'),
31    ('\u{00AB}', '\u{00BB}'),
32    ('\u{0F3A}', '\u{0F3B}'),
33    ('\u{0F3C}', '\u{0F3D}'),
34    ('\u{169B}', '\u{169C}'),
35    ('\u{2039}', '\u{203A}'),
36    ('\u{2045}', '\u{2046}'),
37    ('\u{207D}', '\u{207E}'),
38    ('\u{208D}', '\u{208E}'),
39    ('\u{2208}', '\u{220B}'),
40    ('\u{2209}', '\u{220C}'),
41    ('\u{220A}', '\u{220D}'),
42    ('\u{2215}', '\u{29F5}'),
43    ('\u{221F}', '\u{2BFE}'),
44    ('\u{2220}', '\u{29A3}'),
45    ('\u{2221}', '\u{299B}'),
46    ('\u{2222}', '\u{29A0}'),
47    ('\u{2224}', '\u{2AEE}'),
48    ('\u{223C}', '\u{223D}'),
49    ('\u{2243}', '\u{22CD}'),
50    ('\u{2245}', '\u{224C}'),
51    ('\u{2252}', '\u{2253}'),
52    ('\u{2254}', '\u{2255}'),
53    ('\u{2264}', '\u{2265}'),
54    ('\u{2266}', '\u{2267}'),
55    ('\u{2268}', '\u{2269}'),
56    ('\u{226A}', '\u{226B}'),
57    ('\u{226E}', '\u{226F}'),
58    ('\u{2270}', '\u{2271}'),
59    ('\u{2272}', '\u{2273}'),
60    ('\u{2274}', '\u{2275}'),
61    ('\u{2276}', '\u{2277}'),
62    ('\u{2278}', '\u{2279}'),
63    ('\u{227A}', '\u{227B}'),
64    ('\u{227C}', '\u{227D}'),
65    ('\u{227E}', '\u{227F}'),
66    ('\u{2280}', '\u{2281}'),
67    ('\u{2282}', '\u{2283}'),
68    ('\u{2284}', '\u{2285}'),
69    ('\u{2286}', '\u{2287}'),
70    ('\u{2288}', '\u{2289}'),
71    ('\u{228A}', '\u{228B}'),
72    ('\u{228F}', '\u{2290}'),
73    ('\u{2291}', '\u{2292}'),
74    ('\u{2298}', '\u{29B8}'),
75    ('\u{22A2}', '\u{22A3}'),
76    ('\u{22A6}', '\u{2ADE}'),
77    ('\u{22A8}', '\u{2AE4}'),
78    ('\u{22A9}', '\u{2AE3}'),
79    ('\u{22AB}', '\u{2AE5}'),
80    ('\u{22B0}', '\u{22B1}'),
81    ('\u{22B2}', '\u{22B3}'),
82    ('\u{22B4}', '\u{22B5}'),
83    ('\u{22B6}', '\u{22B7}'),
84    ('\u{22B8}', '\u{27DC}'),
85    ('\u{22C9}', '\u{22CA}'),
86    ('\u{22CB}', '\u{22CC}'),
87    ('\u{22D0}', '\u{22D1}'),
88    ('\u{22D6}', '\u{22D7}'),
89    ('\u{22D8}', '\u{22D9}'),
90    ('\u{22DA}', '\u{22DB}'),
91    ('\u{22DC}', '\u{22DD}'),
92    ('\u{22DE}', '\u{22DF}'),
93    ('\u{22E0}', '\u{22E1}'),
94    ('\u{22E2}', '\u{22E3}'),
95    ('\u{22E4}', '\u{22E5}'),
96    ('\u{22E6}', '\u{22E7}'),
97    ('\u{22E8}', '\u{22E9}'),
98    ('\u{22EA}', '\u{22EB}'),
99    ('\u{22EC}', '\u{22ED}'),
100    ('\u{22F0}', '\u{22F1}'),
101    ('\u{22F2}', '\u{22FA}'),
102    ('\u{22F3}', '\u{22FB}'),
103    ('\u{22F4}', '\u{22FC}'),
104    ('\u{22F6}', '\u{22FD}'),
105    ('\u{22F7}', '\u{22FE}'),
106    ('\u{2308}', '\u{2309}'),
107    ('\u{230A}', '\u{230B}'),
108    ('\u{2329}', '\u{232A}'),
109    ('\u{2768}', '\u{2769}'),
110    ('\u{276A}', '\u{276B}'),
111    ('\u{276C}', '\u{276D}'),
112    ('\u{276E}', '\u{276F}'),
113    ('\u{2770}', '\u{2771}'),
114    ('\u{2772}', '\u{2773}'),
115    ('\u{2774}', '\u{2775}'),
116    ('\u{27C3}', '\u{27C4}'),
117    ('\u{27C5}', '\u{27C6}'),
118    ('\u{27C8}', '\u{27C9}'),
119    ('\u{27CB}', '\u{27CD}'),
120    ('\u{27D5}', '\u{27D6}'),
121    ('\u{27DD}', '\u{27DE}'),
122    ('\u{27E2}', '\u{27E3}'),
123    ('\u{27E4}', '\u{27E5}'),
124    ('\u{27E6}', '\u{27E7}'),
125    ('\u{27E8}', '\u{27E9}'),
126    ('\u{27EA}', '\u{27EB}'),
127    ('\u{27EC}', '\u{27ED}'),
128    ('\u{27EE}', '\u{27EF}'),
129    ('\u{2983}', '\u{2984}'),
130    ('\u{2985}', '\u{2986}'),
131    ('\u{2987}', '\u{2988}'),
132    ('\u{2989}', '\u{298A}'),
133    ('\u{298B}', '\u{298C}'),
134    ('\u{298D}', '\u{2990}'),
135    ('\u{298E}', '\u{298F}'),
136    ('\u{2991}', '\u{2992}'),
137    ('\u{2993}', '\u{2994}'),
138    ('\u{2995}', '\u{2996}'),
139    ('\u{2997}', '\u{2998}'),
140    ('\u{29A4}', '\u{29A5}'),
141    ('\u{29A8}', '\u{29A9}'),
142    ('\u{29AA}', '\u{29AB}'),
143    ('\u{29AC}', '\u{29AD}'),
144    ('\u{29AE}', '\u{29AF}'),
145    ('\u{29C0}', '\u{29C1}'),
146    ('\u{29C4}', '\u{29C5}'),
147    ('\u{29CF}', '\u{29D0}'),
148    ('\u{29D1}', '\u{29D2}'),
149    ('\u{29D4}', '\u{29D5}'),
150    ('\u{29D8}', '\u{29D9}'),
151    ('\u{29DA}', '\u{29DB}'),
152    ('\u{29E8}', '\u{29E9}'),
153    ('\u{29F8}', '\u{29F9}'),
154    ('\u{29FC}', '\u{29FD}'),
155    ('\u{2A2B}', '\u{2A2C}'),
156    ('\u{2A2D}', '\u{2A2E}'),
157    ('\u{2A34}', '\u{2A35}'),
158    ('\u{2A3C}', '\u{2A3D}'),
159    ('\u{2A64}', '\u{2A65}'),
160    ('\u{2A79}', '\u{2A7A}'),
161    ('\u{2A7B}', '\u{2A7C}'),
162    ('\u{2A7D}', '\u{2A7E}'),
163    ('\u{2A7F}', '\u{2A80}'),
164    ('\u{2A81}', '\u{2A82}'),
165    ('\u{2A83}', '\u{2A84}'),
166    ('\u{2A85}', '\u{2A86}'),
167    ('\u{2A87}', '\u{2A88}'),
168    ('\u{2A89}', '\u{2A8A}'),
169    ('\u{2A8B}', '\u{2A8C}'),
170    ('\u{2A8D}', '\u{2A8E}'),
171    ('\u{2A8F}', '\u{2A90}'),
172    ('\u{2A91}', '\u{2A92}'),
173    ('\u{2A93}', '\u{2A94}'),
174    ('\u{2A95}', '\u{2A96}'),
175    ('\u{2A97}', '\u{2A98}'),
176    ('\u{2A99}', '\u{2A9A}'),
177    ('\u{2A9B}', '\u{2A9C}'),
178    ('\u{2A9D}', '\u{2A9E}'),
179    ('\u{2A9F}', '\u{2AA0}'),
180    ('\u{2AA1}', '\u{2AA2}'),
181    ('\u{2AA6}', '\u{2AA7}'),
182    ('\u{2AA8}', '\u{2AA9}'),
183    ('\u{2AAA}', '\u{2AAB}'),
184    ('\u{2AAC}', '\u{2AAD}'),
185    ('\u{2AAF}', '\u{2AB0}'),
186    ('\u{2AB1}', '\u{2AB2}'),
187    ('\u{2AB3}', '\u{2AB4}'),
188    ('\u{2AB5}', '\u{2AB6}'),
189    ('\u{2AB7}', '\u{2AB8}'),
190    ('\u{2AB9}', '\u{2ABA}'),
191    ('\u{2ABB}', '\u{2ABC}'),
192    ('\u{2ABD}', '\u{2ABE}'),
193    ('\u{2ABF}', '\u{2AC0}'),
194    ('\u{2AC1}', '\u{2AC2}'),
195    ('\u{2AC3}', '\u{2AC4}'),
196    ('\u{2AC5}', '\u{2AC6}'),
197    ('\u{2AC7}', '\u{2AC8}'),
198    ('\u{2AC9}', '\u{2ACA}'),
199    ('\u{2ACB}', '\u{2ACC}'),
200    ('\u{2ACD}', '\u{2ACE}'),
201    ('\u{2ACF}', '\u{2AD0}'),
202    ('\u{2AD1}', '\u{2AD2}'),
203    ('\u{2AD3}', '\u{2AD4}'),
204    ('\u{2AD5}', '\u{2AD6}'),
205    ('\u{2AEC}', '\u{2AED}'),
206    ('\u{2AF7}', '\u{2AF8}'),
207    ('\u{2AF9}', '\u{2AFA}'),
208    ('\u{2E02}', '\u{2E03}'),
209    ('\u{2E04}', '\u{2E05}'),
210    ('\u{2E09}', '\u{2E0A}'),
211    ('\u{2E0C}', '\u{2E0D}'),
212    ('\u{2E1C}', '\u{2E1D}'),
213    ('\u{2E20}', '\u{2E21}'),
214    ('\u{2E22}', '\u{2E23}'),
215    ('\u{2E24}', '\u{2E25}'),
216    ('\u{2E26}', '\u{2E27}'),
217    ('\u{2E28}', '\u{2E29}'),
218    ('\u{2E55}', '\u{2E56}'),
219    ('\u{2E57}', '\u{2E58}'),
220    ('\u{2E59}', '\u{2E5A}'),
221    ('\u{2E5B}', '\u{2E5C}'),
222    ('\u{3008}', '\u{3009}'),
223    ('\u{300A}', '\u{300B}'),
224    ('\u{300C}', '\u{300D}'),
225    ('\u{300E}', '\u{300F}'),
226    ('\u{3010}', '\u{3011}'),
227    ('\u{3014}', '\u{3015}'),
228    ('\u{3016}', '\u{3017}'),
229    ('\u{3018}', '\u{3019}'),
230    ('\u{301A}', '\u{301B}'),
231    ('\u{FE59}', '\u{FE5A}'),
232    ('\u{FE5B}', '\u{FE5C}'),
233    ('\u{FE5D}', '\u{FE5E}'),
234    ('\u{FE64}', '\u{FE65}'),
235    ('\u{FF08}', '\u{FF09}'),
236    ('\u{FF1C}', '\u{FF1E}'),
237    ('\u{FF3B}', '\u{FF3D}'),
238    ('\u{FF5B}', '\u{FF5D}'),
239    ('\u{FF5F}', '\u{FF60}'),
240    ('\u{FF62}', '\u{FF63}'),
241];
242
243const OTHER: &[char] = &[
244    '\u{2140}',
245    '\u{2201}',
246    '\u{2202}',
247    '\u{2203}',
248    '\u{2204}',
249    '\u{2211}',
250    '\u{2216}',
251    '\u{221A}',
252    '\u{221B}',
253    '\u{221C}',
254    '\u{221D}',
255    '\u{2226}',
256    '\u{222B}',
257    '\u{222C}',
258    '\u{222D}',
259    '\u{222E}',
260    '\u{222F}',
261    '\u{2230}',
262    '\u{2231}',
263    '\u{2232}',
264    '\u{2233}',
265    '\u{2239}',
266    '\u{223B}',
267    '\u{223E}',
268    '\u{223F}',
269    '\u{2240}',
270    '\u{2241}',
271    '\u{2242}',
272    '\u{2244}',
273    '\u{2246}',
274    '\u{2247}',
275    '\u{2248}',
276    '\u{2249}',
277    '\u{224A}',
278    '\u{224B}',
279    '\u{225F}',
280    '\u{2260}',
281    '\u{2262}',
282    '\u{228C}',
283    '\u{22A7}',
284    '\u{22AA}',
285    '\u{22AC}',
286    '\u{22AD}',
287    '\u{22AE}',
288    '\u{22AF}',
289    '\u{22BE}',
290    '\u{22BF}',
291    '\u{22F5}',
292    '\u{22F8}',
293    '\u{22F9}',
294    '\u{22FF}',
295    '\u{2320}',
296    '\u{2321}',
297    '\u{27C0}',
298    '\u{27CC}',
299    '\u{27D3}',
300    '\u{27D4}',
301    '\u{299C}',
302    '\u{299D}',
303    '\u{299E}',
304    '\u{299F}',
305    '\u{29A2}',
306    '\u{29A6}',
307    '\u{29A7}',
308    '\u{29C2}',
309    '\u{29C3}',
310    '\u{29C9}',
311    '\u{29CE}',
312    '\u{29DC}',
313    '\u{29E1}',
314    '\u{29E3}',
315    '\u{29E4}',
316    '\u{29E5}',
317    '\u{29F4}',
318    '\u{29F6}',
319    '\u{29F7}',
320    '\u{2A0A}',
321    '\u{2A0B}',
322    '\u{2A0C}',
323    '\u{2A0D}',
324    '\u{2A0E}',
325    '\u{2A0F}',
326    '\u{2A10}',
327    '\u{2A11}',
328    '\u{2A12}',
329    '\u{2A13}',
330    '\u{2A14}',
331    '\u{2A15}',
332    '\u{2A16}',
333    '\u{2A17}',
334    '\u{2A18}',
335    '\u{2A19}',
336    '\u{2A1A}',
337    '\u{2A1B}',
338    '\u{2A1C}',
339    '\u{2A1E}',
340    '\u{2A1F}',
341    '\u{2A20}',
342    '\u{2A21}',
343    '\u{2A24}',
344    '\u{2A26}',
345    '\u{2A29}',
346    '\u{2A3E}',
347    '\u{2A57}',
348    '\u{2A58}',
349    '\u{2A6A}',
350    '\u{2A6B}',
351    '\u{2A6C}',
352    '\u{2A6D}',
353    '\u{2A6F}',
354    '\u{2A70}',
355    '\u{2A73}',
356    '\u{2A74}',
357    '\u{2AA3}',
358    '\u{2ADC}',
359    '\u{2AE2}',
360    '\u{2AE6}',
361    '\u{2AF3}',
362    '\u{2AFB}',
363    '\u{2AFD}',
364    '\u{1D6DB}',
365    '\u{1D715}',
366    '\u{1D74F}',
367    '\u{1D789}',
368    '\u{1D7C3}',
369];
370
371/// Returns a bidi mirrored character.
372///
373/// Based on <https://www.unicode.org/Public/UNIDATA/BidiMirroring.txt>.
374pub fn get_mirrored(c: char) -> Option<char> {
375    if let Ok(idx) = PAIRS.binary_search_by(|v| v.0.cmp(&c)) {
376        return Some(PAIRS[idx].1);
377    }
378
379    if let Ok(idx) = PAIRS.binary_search_by(|v| v.1.cmp(&c)) {
380        return Some(PAIRS[idx].0);
381    }
382
383    None
384}
385
386/// Checks that character is bidi mirrored.
387///
388/// Based on <https://www.unicode.org/Public/UNIDATA/BidiMirroring.txt>.
389pub fn is_mirroring(c: char) -> bool {
390    PAIRS.binary_search_by(|v| v.0.cmp(&c)).is_ok() ||
391    PAIRS.binary_search_by(|v| v.1.cmp(&c)).is_ok() ||
392    OTHER.binary_search_by(|v| v.cmp(&c)).is_ok()
393}