1use std::{
2 fmt,
3 sync::{
4 Arc,
5 atomic::{AtomicBool, Ordering},
6 },
7 time::Instant,
8};
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13pub struct SourceRange {
14 pub(crate) start: usize,
15 pub(crate) end: usize,
16}
17
18impl SourceRange {
19 pub const fn new(start: usize, end: usize) -> Self {
21 Self { start, end }
22 }
23 pub const fn start(self) -> usize {
25 self.start
26 }
27 pub const fn end(self) -> usize {
29 self.end
30 }
31}
32
33#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub enum AmbiguityPolicy {
37 #[default]
39 Preserve,
40 Reject,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub enum HintKind {
48 Cardinal,
50 Digits,
52 Date,
54 Time,
56 Ordinal,
58 Roman,
60 Range,
62 Telephone,
64 Electronic,
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub struct Hint {
71 pub(crate) range: SourceRange,
72 pub(crate) kind: HintKind,
73}
74
75impl Hint {
76 pub const fn new(range: SourceRange, kind: HintKind) -> Self {
78 Self { range, kind }
79 }
80 pub const fn range(self) -> SourceRange {
82 self.range
83 }
84 pub const fn kind(self) -> HintKind {
86 self.kind
87 }
88}
89
90#[derive(Clone, Debug, Default)]
92pub struct NormalizeOptions {
93 pub ambiguity_policy: AmbiguityPolicy,
95 pub hints: Vec<Hint>,
97}
98
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
101#[cfg_attr(feature = "serde", derive(serde::Serialize))]
102pub enum SegmentKind {
103 Verbatim,
105 Unresolved,
107 Cardinal,
109 Ordinal,
111 Decimal,
113 Digits,
115 Percent,
117 Money,
119 Unit,
121 Date,
123 Time,
125 Abbreviation,
127 Range,
129 Telephone,
131 Iban,
133 Roman,
135 Electronic,
137 Symbol,
139}
140
141#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize))]
144pub enum IssueCategory {
145 Ambiguous,
147 InvalidExpression,
149 ProtectedIdentifier,
151 Unsupported,
153 UnknownAbbreviation,
155}
156
157#[derive(Clone, Debug, Eq, PartialEq)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize))]
160pub struct Issue {
161 pub(crate) range: SourceRange,
162 pub(crate) category: IssueCategory,
163 pub(crate) explanation: &'static str,
164}
165
166impl Issue {
167 pub const fn range(&self) -> SourceRange {
169 self.range
170 }
171 pub const fn category(&self) -> IssueCategory {
173 self.category
174 }
175 pub const fn explanation(&self) -> &'static str {
177 self.explanation
178 }
179}
180
181#[derive(Clone, Debug, Eq, PartialEq)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize))]
184pub struct Segment {
185 pub(crate) range: SourceRange,
186 pub(crate) kind: SegmentKind,
187 pub(crate) text: String,
188 pub(crate) rule_id: &'static str,
189}
190
191impl Segment {
192 pub const fn range(&self) -> SourceRange {
194 self.range
195 }
196 pub const fn kind(&self) -> SegmentKind {
198 self.kind
199 }
200 pub fn text(&self) -> &str {
202 &self.text
203 }
204 pub const fn rule_id(&self) -> &'static str {
206 self.rule_id
207 }
208}
209
210#[derive(Clone, Debug, Eq, PartialEq)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize))]
213pub struct NormalizeResult {
214 pub(crate) normalized_text: String,
215 pub(crate) locale: &'static str,
216 pub(crate) normalizer_id: &'static str,
217 pub(crate) complete: bool,
218 pub(crate) segments: Vec<Segment>,
219 pub(crate) issues: Vec<Issue>,
220}
221
222impl NormalizeResult {
223 pub fn normalized_text(&self) -> &str {
225 &self.normalized_text
226 }
227 pub const fn locale(&self) -> &'static str {
229 self.locale
230 }
231 pub const fn normalizer_id(&self) -> &'static str {
233 self.normalizer_id
234 }
235 pub fn complete(&self) -> bool {
237 self.complete
238 }
239 pub fn segments(&self) -> &[Segment] {
241 &self.segments
242 }
243 pub fn issues(&self) -> &[Issue] {
245 &self.issues
246 }
247}
248
249#[derive(Clone, Copy, Debug, Eq, PartialEq)]
251pub enum LimitKind {
252 Input,
254 Hints,
256 Candidates,
258 Result,
260}
261
262#[derive(Clone, Debug, Eq, PartialEq)]
264pub enum NormalizeError {
265 InvalidInput,
267 InvalidHint,
269 InvalidConfiguration,
271 LimitExceeded(LimitKind),
273 Cancelled,
275 Unresolved(Vec<Issue>),
277 Internal,
279}
280
281impl fmt::Display for NormalizeError {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 let message = match self {
284 Self::InvalidInput => "input is empty, whitespace-only, or contains forbidden controls",
285 Self::InvalidHint => "hint is not a valid whole-expression original-source range",
286 Self::InvalidConfiguration => "built-in normalizer configuration is invalid",
287 Self::LimitExceeded(_) => "normalization resource limit exceeded",
288 Self::Cancelled => "normalization was cancelled or its deadline expired",
289 Self::Unresolved(_) => "strict normalization contains unresolved linguistic work",
290 Self::Internal => "normalization invariant failed",
291 };
292 f.write_str(message)
293 }
294}
295
296impl std::error::Error for NormalizeError {}
297
298#[derive(Clone, Debug, Default)]
300pub struct WorkControl {
301 cancelled: Arc<AtomicBool>,
302 deadline: Option<Instant>,
303}
304
305impl WorkControl {
306 pub fn new(deadline: Option<Instant>) -> Self {
308 Self {
309 deadline,
310 ..Self::default()
311 }
312 }
313 pub fn cancel(&self) {
315 self.cancelled.store(true, Ordering::Relaxed);
316 }
317 pub(crate) fn check(&self) -> Result<(), NormalizeError> {
318 if self.cancelled.load(Ordering::Relaxed)
319 || self
320 .deadline
321 .is_some_and(|deadline| Instant::now() >= deadline)
322 {
323 Err(NormalizeError::Cancelled)
324 } else {
325 Ok(())
326 }
327 }
328}