1use std::num::NonZero;
2use std::str::{self, FromStr};
3
4use super::{Error, OptionExt as _, Span, Spanned, Unused, ast, unused};
5use crate::FormatDescriptionVersion;
6use crate::format_description::{SpannedValue as _, public};
7
8macro_rules! parse_modifiers {
9 ($modifiers:expr, struct {}) => {{
10 struct Parsed {}
11 drop($modifiers);
12 Ok(Parsed {})
13 }};
14 ($modifiers:expr, struct { $($field:ident : $modifier:ident),* $(,)? }) => {
15 'block: {
16 struct Parsed {
17 $($field: Option<Spanned<<$modifier as ModifierValue>::Type>>),*
18 }
19
20 let mut parsed = Parsed {
21 $($field: None),*
22 };
23
24 for modifier in $modifiers {
25 $(if modifier.key.eq_ignore_ascii_case(stringify!($field).as_bytes()) {
26 if parsed.$field.is_some() {
27 break 'block Err(modifier.key.span.error("duplicate modifier key"));
28 }
29 match <$modifier>::from_modifier_value(&modifier.value) {
30 Ok(value) => {
31 parsed.$field = Some(
32 <<$modifier as ModifierValue>::Type>::from(value)
33 .spanned(modifier.value.span)
34 )
35 },
36 Err(err) => break 'block Err(err),
37 }
38 continue;
39 })*
40 break 'block Err(modifier.key.span.error("invalid modifier key"));
41 }
42
43 Ok(parsed)
44 }
45 };
46}
47
48pub(super) fn parse<'a>(
49 ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
50) -> impl Iterator<Item = Result<Item<'a>, Error>> {
51 ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
52}
53
54pub(super) enum Item<'a> {
55 Literal(&'a [u8]),
56 StringLiteral(&'a str),
57 Component(Component),
58 Optional {
59 value: Box<[Self]>,
60 format: Spanned<bool>,
61 _span: Unused<Span>,
62 },
63 First {
64 value: Box<[Box<[Self]>]>,
65 _span: Unused<Span>,
66 },
67}
68
69impl Item<'_> {
70 pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result<Item<'_>, Error> {
71 Ok(match ast_item {
72 ast::Item::Component {
73 _opening_bracket: _,
74 _leading_whitespace: _,
75 name,
76 modifiers,
77 _trailing_whitespace: _,
78 _closing_bracket: _,
79 } => Item::Component(component_from_ast(&name, &modifiers)?),
80 ast::Item::Literal {
81 version,
82 value: Spanned { value, span },
83 } => {
84 if let Ok(value) = str::from_utf8(value) {
85 Item::StringLiteral(value)
86 } else if version.is_at_least_v3() {
87 return Err(span.error("v3 format descriptions must be valid UTF-8"));
88 } else {
89 Item::Literal(value)
90 }
91 }
92 ast::Item::EscapedBracket {
93 _first: _,
94 _second: _,
95 } => Item::StringLiteral("["),
96 ast::Item::Optional {
97 version,
98 opening_bracket,
99 _leading_whitespace: _,
100 _optional_kw: _,
101 modifiers,
102 _whitespace_after_modifiers: _,
103 nested_format_description,
104 closing_bracket,
105 } => {
106 let items = nested_format_description
107 .items
108 .into_vec()
109 .into_iter()
110 .map(Item::from_ast)
111 .collect::<Result<_, _>>()?;
112
113 let modifiers = parse_modifiers!(modifiers, struct { format: OptionalFormat })?;
114 let format = modifiers.format.transpose().map(|val| val.unwrap_or(true));
115 if version.is_at_most_v2() && !*format {
116 return Err(format
117 .span
118 .error("v1 and v2 format descriptions must format optional items"));
119 }
120
121 Item::Optional {
122 value: items,
123 format,
124 _span: unused(opening_bracket.to(closing_bracket)),
125 }
126 }
127 ast::Item::First {
128 opening_bracket,
129 _leading_whitespace: _,
130 _first_kw: _,
131 modifiers,
132 _whitespace_after_modifiers: _,
133 nested_format_descriptions,
134 closing_bracket,
135 } => {
136 let items = nested_format_descriptions
137 .into_vec()
138 .into_iter()
139 .map(|nested_format_description| {
140 nested_format_description
141 .items
142 .into_vec()
143 .into_iter()
144 .map(Item::from_ast)
145 .collect()
146 })
147 .collect::<Result<_, _>>()?;
148
149 let _modifiers = parse_modifiers!(modifiers, struct {})?;
150
151 Item::First {
152 value: items,
153 _span: unused(opening_bracket.to(closing_bracket)),
154 }
155 }
156 })
157 }
158}
159
160impl TryFrom<(FormatDescriptionVersion, Item<'_>)> for public::OwnedFormatItemInner {
161 type Error = Error;
162
163 fn try_from(
164 (version, item): (FormatDescriptionVersion, Item<'_>),
165 ) -> Result<Self, Self::Error> {
166 Ok(match item {
167 Item::Literal(literal) => Self::Literal(literal.to_vec()),
168 Item::StringLiteral(string) => Self::StringLiteral(string.to_owned()),
169 Item::Component(component) => Self::Component((version, component).try_into()?),
170 Item::Optional {
171 format,
172 value,
173 _span: _,
174 } => Self::Optional {
175 format: *format,
176 item: Box::new((version, value).try_into()?),
177 },
178 Item::First { value, _span: _ } => Self::First(
179 value
180 .into_iter()
181 .map(|item| (version, item).try_into())
182 .collect::<Result<_, _>>()?,
183 ),
184 })
185 }
186}
187
188impl<'a> TryFrom<(FormatDescriptionVersion, Box<[Item<'a>]>)> for public::OwnedFormatItemInner {
189 type Error = Error;
190
191 fn try_from(
192 (version, items): (FormatDescriptionVersion, Box<[Item<'a>]>),
193 ) -> Result<Self, Self::Error> {
194 Ok(Self::Compound(
195 items
196 .into_iter()
197 .map(|item| (version, item).try_into())
198 .collect::<Result<_, _>>()?,
199 ))
200 }
201}
202
203macro_rules! component_definition {
204 (@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
205 (@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
206 (@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
207 (@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
208
209 ($vis:vis enum $name:ident {
210 $($variant:ident = $parse_variant:literal {$(
211 $(#[$required:tt])?
212 $field:ident = $parse_field:literal:
213 Option<$(#[$from_str:tt])? $field_type:ty>
214 ),* $(,)?}),* $(,)?
215 }) => {
216 $vis enum $name {
217 $($variant($variant),)*
218 }
219
220 $($vis struct $variant {
221 $($field: Spanned<Option<$field_type>>),*
222 })*
223
224 $(impl $variant {
225 fn with_modifiers(
226 modifiers: &[ast::Modifier<'_>],
227 _component_span: Span,
228 ) -> Result<Self, Error>
229 {
230 #[allow(unused_mut)]
231 let mut this = Self {
232 $($field: None.spanned(Span::dummy())),*
233 };
234
235 for modifier in modifiers {
236 $(#[allow(clippy::string_lit_as_bytes)]
237 if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) {
238 this.$field = Some(
239 component_definition!(@if_from_str $($from_str)?
240 then {
241 parse_from_modifier_value::<$field_type>(&modifier.value)?
242 } else {
243 <$field_type>::from_modifier_value(&modifier.value)?
244 }
245 )
246 ).spanned(modifier.key_value_span());
247 continue;
248 })*
249 return Err(modifier.key.span.error("invalid modifier key"));
250 }
251
252 $(component_definition! { @if_required $($required)? then {
253 if this.$field.is_none() {
254 return Err(_component_span.error("missing required modifier"));
255 }
256 }})*
257
258 Ok(this)
259 }
260 })*
261
262 fn component_from_ast(
263 name: &Spanned<&[u8]>,
264 modifiers: &[ast::Modifier<'_>],
265 ) -> Result<Component, Error> {
266 $(#[allow(clippy::string_lit_as_bytes)]
267 if name.eq_ignore_ascii_case($parse_variant.as_bytes()) {
268 return Ok(Component::$variant($variant::with_modifiers(&modifiers, name.span)?));
269 })*
270 Err(name.span.error("invalid component"))
271 }
272 }
273}
274
275component_definition! {
276 pub(super) enum Component {
277 Day = "day" {
278 padding = "padding": Option<Padding>,
279 },
280 End = "end" {
281 trailing_input = "trailing_input": Option<TrailingInput>,
282 },
283 Hour = "hour" {
284 padding = "padding": Option<Padding>,
285 base = "repr": Option<HourBase>,
286 },
287 Ignore = "ignore" {
288 #[required]
289 count = "count": Option<#[from_str] NonZero<u16>>,
290 },
291 Minute = "minute" {
292 padding = "padding": Option<Padding>,
293 },
294 Month = "month" {
295 padding = "padding": Option<Padding>,
296 repr = "repr": Option<MonthRepr>,
297 case_sensitive = "case_sensitive": Option<MonthCaseSensitive>,
298 },
299 OffsetHour = "offset_hour" {
300 sign_behavior = "sign": Option<SignBehavior>,
301 padding = "padding": Option<Padding>,
302 },
303 OffsetMinute = "offset_minute" {
304 padding = "padding": Option<Padding>,
305 },
306 OffsetSecond = "offset_second" {
307 padding = "padding": Option<Padding>,
308 },
309 Ordinal = "ordinal" {
310 padding = "padding": Option<Padding>,
311 },
312 Period = "period" {
313 case = "case": Option<PeriodCase>,
314 case_sensitive = "case_sensitive": Option<PeriodCaseSensitive>,
315 },
316 Second = "second" {
317 padding = "padding": Option<Padding>,
318 },
319 Subsecond = "subsecond" {
320 digits = "digits": Option<SubsecondDigits>,
321 },
322 UnixTimestamp = "unix_timestamp" {
323 precision = "precision": Option<UnixTimestampPrecision>,
324 sign_behavior = "sign": Option<SignBehavior>,
325 },
326 Weekday = "weekday" {
327 repr = "repr": Option<WeekdayRepr>,
328 one_indexed = "one_indexed": Option<WeekdayOneIndexed>,
329 case_sensitive = "case_sensitive": Option<WeekdayCaseSensitive>,
330 },
331 WeekNumber = "week_number" {
332 padding = "padding": Option<Padding>,
333 repr = "repr": Option<WeekNumberRepr>,
334 },
335 Year = "year" {
336 padding = "padding": Option<Padding>,
337 repr = "repr": Option<YearRepr>,
338 range = "range": Option<YearRange>,
339 base = "base": Option<YearBase>,
340 sign_behavior = "sign": Option<SignBehavior>,
341 },
342 }
343}
344
345impl TryFrom<(FormatDescriptionVersion, Component)> for public::Component {
346 type Error = Error;
347
348 #[inline]
349 fn try_from(
350 (version, component): (FormatDescriptionVersion, Component),
351 ) -> Result<Self, Self::Error> {
352 macro_rules! reject_modifier {
353 ($modifier:ident, $modifier_str:literal, $context:literal) => {
354 if version.is_at_least_v3() && $modifier.value.is_some() {
355 return Err($modifier.span.error(concat!(
356 "the '",
357 $modifier_str,
358 "' modifier is not valid ",
359 $context
360 )));
361 }
362 };
363 }
364
365 use crate::format_description::public::modifier;
366 Ok(match component {
367 Component::Day(Day { padding }) => Self::Day(modifier::Day {
368 padding: padding.unwrap_or_default().into(),
369 }),
370 Component::End(End { trailing_input }) => Self::End(modifier::End {
371 trailing_input: trailing_input.unwrap_or_default().into(),
372 }),
373 Component::Hour(Hour { padding, base }) => match base.unwrap_or_default() {
374 HourBase::Twelve => Self::Hour12(modifier::Hour12 {
375 padding: padding.unwrap_or_default().into(),
376 }),
377 HourBase::TwentyFour => Self::Hour24(modifier::Hour24 {
378 padding: padding.unwrap_or_default().into(),
379 }),
380 },
381 Component::Ignore(Ignore { count }) => Self::Ignore(modifier::Ignore {
382 count: match *count {
383 Some(value) => value,
384 None => bug!("required modifier was not set"),
385 },
386 }),
387 Component::Minute(Minute { padding }) => Self::Minute(modifier::Minute {
388 padding: padding.unwrap_or_default().into(),
389 }),
390 Component::Month(Month {
391 padding,
392 repr,
393 case_sensitive,
394 }) => match repr.unwrap_or_default() {
395 MonthRepr::Numerical => {
396 reject_modifier!(case_sensitive, "case_sensitive", "for numerical month");
397 Self::MonthNumerical(modifier::MonthNumerical {
398 padding: padding.unwrap_or_default().into(),
399 })
400 }
401 MonthRepr::Long => {
402 reject_modifier!(padding, "padding", "for long month");
403 Self::MonthLong(modifier::MonthLong {
404 case_sensitive: case_sensitive.unwrap_or_default().into(),
405 })
406 }
407 MonthRepr::Short => {
408 reject_modifier!(padding, "padding", "for short month");
409 Self::MonthShort(modifier::MonthShort {
410 case_sensitive: case_sensitive.unwrap_or_default().into(),
411 })
412 }
413 },
414 Component::OffsetHour(OffsetHour {
415 sign_behavior,
416 padding,
417 }) => Self::OffsetHour(modifier::OffsetHour {
418 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
419 padding: padding.unwrap_or_default().into(),
420 }),
421 Component::OffsetMinute(OffsetMinute { padding }) => {
422 Self::OffsetMinute(modifier::OffsetMinute {
423 padding: padding.unwrap_or_default().into(),
424 })
425 }
426 Component::OffsetSecond(OffsetSecond { padding }) => {
427 Self::OffsetSecond(modifier::OffsetSecond {
428 padding: padding.unwrap_or_default().into(),
429 })
430 }
431 Component::Ordinal(Ordinal { padding }) => Self::Ordinal(modifier::Ordinal {
432 padding: padding.unwrap_or_default().into(),
433 }),
434 Component::Period(Period {
435 case,
436 case_sensitive,
437 }) => Self::Period(modifier::Period {
438 is_uppercase: case.unwrap_or_default().into(),
439 case_sensitive: case_sensitive.unwrap_or_default().into(),
440 }),
441 Component::Second(Second { padding }) => Self::Second(modifier::Second {
442 padding: padding.unwrap_or_default().into(),
443 }),
444 Component::Subsecond(Subsecond { digits }) => Self::Subsecond(modifier::Subsecond {
445 digits: digits.unwrap_or_default().into(),
446 }),
447 Component::UnixTimestamp(UnixTimestamp {
448 precision,
449 sign_behavior,
450 }) => match precision.unwrap_or_default() {
451 UnixTimestampPrecision::Second => {
452 Self::UnixTimestampSecond(modifier::UnixTimestampSecond {
453 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
454 })
455 }
456 UnixTimestampPrecision::Millisecond => {
457 Self::UnixTimestampMillisecond(modifier::UnixTimestampMillisecond {
458 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
459 })
460 }
461 UnixTimestampPrecision::Microsecond => {
462 Self::UnixTimestampMicrosecond(modifier::UnixTimestampMicrosecond {
463 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
464 })
465 }
466 UnixTimestampPrecision::Nanosecond => {
467 Self::UnixTimestampNanosecond(modifier::UnixTimestampNanosecond {
468 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
469 })
470 }
471 },
472 Component::Weekday(Weekday {
473 repr,
474 one_indexed,
475 case_sensitive,
476 }) => match repr.unwrap_or_default() {
477 WeekdayRepr::Short => {
478 reject_modifier!(one_indexed, "one_indexed", "for short weekday");
479 Self::WeekdayShort(modifier::WeekdayShort {
480 case_sensitive: case_sensitive.unwrap_or_default().into(),
481 })
482 }
483 WeekdayRepr::Long => {
484 reject_modifier!(one_indexed, "one_indexed", "for long weekday");
485 Self::WeekdayLong(modifier::WeekdayLong {
486 case_sensitive: case_sensitive.unwrap_or_default().into(),
487 })
488 }
489 WeekdayRepr::Sunday => {
490 reject_modifier!(case_sensitive, "case_sensitive", "for numerical weekday");
491 Self::WeekdaySunday(modifier::WeekdaySunday {
492 one_indexed: one_indexed.unwrap_or_default().into(),
493 })
494 }
495 WeekdayRepr::Monday => {
496 reject_modifier!(case_sensitive, "case_sensitive", "for numerical weekday");
497 Self::WeekdayMonday(modifier::WeekdayMonday {
498 one_indexed: one_indexed.unwrap_or_default().into(),
499 })
500 }
501 },
502 Component::WeekNumber(WeekNumber { padding, repr }) => match repr.unwrap_or_default() {
503 WeekNumberRepr::Iso => Self::WeekNumberIso(modifier::WeekNumberIso {
504 padding: padding.unwrap_or_default().into(),
505 }),
506 WeekNumberRepr::Sunday => Self::WeekNumberSunday(modifier::WeekNumberSunday {
507 padding: padding.unwrap_or_default().into(),
508 }),
509 WeekNumberRepr::Monday => Self::WeekNumberMonday(modifier::WeekNumberMonday {
510 padding: padding.unwrap_or_default().into(),
511 }),
512 },
513 Component::Year(Year {
514 padding,
515 repr,
516 range,
517 base,
518 sign_behavior,
519 }) => {
520 #[cfg(not(feature = "large-dates"))]
521 reject_modifier!(
522 range,
523 "range",
524 "when the `large-dates` feature is not enabled"
525 );
526
527 match (
528 base.unwrap_or_default(),
529 repr.unwrap_or_default(),
530 range.unwrap_or_default(),
531 ) {
532 #[cfg(feature = "large-dates")]
533 (YearBase::Calendar, YearRepr::Full, YearRange::Extended) => {
534 Self::CalendarYearFullExtendedRange(
535 modifier::CalendarYearFullExtendedRange {
536 padding: padding.unwrap_or_default().into(),
537 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
538 },
539 )
540 }
541 (YearBase::Calendar, YearRepr::Full, _) => Self::CalendarYearFullStandardRange(
542 modifier::CalendarYearFullStandardRange {
543 padding: padding.unwrap_or_default().into(),
544 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
545 },
546 ),
547 #[cfg(feature = "large-dates")]
548 (YearBase::Calendar, YearRepr::Century, YearRange::Extended) => {
549 Self::CalendarYearCenturyExtendedRange(
550 modifier::CalendarYearCenturyExtendedRange {
551 padding: padding.unwrap_or_default().into(),
552 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
553 },
554 )
555 }
556 (YearBase::Calendar, YearRepr::Century, _) => {
557 Self::CalendarYearCenturyStandardRange(
558 modifier::CalendarYearCenturyStandardRange {
559 padding: padding.unwrap_or_default().into(),
560 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
561 },
562 )
563 }
564 #[cfg(feature = "large-dates")]
565 (YearBase::IsoWeek, YearRepr::Full, YearRange::Extended) => {
566 Self::IsoYearFullExtendedRange(modifier::IsoYearFullExtendedRange {
567 padding: padding.unwrap_or_default().into(),
568 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
569 })
570 }
571 (YearBase::IsoWeek, YearRepr::Full, _) => {
572 Self::IsoYearFullStandardRange(modifier::IsoYearFullStandardRange {
573 padding: padding.unwrap_or_default().into(),
574 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
575 })
576 }
577 #[cfg(feature = "large-dates")]
578 (YearBase::IsoWeek, YearRepr::Century, YearRange::Extended) => {
579 Self::IsoYearCenturyExtendedRange(modifier::IsoYearCenturyExtendedRange {
580 padding: padding.unwrap_or_default().into(),
581 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
582 })
583 }
584 (YearBase::IsoWeek, YearRepr::Century, _) => {
585 Self::IsoYearCenturyStandardRange(modifier::IsoYearCenturyStandardRange {
586 padding: padding.unwrap_or_default().into(),
587 sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
588 })
589 }
590 (YearBase::Calendar, YearRepr::LastTwo, _) => {
591 #[cfg(feature = "large-dates")]
592 reject_modifier!(range, "range", "when `repr:last_two` is used");
593 Self::CalendarYearLastTwo(modifier::CalendarYearLastTwo {
594 padding: padding.unwrap_or_default().into(),
595 })
596 }
597 (YearBase::IsoWeek, YearRepr::LastTwo, _) => {
598 #[cfg(feature = "large-dates")]
599 reject_modifier!(range, "range", "when `repr:last_two` is used");
600 Self::IsoYearLastTwo(modifier::IsoYearLastTwo {
601 padding: padding.unwrap_or_default().into(),
602 })
603 }
604 }
605 }
606 })
607 }
608}
609
610macro_rules! target_ty {
611 ($name:ident $type:ty) => {
612 $type
613 };
614 ($name:ident) => {
615 super::public::modifier::$name
616 };
617}
618
619macro_rules! target_value {
621 ($name:ident $variant:ident $value:expr) => {
622 $value
623 };
624 ($name:ident $variant:ident) => {
625 super::public::modifier::$name::$variant
626 };
627}
628
629macro_rules! if_not_parse_only {
630 (@parse_only $($x:tt)*) => {};
631 ($($x:tt)*) => { $($x)* };
632}
633
634trait ModifierValue {
635 type Type;
636}
637
638macro_rules! modifier {
639 ($(
640 $(@$instruction:ident)? enum $name:ident $(($target_ty:ty))? {
641 $(
642 $(#[$attr:meta])?
643 $variant:ident $(($target_value:expr))? = $parse_variant:literal
644 ),* $(,)?
645 }
646 )+) => {$(
647 #[derive(Default, Clone, Copy)]
648 enum $name {
649 $($(#[$attr])? $variant),*
650 }
651
652 impl $name {
653 fn from_modifier_value(value: &Spanned<&[u8]>) -> Result<Self, Error> {
655 $(if value.eq_ignore_ascii_case($parse_variant) {
656 return Ok(Self::$variant);
657 })*
658 Err(value.span.error("invalid modifier value"))
659 }
660 }
661
662 if_not_parse_only! { $(@$instruction)?
663 impl ModifierValue for $name {
664 type Type = target_ty!($name $($target_ty)?);
665 }
666
667 impl From<$name> for <$name as ModifierValue>::Type {
668 #[inline]
669 fn from(modifier: $name) -> Self {
670 match modifier {
671 $($name::$variant => target_value!($name $variant $($target_value)?)),*
672 }
673 }
674 }
675 }
676 )+};
677}
678
679modifier! {
680 enum HourBase(bool) {
681 Twelve(true) = b"12",
682 #[default]
683 TwentyFour(false) = b"24",
684 }
685
686 enum MonthCaseSensitive(bool) {
687 False(false) = b"false",
688 #[default]
689 True(true) = b"true",
690 }
691
692 @parse_only enum MonthRepr {
693 #[default]
694 Numerical = b"numerical",
695 Long = b"long",
696 Short = b"short",
697 }
698
699 enum OptionalFormat(bool) {
700 False(false) = b"false",
701 #[default]
702 True(true) = b"true",
703 }
704
705 enum Padding {
706 Space = b"space",
707 #[default]
708 Zero = b"zero",
709 None = b"none",
710 }
711
712 enum PeriodCase(bool) {
713 Lower(false) = b"lower",
714 #[default]
715 Upper(true) = b"upper",
716 }
717
718 enum PeriodCaseSensitive(bool) {
719 False(false) = b"false",
720 #[default]
721 True(true) = b"true",
722 }
723
724 enum SignBehavior(bool) {
725 #[default]
726 Automatic(false) = b"automatic",
727 Mandatory(true) = b"mandatory",
728 }
729
730 enum SubsecondDigits {
731 One = b"1",
732 Two = b"2",
733 Three = b"3",
734 Four = b"4",
735 Five = b"5",
736 Six = b"6",
737 Seven = b"7",
738 Eight = b"8",
739 Nine = b"9",
740 #[default]
741 OneOrMore = b"1+",
742 }
743
744 enum TrailingInput {
745 #[default]
746 Prohibit = b"prohibit",
747 Discard = b"discard",
748 }
749
750 @parse_only enum UnixTimestampPrecision {
751 #[default]
752 Second = b"second",
753 Millisecond = b"millisecond",
754 Microsecond = b"microsecond",
755 Nanosecond = b"nanosecond",
756 }
757
758 @parse_only enum WeekNumberRepr {
759 #[default]
760 Iso = b"iso",
761 Sunday = b"sunday",
762 Monday = b"monday",
763 }
764
765 enum WeekdayCaseSensitive(bool) {
766 False(false) = b"false",
767 #[default]
768 True(true) = b"true",
769 }
770
771 enum WeekdayOneIndexed(bool) {
772 False(false) = b"false",
773 #[default]
774 True(true) = b"true",
775 }
776
777 @parse_only enum WeekdayRepr {
778 Short = b"short",
779 #[default]
780 Long = b"long",
781 Sunday = b"sunday",
782 Monday = b"monday",
783 }
784
785 enum YearBase(bool) {
786 #[default]
787 Calendar(false) = b"calendar",
788 IsoWeek(true) = b"iso_week",
789 }
790
791 @parse_only enum YearRepr {
792 #[default]
793 Full = b"full",
794 Century = b"century",
795 LastTwo = b"last_two",
796 }
797
798 @parse_only enum YearRange {
799 Standard = b"standard",
800 #[default]
801 Extended = b"extended",
802 }
803}
804
805fn parse_from_modifier_value<T: FromStr>(value: &Spanned<&[u8]>) -> Result<T, Error> {
806 str::from_utf8(value)
807 .ok()
808 .and_then(|val| val.parse::<T>().ok())
809 .ok_or_else(|| value.span.error("invalid modifier value"))
810}