1//! [Confusable detection](https://www.unicode.org/reports/tr39/#Confusable_Detection)
2
3use core::iter;
4
5enum OnceOrMore<T, I> {
6 Once(iter::Once<T>),
7 More(I),
8}
9
10impl<T, I> Iterator for OnceOrMore<T, I>
11where
12 I: Iterator<Item = T>,
13{
14 type Item = T;
15
16 fn next(&mut self) -> Option<T> {
17 use OnceOrMore::*;
18 match self {
19 Once(v: impl Iterator) => v.next(),
20 More(i: &mut I) => i.next(),
21 }
22 }
23}
24
25type StaticSliceIterCloned = core::iter::Cloned<core::slice::Iter<'static, char>>;
26
27fn char_prototype(c: char) -> OnceOrMore<char, StaticSliceIterCloned> {
28 use crate::tables::confusable_detection::char_confusable_prototype;
29 match char_confusable_prototype(c) {
30 None => OnceOrMore::Once(iter::once(c)),
31 Some(l: &[char]) => OnceOrMore::More(l.iter().cloned()),
32 }
33}
34
35/// Calculate skeleton for string, as defined by UTS 39
36pub fn skeleton(s: &str) -> impl Iterator<Item = char> + '_ {
37 use unicode_normalization::UnicodeNormalization;
38 s.chars().nfd().flat_map(char_prototype).nfd()
39}
40