1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//! The Unicode `General_Category` property and its major groupings.
/// A Unicode `General_Category` value (UAX #44).
///
/// Every codepoint has exactly one general category. Codepoints that are
/// unassigned — or that fall outside the codepoint range compiled in via the
/// crate's feature tiers — report [`GeneralCategory::Unassigned`] (`Cn`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum GeneralCategory {
// Letter
/// `Lu` — Uppercase Letter
UppercaseLetter = 0,
/// `Ll` — Lowercase Letter
LowercaseLetter,
/// `Lt` — Titlecase Letter
TitlecaseLetter,
/// `Lm` — Modifier Letter
ModifierLetter,
/// `Lo` — Other Letter
OtherLetter,
// Mark
/// `Mn` — Nonspacing Mark
NonspacingMark,
/// `Mc` — Spacing Mark
SpacingMark,
/// `Me` — Enclosing Mark
EnclosingMark,
// Number
/// `Nd` — Decimal Number
DecimalNumber,
/// `Nl` — Letter Number
LetterNumber,
/// `No` — Other Number
OtherNumber,
// Punctuation
/// `Pc` — Connector Punctuation
ConnectorPunctuation,
/// `Pd` — Dash Punctuation
DashPunctuation,
/// `Ps` — Open Punctuation
OpenPunctuation,
/// `Pe` — Close Punctuation
ClosePunctuation,
/// `Pi` — Initial Punctuation
InitialPunctuation,
/// `Pf` — Final Punctuation
FinalPunctuation,
/// `Po` — Other Punctuation
OtherPunctuation,
// Symbol
/// `Sm` — Math Symbol
MathSymbol,
/// `Sc` — Currency Symbol
CurrencySymbol,
/// `Sk` — Modifier Symbol
ModifierSymbol,
/// `So` — Other Symbol
OtherSymbol,
// Separator
/// `Zs` — Space Separator
SpaceSeparator,
/// `Zl` — Line Separator
LineSeparator,
/// `Zp` — Paragraph Separator
ParagraphSeparator,
// Other
/// `Cc` — Control
Control,
/// `Cf` — Format
Format,
/// `Cs` — Surrogate
Surrogate,
/// `Co` — Private Use
PrivateUse,
/// `Cn` — Unassigned (also reported for non-compiled ranges)
Unassigned,
}
/// The seven major-class groupings of [`GeneralCategory`] (the first letter of
/// the two-letter abbreviation): `L`, `M`, `N`, `P`, `S`, `Z`, `C`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Group {
/// `L` — Letter
Letter,
/// `M` — Mark
Mark,
/// `N` — Number
Number,
/// `P` — Punctuation
Punctuation,
/// `S` — Symbol
Symbol,
/// `Z` — Separator
Separator,
/// `C` — Other
Other,
}
impl GeneralCategory {
/// The major-class [`Group`] this category belongs to.
#[inline]
#[must_use]
pub const fn group(self) -> Group {
use GeneralCategory::*;
match self {
UppercaseLetter | LowercaseLetter | TitlecaseLetter | ModifierLetter | OtherLetter => {
Group::Letter
}
NonspacingMark | SpacingMark | EnclosingMark => Group::Mark,
DecimalNumber | LetterNumber | OtherNumber => Group::Number,
ConnectorPunctuation | DashPunctuation | OpenPunctuation | ClosePunctuation
| InitialPunctuation | FinalPunctuation | OtherPunctuation => Group::Punctuation,
MathSymbol | CurrencySymbol | ModifierSymbol | OtherSymbol => Group::Symbol,
SpaceSeparator | LineSeparator | ParagraphSeparator => Group::Separator,
Control | Format | Surrogate | PrivateUse | Unassigned => Group::Other,
}
}
/// The canonical two-letter abbreviation, e.g. `"Lu"` for
/// [`GeneralCategory::UppercaseLetter`].
#[inline]
#[must_use]
pub const fn abbr(self) -> &'static str {
use GeneralCategory::*;
match self {
UppercaseLetter => "Lu",
LowercaseLetter => "Ll",
TitlecaseLetter => "Lt",
ModifierLetter => "Lm",
OtherLetter => "Lo",
NonspacingMark => "Mn",
SpacingMark => "Mc",
EnclosingMark => "Me",
DecimalNumber => "Nd",
LetterNumber => "Nl",
OtherNumber => "No",
ConnectorPunctuation => "Pc",
DashPunctuation => "Pd",
OpenPunctuation => "Ps",
ClosePunctuation => "Pe",
InitialPunctuation => "Pi",
FinalPunctuation => "Pf",
OtherPunctuation => "Po",
MathSymbol => "Sm",
CurrencySymbol => "Sc",
ModifierSymbol => "Sk",
OtherSymbol => "So",
SpaceSeparator => "Zs",
LineSeparator => "Zl",
ParagraphSeparator => "Zp",
Control => "Cc",
Format => "Cf",
Surrogate => "Cs",
PrivateUse => "Co",
Unassigned => "Cn",
}
}
/// `true` if this is any letter (`L*`).
#[inline]
#[must_use]
pub const fn is_letter(self) -> bool {
matches!(self.group(), Group::Letter)
}
/// `true` if this is a cased letter (`Lu`, `Ll`, or `Lt`).
#[inline]
#[must_use]
pub const fn is_cased_letter(self) -> bool {
use GeneralCategory::*;
matches!(self, UppercaseLetter | LowercaseLetter | TitlecaseLetter)
}
/// `true` if this is any mark (`M*`).
#[inline]
#[must_use]
pub const fn is_mark(self) -> bool {
matches!(self.group(), Group::Mark)
}
/// `true` if this is any number (`N*`).
#[inline]
#[must_use]
pub const fn is_number(self) -> bool {
matches!(self.group(), Group::Number)
}
/// `true` if this is any punctuation (`P*`).
#[inline]
#[must_use]
pub const fn is_punctuation(self) -> bool {
matches!(self.group(), Group::Punctuation)
}
/// `true` if this is any symbol (`S*`).
#[inline]
#[must_use]
pub const fn is_symbol(self) -> bool {
matches!(self.group(), Group::Symbol)
}
/// `true` if this is any separator (`Z*`).
#[inline]
#[must_use]
pub const fn is_separator(self) -> bool {
matches!(self.group(), Group::Separator)
}
/// `true` if this is in the "Other" group (`C*`): control, format,
/// surrogate, private-use, or unassigned.
#[inline]
#[must_use]
pub const fn is_other(self) -> bool {
matches!(self.group(), Group::Other)
}
/// `true` if a codepoint with this category is assigned, i.e. not
/// [`GeneralCategory::Unassigned`].
///
/// Note: private-use (`Co`) and surrogate (`Cs`) codepoints count as
/// assigned; only `Cn` is unassigned.
#[inline]
#[must_use]
pub const fn is_assigned(self) -> bool {
!matches!(self, GeneralCategory::Unassigned)
}
}