1use std::num::NonZero;
2use std::str::{self, FromStr};
3
4use super::{ast, unused, Error, Span, Spanned, Unused};
5
6pub(super) fn parse<'a>(
7 ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
8) -> impl Iterator<Item = Result<Item<'a>, Error>> {
9 ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
10}
11
12pub(super) enum Item<'a> {
13 Literal(&'a [u8]),
14 Component(Component),
15 Optional {
16 value: Box<[Self]>,
17 _span: Unused<Span>,
18 },
19 First {
20 value: Box<[Box<[Self]>]>,
21 _span: Unused<Span>,
22 },
23}
24
25impl Item<'_> {
26 pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result<Item<'_>, Error> {
27 Ok(match ast_item {
28 ast::Item::Component {
29 _opening_bracket: _,
30 _leading_whitespace: _,
31 name,
32 modifiers,
33 _trailing_whitespace: _,
34 _closing_bracket: _,
35 } => Item::Component(component_from_ast(&name, &modifiers)?),
36 ast::Item::Literal(Spanned { value, span: _ }) => Item::Literal(value),
37 ast::Item::EscapedBracket {
38 _first: _,
39 _second: _,
40 } => Item::Literal(b"["),
41 ast::Item::Optional {
42 opening_bracket,
43 _leading_whitespace: _,
44 _optional_kw: _,
45 _whitespace: _,
46 nested_format_description,
47 closing_bracket,
48 } => {
49 let items = nested_format_description
50 .items
51 .into_vec()
52 .into_iter()
53 .map(Item::from_ast)
54 .collect::<Result<_, _>>()?;
55 Item::Optional {
56 value: items,
57 _span: unused(opening_bracket.to(closing_bracket)),
58 }
59 }
60 ast::Item::First {
61 opening_bracket,
62 _leading_whitespace: _,
63 _first_kw: _,
64 _whitespace: _,
65 nested_format_descriptions,
66 closing_bracket,
67 } => {
68 let items = nested_format_descriptions
69 .into_vec()
70 .into_iter()
71 .map(|nested_format_description| {
72 nested_format_description
73 .items
74 .into_vec()
75 .into_iter()
76 .map(Item::from_ast)
77 .collect()
78 })
79 .collect::<Result<_, _>>()?;
80 Item::First {
81 value: items,
82 _span: unused(opening_bracket.to(closing_bracket)),
83 }
84 }
85 })
86 }
87}
88
89impl From<Item<'_>> for crate::format_description::public::OwnedFormatItem {
90 fn from(item: Item<'_>) -> Self {
91 match item {
92 Item::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
93 Item::Component(component) => Self::Component(component.into()),
94 Item::Optional { value, _span: _ } => Self::Optional(Box::new(value.into())),
95 Item::First { value, _span: _ } => {
96 Self::First(value.into_vec().into_iter().map(Into::into).collect())
97 }
98 }
99 }
100}
101
102impl<'a> From<Box<[Item<'a>]>> for crate::format_description::public::OwnedFormatItem {
103 fn from(items: Box<[Item<'a>]>) -> Self {
104 let items = items.into_vec();
105 match <[_; 1]>::try_from(items) {
106 Ok([item]) => item.into(),
107 Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
108 }
109 }
110}
111
112macro_rules! component_definition {
113 (@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
114 (@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
115 (@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
116 (@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
117
118 ($vis:vis enum $name:ident {
119 $($variant:ident = $parse_variant:literal {$(
120 $(#[$required:tt])?
121 $field:ident = $parse_field:literal:
122 Option<$(#[$from_str:tt])? $field_type:ty>
123 => $target_field:ident
124 ),* $(,)?}),* $(,)?
125 }) => {
126 $vis enum $name {
127 $($variant($variant),)*
128 }
129
130 $($vis struct $variant {
131 $($field: Option<$field_type>),*
132 })*
133
134 $(impl $variant {
135 fn with_modifiers(
136 modifiers: &[ast::Modifier<'_>],
137 _component_span: Span,
138 ) -> Result<Self, Error>
139 {
140 #[allow(unused_mut)]
141 let mut this = Self {
142 $($field: None),*
143 };
144
145 for modifier in modifiers {
146 $(#[allow(clippy::string_lit_as_bytes)]
147 if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) {
148 this.$field = component_definition!(@if_from_str $($from_str)?
149 then {
150 parse_from_modifier_value::<$field_type>(&modifier.value)?
151 } else {
152 <$field_type>::from_modifier_value(&modifier.value)?
153 });
154 continue;
155 })*
156 return Err(modifier.key.span.error("invalid modifier key"));
157 }
158
159 $(component_definition! { @if_required $($required)? then {
160 if this.$field.is_none() {
161 return Err(_component_span.error("missing required modifier"));
162 }
163 }})*
164
165 Ok(this)
166 }
167 })*
168
169 impl From<$name> for crate::format_description::public::Component {
170 fn from(component: $name) -> Self {
171 match component {$(
172 $name::$variant($variant { $($field),* }) => {
173 $crate::format_description::public::Component::$variant(
174 super::public::modifier::$variant {$(
175 $target_field: component_definition! { @if_required $($required)?
176 then {
177 match $field {
178 Some(value) => value.into(),
179 None => bug!("required modifier was not set"),
180 }
181 } else {
182 $field.unwrap_or_default().into()
183 }
184 }
185 ),*}
186 )
187 }
188 )*}
189 }
190 }
191
192 fn component_from_ast(
193 name: &Spanned<&[u8]>,
194 modifiers: &[ast::Modifier<'_>],
195 ) -> Result<Component, Error> {
196 $(#[allow(clippy::string_lit_as_bytes)]
197 if name.eq_ignore_ascii_case($parse_variant.as_bytes()) {
198 return Ok(Component::$variant($variant::with_modifiers(&modifiers, name.span)?));
199 })*
200 Err(name.span.error("invalid component"))
201 }
202 }
203}
204
205pub(super) enum Component {
Day(Day),
End(End),
Hour(Hour),
Ignore(Ignore),
Minute(Minute),
Month(Month),
OffsetHour(OffsetHour),
OffsetMinute(OffsetMinute),
OffsetSecond(OffsetSecond),
Ordinal(Ordinal),
Period(Period),
Second(Second),
Subsecond(Subsecond),
UnixTimestamp(UnixTimestamp),
Weekday(Weekday),
WeekNumber(WeekNumber),
Year(Year),
}
pub(super) struct Day {
padding: Option<Padding>,
}
pub(super) struct End {}
pub(super) struct Hour {
padding: Option<Padding>,
base: Option<HourBase>,
}
pub(super) struct Ignore {
count: Option<NonZero<u16>>,
}
pub(super) struct Minute {
padding: Option<Padding>,
}
pub(super) struct Month {
padding: Option<Padding>,
repr: Option<MonthRepr>,
case_sensitive: Option<MonthCaseSensitive>,
}
pub(super) struct OffsetHour {
sign_behavior: Option<SignBehavior>,
padding: Option<Padding>,
}
pub(super) struct OffsetMinute {
padding: Option<Padding>,
}
pub(super) struct OffsetSecond {
padding: Option<Padding>,
}
pub(super) struct Ordinal {
padding: Option<Padding>,
}
pub(super) struct Period {
case: Option<PeriodCase>,
case_sensitive: Option<PeriodCaseSensitive>,
}
pub(super) struct Second {
padding: Option<Padding>,
}
pub(super) struct Subsecond {
digits: Option<SubsecondDigits>,
}
pub(super) struct UnixTimestamp {
precision: Option<UnixTimestampPrecision>,
sign_behavior: Option<SignBehavior>,
}
pub(super) struct Weekday {
repr: Option<WeekdayRepr>,
one_indexed: Option<WeekdayOneIndexed>,
case_sensitive: Option<WeekdayCaseSensitive>,
}
pub(super) struct WeekNumber {
padding: Option<Padding>,
repr: Option<WeekNumberRepr>,
}
pub(super) struct Year {
padding: Option<Padding>,
repr: Option<YearRepr>,
range: Option<YearRange>,
base: Option<YearBase>,
sign_behavior: Option<SignBehavior>,
}
impl Day {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl End {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self {};
for modifier in modifiers {
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Hour {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None, base: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("repr".as_bytes()) {
this.base = <HourBase>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Ignore {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { count: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("count".as_bytes()) {
this.count =
parse_from_modifier_value::<NonZero<u16>>(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
if this.count.is_none() {
return Err(_component_span.error("missing required modifier"));
}
Ok(this)
}
}
impl Minute {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Month {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this =
Self { padding: None, repr: None, case_sensitive: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("repr".as_bytes()) {
this.repr =
<MonthRepr>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("case_sensitive".as_bytes())
{
this.case_sensitive =
<MonthCaseSensitive>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl OffsetHour {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { sign_behavior: None, padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("sign".as_bytes()) {
this.sign_behavior =
<SignBehavior>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl OffsetMinute {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl OffsetSecond {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Ordinal {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Period {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { case: None, case_sensitive: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("case".as_bytes()) {
this.case =
<PeriodCase>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("case_sensitive".as_bytes())
{
this.case_sensitive =
<PeriodCaseSensitive>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Second {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Subsecond {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { digits: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("digits".as_bytes()) {
this.digits =
<SubsecondDigits>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl UnixTimestamp {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { precision: None, sign_behavior: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("precision".as_bytes()) {
this.precision =
<UnixTimestampPrecision>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("sign".as_bytes()) {
this.sign_behavior =
<SignBehavior>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Weekday {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this =
Self { repr: None, one_indexed: None, case_sensitive: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("repr".as_bytes()) {
this.repr =
<WeekdayRepr>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("one_indexed".as_bytes()) {
this.one_indexed =
<WeekdayOneIndexed>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("case_sensitive".as_bytes())
{
this.case_sensitive =
<WeekdayCaseSensitive>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl WeekNumber {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this = Self { padding: None, repr: None };
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("repr".as_bytes()) {
this.repr =
<WeekNumberRepr>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl Year {
fn with_modifiers(modifiers: &[ast::Modifier<'_>], _component_span: Span)
-> Result<Self, Error> {
#[allow(unused_mut)]
let mut this =
Self {
padding: None,
repr: None,
range: None,
base: None,
sign_behavior: None,
};
for modifier in modifiers {
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("padding".as_bytes()) {
this.padding =
<Padding>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("repr".as_bytes()) {
this.repr = <YearRepr>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("range".as_bytes()) {
this.range =
<YearRange>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("base".as_bytes()) {
this.base = <YearBase>::from_modifier_value(&modifier.value)?;
continue;
}
#[allow(clippy :: string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case("sign".as_bytes()) {
this.sign_behavior =
<SignBehavior>::from_modifier_value(&modifier.value)?;
continue;
}
return Err(modifier.key.span.error("invalid modifier key"));
}
Ok(this)
}
}
impl From<Component> for crate::format_description::public::Component {
fn from(component: Component) -> Self {
match component {
Component::Day(Day { padding }) => {
crate::format_description::public::Component::Day(super::public::modifier::Day {
padding: padding.unwrap_or_default().into(),
})
}
Component::End(End {}) => {
crate::format_description::public::Component::End(super::public::modifier::End {})
}
Component::Hour(Hour { padding, base }) => {
crate::format_description::public::Component::Hour(super::public::modifier::Hour {
padding: padding.unwrap_or_default().into(),
is_12_hour_clock: base.unwrap_or_default().into(),
})
}
Component::Ignore(Ignore { count }) => {
crate::format_description::public::Component::Ignore(super::public::modifier::Ignore {
count: match count {
Some(value) => value.into(),
None => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("internal error: required modifier was not set")));
}
},
})
}
Component::Minute(Minute { padding }) => {
crate::format_description::public::Component::Minute(super::public::modifier::Minute {
padding: padding.unwrap_or_default().into(),
})
}
Component::Month(Month { padding, repr, case_sensitive }) => {
crate::format_description::public::Component::Month(super::public::modifier::Month {
padding: padding.unwrap_or_default().into(),
repr: repr.unwrap_or_default().into(),
case_sensitive: case_sensitive.unwrap_or_default().into(),
})
}
Component::OffsetHour(OffsetHour { sign_behavior, padding }) => {
crate::format_description::public::Component::OffsetHour(super::public::modifier::OffsetHour {
sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
padding: padding.unwrap_or_default().into(),
})
}
Component::OffsetMinute(OffsetMinute { padding }) => {
crate::format_description::public::Component::OffsetMinute(super::public::modifier::OffsetMinute {
padding: padding.unwrap_or_default().into(),
})
}
Component::OffsetSecond(OffsetSecond { padding }) => {
crate::format_description::public::Component::OffsetSecond(super::public::modifier::OffsetSecond {
padding: padding.unwrap_or_default().into(),
})
}
Component::Ordinal(Ordinal { padding }) => {
crate::format_description::public::Component::Ordinal(super::public::modifier::Ordinal {
padding: padding.unwrap_or_default().into(),
})
}
Component::Period(Period { case, case_sensitive }) => {
crate::format_description::public::Component::Period(super::public::modifier::Period {
is_uppercase: case.unwrap_or_default().into(),
case_sensitive: case_sensitive.unwrap_or_default().into(),
})
}
Component::Second(Second { padding }) => {
crate::format_description::public::Component::Second(super::public::modifier::Second {
padding: padding.unwrap_or_default().into(),
})
}
Component::Subsecond(Subsecond { digits }) => {
crate::format_description::public::Component::Subsecond(super::public::modifier::Subsecond {
digits: digits.unwrap_or_default().into(),
})
}
Component::UnixTimestamp(UnixTimestamp { precision, sign_behavior
}) => {
crate::format_description::public::Component::UnixTimestamp(super::public::modifier::UnixTimestamp {
precision: precision.unwrap_or_default().into(),
sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
})
}
Component::Weekday(Weekday { repr, one_indexed, case_sensitive })
=> {
crate::format_description::public::Component::Weekday(super::public::modifier::Weekday {
repr: repr.unwrap_or_default().into(),
one_indexed: one_indexed.unwrap_or_default().into(),
case_sensitive: case_sensitive.unwrap_or_default().into(),
})
}
Component::WeekNumber(WeekNumber { padding, repr }) => {
crate::format_description::public::Component::WeekNumber(super::public::modifier::WeekNumber {
padding: padding.unwrap_or_default().into(),
repr: repr.unwrap_or_default().into(),
})
}
Component::Year(Year { padding, repr, range, base, sign_behavior
}) => {
crate::format_description::public::Component::Year(super::public::modifier::Year {
padding: padding.unwrap_or_default().into(),
repr: repr.unwrap_or_default().into(),
range: range.unwrap_or_default().into(),
iso_week_based: base.unwrap_or_default().into(),
sign_is_mandatory: sign_behavior.unwrap_or_default().into(),
})
}
}
}
}
fn component_from_ast(name: &Spanned<&[u8]>, modifiers: &[ast::Modifier<'_>])
-> Result<Component, Error> {
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("day".as_bytes()) {
return Ok(Component::Day(Day::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("end".as_bytes()) {
return Ok(Component::End(End::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("hour".as_bytes()) {
return Ok(Component::Hour(Hour::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("ignore".as_bytes()) {
return Ok(Component::Ignore(Ignore::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("minute".as_bytes()) {
return Ok(Component::Minute(Minute::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("month".as_bytes()) {
return Ok(Component::Month(Month::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("offset_hour".as_bytes()) {
return Ok(Component::OffsetHour(OffsetHour::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("offset_minute".as_bytes()) {
return Ok(Component::OffsetMinute(OffsetMinute::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("offset_second".as_bytes()) {
return Ok(Component::OffsetSecond(OffsetSecond::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("ordinal".as_bytes()) {
return Ok(Component::Ordinal(Ordinal::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("period".as_bytes()) {
return Ok(Component::Period(Period::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("second".as_bytes()) {
return Ok(Component::Second(Second::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("subsecond".as_bytes()) {
return Ok(Component::Subsecond(Subsecond::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("unix_timestamp".as_bytes()) {
return Ok(Component::UnixTimestamp(UnixTimestamp::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("weekday".as_bytes()) {
return Ok(Component::Weekday(Weekday::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("week_number".as_bytes()) {
return Ok(Component::WeekNumber(WeekNumber::with_modifiers(&modifiers,
name.span)?));
}
#[allow(clippy :: string_lit_as_bytes)]
if name.eq_ignore_ascii_case("year".as_bytes()) {
return Ok(Component::Year(Year::with_modifiers(&modifiers,
name.span)?));
}
Err(name.span.error("invalid component"))
}component_definition! {
206 pub(super) enum Component {
207 Day = "day" {
208 padding = "padding": Option<Padding> => padding,
209 },
210 End = "end" {},
211 Hour = "hour" {
212 padding = "padding": Option<Padding> => padding,
213 base = "repr": Option<HourBase> => is_12_hour_clock,
214 },
215 Ignore = "ignore" {
216 #[required]
217 count = "count": Option<#[from_str] NonZero<u16>> => count,
218 },
219 Minute = "minute" {
220 padding = "padding": Option<Padding> => padding,
221 },
222 Month = "month" {
223 padding = "padding": Option<Padding> => padding,
224 repr = "repr": Option<MonthRepr> => repr,
225 case_sensitive = "case_sensitive": Option<MonthCaseSensitive> => case_sensitive,
226 },
227 OffsetHour = "offset_hour" {
228 sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
229 padding = "padding": Option<Padding> => padding,
230 },
231 OffsetMinute = "offset_minute" {
232 padding = "padding": Option<Padding> => padding,
233 },
234 OffsetSecond = "offset_second" {
235 padding = "padding": Option<Padding> => padding,
236 },
237 Ordinal = "ordinal" {
238 padding = "padding": Option<Padding> => padding,
239 },
240 Period = "period" {
241 case = "case": Option<PeriodCase> => is_uppercase,
242 case_sensitive = "case_sensitive": Option<PeriodCaseSensitive> => case_sensitive,
243 },
244 Second = "second" {
245 padding = "padding": Option<Padding> => padding,
246 },
247 Subsecond = "subsecond" {
248 digits = "digits": Option<SubsecondDigits> => digits,
249 },
250 UnixTimestamp = "unix_timestamp" {
251 precision = "precision": Option<UnixTimestampPrecision> => precision,
252 sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
253 },
254 Weekday = "weekday" {
255 repr = "repr": Option<WeekdayRepr> => repr,
256 one_indexed = "one_indexed": Option<WeekdayOneIndexed> => one_indexed,
257 case_sensitive = "case_sensitive": Option<WeekdayCaseSensitive> => case_sensitive,
258 },
259 WeekNumber = "week_number" {
260 padding = "padding": Option<Padding> => padding,
261 repr = "repr": Option<WeekNumberRepr> => repr,
262 },
263 Year = "year" {
264 padding = "padding": Option<Padding> => padding,
265 repr = "repr": Option<YearRepr> => repr,
266 range = "range": Option<YearRange> => range,
267 base = "base": Option<YearBase> => iso_week_based,
268 sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
269 },
270 }
271}
272
273macro_rules! target_ty {
274 ($name:ident $type:ty) => {
275 $type
276 };
277 ($name:ident) => {
278 super::public::modifier::$name
279 };
280}
281
282macro_rules! target_value {
284 ($name:ident $variant:ident $value:expr) => {
285 $value
286 };
287 ($name:ident $variant:ident) => {
288 super::public::modifier::$name::$variant
289 };
290}
291
292macro_rules! modifier {
293 ($(
294 enum $name:ident $(($target_ty:ty))? {
295 $(
296 $(#[$attr:meta])?
297 $variant:ident $(($target_value:expr))? = $parse_variant:literal
298 ),* $(,)?
299 }
300 )+) => {$(
301 #[derive(Default)]
302 enum $name {
303 $($(#[$attr])? $variant),*
304 }
305
306 impl $name {
307 fn from_modifier_value(value: &Spanned<&[u8]>) -> Result<Option<Self>, Error> {
309 $(if value.eq_ignore_ascii_case($parse_variant) {
310 return Ok(Some(Self::$variant));
311 })*
312 Err(value.span.error("invalid modifier value"))
313 }
314 }
315
316 impl From<$name> for target_ty!($name $($target_ty)?) {
317 fn from(modifier: $name) -> Self {
318 match modifier {
319 $($name::$variant => target_value!($name $variant $($target_value)?)),*
320 }
321 }
322 }
323 )+};
324}
325
326#[automatically_derived]
impl ::core::default::Default for YearRange {
#[inline]
fn default() -> YearRange { Self::Extended }
}
impl YearRange {
fn from_modifier_value(value: &Spanned<&[u8]>)
-> Result<Option<Self>, Error> {
if value.eq_ignore_ascii_case(b"standard") {
return Ok(Some(Self::Standard));
}
if value.eq_ignore_ascii_case(b"extended") {
return Ok(Some(Self::Extended));
}
Err(value.span.error("invalid modifier value"))
}
}
impl From<YearRange> for super::public::modifier::YearRange {
fn from(modifier: YearRange) -> Self {
match modifier {
YearRange::Standard =>
super::public::modifier::YearRange::Standard,
YearRange::Extended =>
super::public::modifier::YearRange::Extended,
}
}
}modifier! {
327 enum HourBase(bool) {
328 Twelve(true) = b"12",
329 #[default]
330 TwentyFour(false) = b"24",
331 }
332
333 enum MonthCaseSensitive(bool) {
334 False(false) = b"false",
335 #[default]
336 True(true) = b"true",
337 }
338
339 enum MonthRepr {
340 #[default]
341 Numerical = b"numerical",
342 Long = b"long",
343 Short = b"short",
344 }
345
346 enum Padding {
347 Space = b"space",
348 #[default]
349 Zero = b"zero",
350 None = b"none",
351 }
352
353 enum PeriodCase(bool) {
354 Lower(false) = b"lower",
355 #[default]
356 Upper(true) = b"upper",
357 }
358
359 enum PeriodCaseSensitive(bool) {
360 False(false) = b"false",
361 #[default]
362 True(true) = b"true",
363 }
364
365 enum SignBehavior(bool) {
366 #[default]
367 Automatic(false) = b"automatic",
368 Mandatory(true) = b"mandatory",
369 }
370
371 enum SubsecondDigits {
372 One = b"1",
373 Two = b"2",
374 Three = b"3",
375 Four = b"4",
376 Five = b"5",
377 Six = b"6",
378 Seven = b"7",
379 Eight = b"8",
380 Nine = b"9",
381 #[default]
382 OneOrMore = b"1+",
383 }
384
385 enum UnixTimestampPrecision {
386 #[default]
387 Second = b"second",
388 Millisecond = b"millisecond",
389 Microsecond = b"microsecond",
390 Nanosecond = b"nanosecond",
391 }
392
393 enum WeekNumberRepr {
394 #[default]
395 Iso = b"iso",
396 Sunday = b"sunday",
397 Monday = b"monday",
398 }
399
400 enum WeekdayCaseSensitive(bool) {
401 False(false) = b"false",
402 #[default]
403 True(true) = b"true",
404 }
405
406 enum WeekdayOneIndexed(bool) {
407 False(false) = b"false",
408 #[default]
409 True(true) = b"true",
410 }
411
412 enum WeekdayRepr {
413 Short = b"short",
414 #[default]
415 Long = b"long",
416 Sunday = b"sunday",
417 Monday = b"monday",
418 }
419
420 enum YearBase(bool) {
421 #[default]
422 Calendar(false) = b"calendar",
423 IsoWeek(true) = b"iso_week",
424 }
425
426 enum YearRepr {
427 #[default]
428 Full = b"full",
429 Century = b"century",
430 LastTwo = b"last_two",
431 }
432
433 enum YearRange {
434 Standard = b"standard",
435 #[default]
436 Extended = b"extended",
437 }
438}
439
440fn parse_from_modifier_value<T: FromStr>(value: &Spanned<&[u8]>) -> Result<Option<T>, Error> {
441 str::from_utf8(value)
442 .ok()
443 .and_then(|val| val.parse::<T>().ok())
444 .map(|val| Some(val))
445 .ok_or_else(|| value.span.error("invalid modifier value"))
446}