1#![warn(
13 bad_style,
14 missing_debug_implementations,
15 missing_docs,
16 unconditional_recursion
17)]
18#![forbid(unsafe_code)]
19
20mod decompose;
40mod recompose;
41
42use std::str::Chars;
43
44pub use crate::decompose::Decompositions;
45pub use crate::recompose::Recompositions;
46pub use unic_ucd_normal::UNICODE_VERSION;
47
48mod pkg_info;
49pub use crate::pkg_info::{PKG_DESCRIPTION, PKG_NAME, PKG_VERSION};
50
51pub trait StrNormalForm<I: Iterator<Item = char>> {
55 fn nfd(self) -> Decompositions<I>;
58
59 fn nfkd(self) -> Decompositions<I>;
62
63 fn nfc(self) -> Recompositions<I>;
66
67 fn nfkc(self) -> Recompositions<I>;
70}
71
72impl<'a> StrNormalForm<Chars<'a>> for &'a str {
73 #[inline]
74 fn nfd(self) -> Decompositions<Chars<'a>> {
75 decompose::new_canonical(self.chars())
76 }
77
78 #[inline]
79 fn nfkd(self) -> Decompositions<Chars<'a>> {
80 decompose::new_compatible(self.chars())
81 }
82
83 #[inline]
84 fn nfc(self) -> Recompositions<Chars<'a>> {
85 recompose::new_canonical(self.chars())
86 }
87
88 #[inline]
89 fn nfkc(self) -> Recompositions<Chars<'a>> {
90 recompose::new_compatible(self.chars())
91 }
92}
93
94impl<I: Iterator<Item = char>> StrNormalForm<I> for I {
95 #[inline]
96 fn nfd(self) -> Decompositions<I> {
97 decompose::new_canonical(self)
98 }
99
100 #[inline]
101 fn nfkd(self) -> Decompositions<I> {
102 decompose::new_compatible(self)
103 }
104
105 #[inline]
106 fn nfc(self) -> Recompositions<I> {
107 recompose::new_canonical(self)
108 }
109
110 #[inline]
111 fn nfkc(self) -> Recompositions<I> {
112 recompose::new_compatible(self)
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::StrNormalForm;
119
120 #[test]
121 fn test_nfd() {
122 macro_rules! nfg_eq {
123 ($input: expr, $expected: expr) => {
124 assert_eq!($input.nfd().to_string(), $expected);
125 assert_eq!(
128 $input.chars().map(|c| c).nfd().collect::<String>(),
129 $expected
130 );
131 };
132 }
133 nfg_eq!("abc", "abc");
134 nfg_eq!("\u{1e0b}\u{1c4}", "d\u{307}\u{1c4}");
135 nfg_eq!("\u{2026}", "\u{2026}");
136 nfg_eq!("\u{2126}", "\u{3a9}");
137 nfg_eq!("\u{1e0b}\u{323}", "d\u{323}\u{307}");
138 nfg_eq!("\u{1e0d}\u{307}", "d\u{323}\u{307}");
139 nfg_eq!("a\u{301}", "a\u{301}");
140 nfg_eq!("\u{301}a", "\u{301}a");
141 nfg_eq!("\u{d4db}", "\u{1111}\u{1171}\u{11b6}");
142 nfg_eq!("\u{ac1c}", "\u{1100}\u{1162}");
143 }
144
145 #[test]
146 fn test_nfkd() {
147 macro_rules! nfkd_eq {
148 ($input: expr, $expected: expr) => {
149 assert_eq!($input.nfkd().to_string(), $expected);
150 };
151 }
152 nfkd_eq!("abc", "abc");
153 nfkd_eq!("\u{1e0b}\u{1c4}", "d\u{307}DZ\u{30c}");
154 nfkd_eq!("\u{2026}", "...");
155 nfkd_eq!("\u{2126}", "\u{3a9}");
156 nfkd_eq!("\u{1e0b}\u{323}", "d\u{323}\u{307}");
157 nfkd_eq!("\u{1e0d}\u{307}", "d\u{323}\u{307}");
158 nfkd_eq!("a\u{301}", "a\u{301}");
159 nfkd_eq!("\u{301}a", "\u{301}a");
160 nfkd_eq!("\u{d4db}", "\u{1111}\u{1171}\u{11b6}");
161 nfkd_eq!("\u{ac1c}", "\u{1100}\u{1162}");
162 }
163
164 #[test]
165 fn test_nfc() {
166 macro_rules! nfc_eq {
167 ($input: expr, $expected: expr) => {
168 assert_eq!($input.nfc().to_string(), $expected);
169 };
170 }
171 nfc_eq!("abc", "abc");
172 nfc_eq!("\u{1e0b}\u{1c4}", "\u{1e0b}\u{1c4}");
173 nfc_eq!("\u{2026}", "\u{2026}");
174 nfc_eq!("\u{2126}", "\u{3a9}");
175 nfc_eq!("\u{1e0b}\u{323}", "\u{1e0d}\u{307}");
176 nfc_eq!("\u{1e0d}\u{307}", "\u{1e0d}\u{307}");
177 nfc_eq!("a\u{301}", "\u{e1}");
178 nfc_eq!("\u{301}a", "\u{301}a");
179 nfc_eq!("\u{d4db}", "\u{d4db}");
180 nfc_eq!("\u{ac1c}", "\u{ac1c}");
181 nfc_eq!(
182 "a\u{300}\u{305}\u{315}\u{5ae}b",
183 "\u{e0}\u{5ae}\u{305}\u{315}b"
184 );
185 }
186
187 #[test]
188 fn test_nfkc() {
189 macro_rules! nfkc_eq {
190 ($input: expr, $expected: expr) => {
191 assert_eq!($input.nfkc().to_string(), $expected);
192 };
193 }
194 nfkc_eq!("abc", "abc");
195 nfkc_eq!("\u{1e0b}\u{1c4}", "\u{1e0b}D\u{17d}");
196 nfkc_eq!("\u{2026}", "...");
197 nfkc_eq!("\u{2126}", "\u{3a9}");
198 nfkc_eq!("\u{1e0b}\u{323}", "\u{1e0d}\u{307}");
199 nfkc_eq!("\u{1e0d}\u{307}", "\u{1e0d}\u{307}");
200 nfkc_eq!("a\u{301}", "\u{e1}");
201 nfkc_eq!("\u{301}a", "\u{301}a");
202 nfkc_eq!("\u{d4db}", "\u{d4db}");
203 nfkc_eq!("\u{ac1c}", "\u{ac1c}");
204 nfkc_eq!(
205 "a\u{300}\u{305}\u{315}\u{5ae}b",
206 "\u{e0}\u{5ae}\u{305}\u{315}b"
207 );
208 }
209}