1use std::{fmt::Debug, ops::Range};
4
5use crate::Label;
6
7use super::ParseError;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum AllPosition {
15 Phone(PhonePosition),
17 SignedRange(SignedRangePosition),
19 UnsignedRange(UnsignedRangePosition),
21 Boolean(BooleanPosition),
23 Category(CategoryPosition),
25 Undefined(UndefinedPotision),
27}
28
29macro_rules! as_ref_map {
30 ($label:ident.$block:ident.$prop:ident) => {
31 $label.$block.as_ref().map(|b| &b.$prop)
32 };
33}
34
35macro_rules! as_ref_and_then {
36 ($label:ident.$block:ident.$prop:ident) => {
37 $label.$block.as_ref().and_then(|b| b.$prop.as_ref())
38 };
39}
40
41pub trait Position {
43 type Target;
45 type Range;
47
48 fn range(&self, ranges: &[&str]) -> Result<Self::Range, ParseError>;
50 fn get<'a>(&self, label: &'a Label) -> Option<&'a Self::Target>;
52 fn test(&self, range: &Self::Range, target: &Self::Target) -> bool;
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59#[allow(missing_docs)]
60pub enum PhonePosition {
61 P1,
62 P2,
63 P3,
64 P4,
65 P5,
66}
67
68impl Position for PhonePosition {
69 type Target = String;
70 type Range = Vec<String>;
71
72 fn range(&self, ranges: &[&str]) -> Result<Self::Range, ParseError> {
73 Ok(ranges.iter().map(|s| s.to_string()).collect())
74 }
75
76 fn get<'a>(&self, label: &'a Label) -> Option<&'a Self::Target> {
77 match self {
78 Self::P1 => label.phoneme.p2.as_ref(),
79 Self::P2 => label.phoneme.p1.as_ref(),
80 Self::P3 => label.phoneme.c.as_ref(),
81 Self::P4 => label.phoneme.n1.as_ref(),
82 Self::P5 => label.phoneme.n2.as_ref(),
83 }
84 }
85
86 fn test(&self, range: &Self::Range, target: &Self::Target) -> bool {
87 range.contains(target)
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
94#[allow(missing_docs)]
95pub enum SignedRangePosition {
96 A1,
97}
98
99impl Position for SignedRangePosition {
100 type Target = i8;
101 type Range = Range<i8>;
102
103 fn range(&self, ranges: &[&str]) -> Result<Self::Range, ParseError> {
104 let parsed_ranges = ranges.iter().map(range_i8).collect::<Result<Vec<_>, _>>()?;
105 merge_ranges(parsed_ranges)
106 }
107
108 fn get<'a>(&self, label: &'a Label) -> Option<&'a Self::Target> {
109 match self {
110 Self::A1 => as_ref_map!(label.mora.relative_accent_position),
111 }
112 }
113
114 fn test(&self, range: &Self::Range, target: &Self::Target) -> bool {
115 range.contains(target)
116 }
117}
118
119fn range_i8<S: AsRef<str>>(s: S) -> Result<Range<i8>, ParseError> {
120 let range = match s.as_ref() {
121 "-??" => -99..-9,
122 "-?" => -9..0,
123 "?" => 0..10,
124 s if s.ends_with('?') => {
125 let d = s[..s.len() - 1]
126 .parse::<i8>()
127 .map_err(ParseError::FailWildcard)?;
128 if d >= 0 {
129 d * 10..(d + 1) * 10
130 } else {
131 (d - 1) * 10 + 1..d * 10 + 1
132 }
133 }
134 s => {
135 let d = s.parse::<i8>().map_err(ParseError::FailLiteral)?;
136 d..d + 1
137 }
138 };
139 Ok(range)
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
145#[allow(missing_docs)]
146pub enum UnsignedRangePosition {
147 A2,
148 A3,
149
150 E1,
151 E2,
152
153 F1,
154 F2,
155 F5,
156 F6,
157 F7,
158 F8,
159
160 G1,
161 G2,
162
163 H1,
164 H2,
165
166 I1,
167 I2,
168 I3,
169 I4,
170 I5,
171 I6,
172 I7,
173 I8,
174
175 J1,
176 J2,
177
178 K1,
179 K2,
180 K3,
181}
182
183impl Position for UnsignedRangePosition {
184 type Target = u8;
185 type Range = Range<u8>;
186
187 fn range(&self, ranges: &[&str]) -> Result<Self::Range, ParseError> {
188 let parsed_ranges = ranges.iter().map(range_u8).collect::<Result<Vec<_>, _>>()?;
189 merge_ranges(parsed_ranges)
190 }
191
192 fn get<'a>(&self, label: &'a Label) -> Option<&'a Self::Target> {
193 match self {
194 Self::A2 => as_ref_map!(label.mora.position_forward),
195 Self::A3 => as_ref_map!(label.mora.position_backward),
196 Self::E1 => as_ref_map!(label.accent_phrase_prev.mora_count),
197 Self::E2 => as_ref_map!(label.accent_phrase_prev.accent_position),
198 Self::F1 => as_ref_map!(label.accent_phrase_curr.mora_count),
199 Self::F2 => as_ref_map!(label.accent_phrase_curr.accent_position),
200 Self::F5 => as_ref_map!(label.accent_phrase_curr.accent_phrase_position_forward),
201 Self::F6 => as_ref_map!(label.accent_phrase_curr.accent_phrase_position_backward),
202 Self::F7 => as_ref_map!(label.accent_phrase_curr.mora_position_forward),
203 Self::F8 => as_ref_map!(label.accent_phrase_curr.mora_position_backward),
204 Self::G1 => as_ref_map!(label.accent_phrase_next.mora_count),
205 Self::G2 => as_ref_map!(label.accent_phrase_next.accent_position),
206 Self::H1 => as_ref_map!(label.breath_group_prev.accent_phrase_count),
207 Self::H2 => as_ref_map!(label.breath_group_prev.mora_count),
208 Self::I1 => as_ref_map!(label.breath_group_curr.accent_phrase_count),
209 Self::I2 => as_ref_map!(label.breath_group_curr.mora_count),
210 Self::I3 => as_ref_map!(label.breath_group_curr.breath_group_position_forward),
211 Self::I4 => as_ref_map!(label.breath_group_curr.breath_group_position_backward),
212 Self::I5 => as_ref_map!(label.breath_group_curr.accent_phrase_position_forward),
213 Self::I6 => as_ref_map!(label.breath_group_curr.accent_phrase_position_backward),
214 Self::I7 => as_ref_map!(label.breath_group_curr.mora_position_forward),
215 Self::I8 => as_ref_map!(label.breath_group_curr.mora_position_backward),
216 Self::J1 => as_ref_map!(label.breath_group_next.accent_phrase_count),
217 Self::J2 => as_ref_map!(label.breath_group_next.mora_count),
218 Self::K1 => Some(&label.utterance.breath_group_count),
219 Self::K2 => Some(&label.utterance.accent_phrase_count),
220 Self::K3 => Some(&label.utterance.mora_count),
221 }
222 }
223
224 fn test(&self, range: &Self::Range, target: &Self::Target) -> bool {
225 range.contains(target)
226 }
227}
228
229fn range_u8<S: AsRef<str>>(s: S) -> Result<Range<u8>, ParseError> {
230 let range = match s.as_ref() {
231 "?" => 1..10,
232 s if s.ends_with('?') => {
233 let d = s[..s.len() - 1]
234 .parse::<u8>()
235 .map_err(ParseError::FailWildcard)?;
236 d * 10..(d + 1) * 10
237 }
238 s => {
239 let d = s.parse::<u8>().map_err(ParseError::FailLiteral)?;
240 d..d + 1
241 }
242 };
243 Ok(range)
244}
245
246fn merge_ranges<Idx>(mut ranges: Vec<Range<Idx>>) -> Result<Range<Idx>, ParseError>
247where
248 Idx: Ord + Copy,
249{
250 ranges.sort_unstable_by_key(|range| range.start);
251 let merged = ranges
252 .into_iter()
253 .try_fold(None, |acc: Option<Range<Idx>>, curr| match acc {
254 Some(mut acc) if curr.start <= acc.end => {
257 acc.end = acc.end.max(curr.end);
258 Ok(Some(acc))
259 }
260 None => Ok(Some(curr)),
261 _ => Err(ParseError::IncontinuousRange),
262 })?;
263 merged.ok_or(ParseError::Empty)
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
268#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
269#[allow(missing_docs)]
270pub enum BooleanPosition {
271 E3,
272 E5,
273
274 F3,
275
276 G3,
277 G5,
278}
279
280impl Position for BooleanPosition {
281 type Target = bool;
282 type Range = bool;
283
284 fn range(&self, ranges: &[&str]) -> Result<Self::Range, ParseError> {
285 let first = ranges.first().ok_or(ParseError::Empty)?;
286 let field_false = matches!(self, Self::E5 | Self::G5);
288 match *first {
289 "0" => Ok(field_false),
290 "1" => Ok(!field_false),
291 _ => Err(ParseError::InvalidBoolean(first.to_string())),
292 }
293 }
294
295 fn get<'a>(&self, label: &'a Label) -> Option<&'a Self::Target> {
296 match self {
297 Self::E3 => as_ref_map!(label.accent_phrase_prev.is_interrogative),
298 Self::E5 => as_ref_and_then!(label.accent_phrase_prev.is_pause_insertion),
299 Self::F3 => as_ref_map!(label.accent_phrase_curr.is_interrogative),
300 Self::G3 => as_ref_map!(label.accent_phrase_next.is_interrogative),
301 Self::G5 => as_ref_and_then!(label.accent_phrase_next.is_pause_insertion),
302 }
303 }
304
305 fn test(&self, range: &Self::Range, target: &Self::Target) -> bool {
306 range == target
307 }
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq)]
312#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
313#[allow(missing_docs)]
314pub enum CategoryPosition {
315 B1,
316 B2,
317 B3,
318 C1,
319 C2,
320 C3,
321 D1,
322 D2,
323 D3,
324}
325
326impl Position for CategoryPosition {
327 type Target = u8;
328 type Range = Vec<u8>;
329
330 fn range(&self, ranges: &[&str]) -> Result<Self::Range, ParseError> {
331 ranges
332 .iter()
333 .map(|s| s.parse::<u8>().map_err(ParseError::FailLiteral))
334 .collect()
335 }
336
337 fn get<'a>(&self, label: &'a Label) -> Option<&'a Self::Target> {
338 match self {
339 Self::B1 => as_ref_and_then!(label.word_prev.pos),
340 Self::B2 => as_ref_and_then!(label.word_prev.ctype),
341 Self::B3 => as_ref_and_then!(label.word_prev.cform),
342 Self::C1 => as_ref_and_then!(label.word_curr.pos),
343 Self::C2 => as_ref_and_then!(label.word_curr.ctype),
344 Self::C3 => as_ref_and_then!(label.word_curr.cform),
345 Self::D1 => as_ref_and_then!(label.word_next.pos),
346 Self::D2 => as_ref_and_then!(label.word_next.ctype),
347 Self::D3 => as_ref_and_then!(label.word_next.cform),
348 }
349 }
350
351 fn test(&self, range: &Self::Range, target: &Self::Target) -> bool {
352 range.contains(target)
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
359#[allow(missing_docs)]
360pub enum UndefinedPotision {
361 E4,
362 F4,
363 G4,
364}
365
366impl Position for UndefinedPotision {
367 type Target = ();
368 type Range = ();
369
370 fn range(&self, _: &[&str]) -> Result<Self::Range, ParseError> {
371 Ok(())
372 }
373
374 fn get<'a>(&self, _: &'a Label) -> Option<&'a Self::Target> {
375 None
376 }
377
378 fn test(&self, _: &Self::Range, _: &Self::Target) -> bool {
379 true
380 }
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386
387 #[test]
388 fn parse_i8_range() {
389 assert_eq!(range_i8("12"), Ok(12..13));
390 assert_eq!(range_i8("1?"), Ok(10..20));
391 assert_eq!(range_i8("?"), Ok(0..10));
392
393 assert_eq!(range_i8("-12"), Ok(-12..-11));
394 assert_eq!(range_i8("-?"), Ok(-9..0));
395 assert_eq!(range_i8("-??"), Ok(-99..-9));
396
397 assert_eq!(range_i8("-1?"), Ok(-19..-9));
398 }
399
400 #[test]
401 fn parse_u8_range() {
402 assert_eq!(range_u8("12"), Ok(12..13));
403 assert_eq!(range_u8("1?"), Ok(10..20));
404 assert_eq!(range_u8("12?"), Ok(120..130));
405 assert_eq!(range_u8("?"), Ok(1..10));
406 }
407
408 #[test]
409 fn range_fail() {
410 use std::num::IntErrorKind;
411 assert!(matches!(
412 range_u8("?2"),
413 Err(ParseError::FailLiteral(e)) if *e.kind() == IntErrorKind::InvalidDigit
414 ));
415 assert!(matches!(
416 range_i8("?2"),
417 Err(ParseError::FailLiteral(e)) if *e.kind() == IntErrorKind::InvalidDigit
418 ));
419
420 assert!(matches!(
421 range_u8("???"),
422 Err(ParseError::FailWildcard(e)) if *e.kind() == IntErrorKind::InvalidDigit
423 ));
424 assert!(matches!(
425 range_i8("???"),
426 Err(ParseError::FailWildcard(e)) if *e.kind() == IntErrorKind::InvalidDigit
427 ));
428 }
429
430 #[test]
431 #[allow(clippy::single_range_in_vec_init)]
432 fn merge_ranges_1() {
433 assert_eq!(merge_ranges(vec![0..1]), Ok(0..1));
434 assert_eq!(merge_ranges(vec![0..1, 1..3]), Ok(0..3));
435 assert_eq!(merge_ranges(vec![1..3, 0..1]), Ok(0..3));
436 assert_eq!(merge_ranges(vec![0..2, 1..3]), Ok(0..3));
437 assert_eq!(merge_ranges(vec![-6..7, 1..3]), Ok(-6..7));
438 assert_eq!(
439 merge_ranges(vec![-6..7, 1..3, 2..6, -8..-7, -8..0]),
440 Ok(-8..7)
441 );
442
443 assert_eq!(merge_ranges::<u8>(vec![]), Err(ParseError::Empty));
444 assert_eq!(
445 merge_ranges(vec![0..1, 5..6]),
446 Err(ParseError::IncontinuousRange)
447 );
448 assert_eq!(
449 merge_ranges(vec![3..6, -1..2]),
450 Err(ParseError::IncontinuousRange)
451 );
452 assert_eq!(
453 merge_ranges(vec![-6..7, 1..3, 2..6, -8..-7]),
454 Err(ParseError::IncontinuousRange)
455 );
456 }
457}