Skip to main content

time_macros/format_description/
format_item.rs

1use std::num::NonZero;
2use std::str::{self, FromStr};
3
4use super::{Error, Span, Spanned, Unused, ast, 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 {
    trailing_input: Option<TrailingInput>,
}
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 { trailing_input: None };
        for modifier in modifiers {

            #[allow(clippy :: string_lit_as_bytes)]
            if modifier.key.eq_ignore_ascii_case("trailing_input".as_bytes())
                {
                this.trailing_input =
                    <TrailingInput>::from_modifier_value(&modifier.value)?;
                continue;
            }
            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 { trailing_input }) => {
                crate::format_description::public::Component::End(super::public::modifier::End {
                        trailing_input: trailing_input.unwrap_or_default().into(),
                    })
            }
            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            trailing_input = "trailing_input": Option<TrailingInput> => trailing_input,
212        },
213        Hour = "hour" {
214            padding = "padding": Option<Padding> => padding,
215            base = "repr": Option<HourBase> => is_12_hour_clock,
216        },
217        Ignore = "ignore" {
218            #[required]
219            count = "count": Option<#[from_str] NonZero<u16>> => count,
220        },
221        Minute = "minute" {
222            padding = "padding": Option<Padding> => padding,
223        },
224        Month = "month" {
225            padding = "padding": Option<Padding> => padding,
226            repr = "repr": Option<MonthRepr> => repr,
227            case_sensitive = "case_sensitive": Option<MonthCaseSensitive> => case_sensitive,
228        },
229        OffsetHour = "offset_hour" {
230            sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
231            padding = "padding": Option<Padding> => padding,
232        },
233        OffsetMinute = "offset_minute" {
234            padding = "padding": Option<Padding> => padding,
235        },
236        OffsetSecond = "offset_second" {
237            padding = "padding": Option<Padding> => padding,
238        },
239        Ordinal = "ordinal" {
240            padding = "padding": Option<Padding> => padding,
241        },
242        Period = "period" {
243            case = "case": Option<PeriodCase> => is_uppercase,
244            case_sensitive = "case_sensitive": Option<PeriodCaseSensitive> => case_sensitive,
245        },
246        Second = "second" {
247            padding = "padding": Option<Padding> => padding,
248        },
249        Subsecond = "subsecond" {
250            digits = "digits": Option<SubsecondDigits> => digits,
251        },
252        UnixTimestamp = "unix_timestamp" {
253            precision = "precision": Option<UnixTimestampPrecision> => precision,
254            sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
255        },
256        Weekday = "weekday" {
257            repr = "repr": Option<WeekdayRepr> => repr,
258            one_indexed = "one_indexed": Option<WeekdayOneIndexed> => one_indexed,
259            case_sensitive = "case_sensitive": Option<WeekdayCaseSensitive> => case_sensitive,
260        },
261        WeekNumber = "week_number" {
262            padding = "padding": Option<Padding> => padding,
263            repr = "repr": Option<WeekNumberRepr> => repr,
264        },
265        Year = "year" {
266            padding = "padding": Option<Padding> => padding,
267            repr = "repr": Option<YearRepr> => repr,
268            range = "range": Option<YearRange> => range,
269            base = "base": Option<YearBase> => iso_week_based,
270            sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
271        },
272    }
273}
274
275macro_rules! target_ty {
276    ($name:ident $type:ty) => {
277        $type
278    };
279    ($name:ident) => {
280        super::public::modifier::$name
281    };
282}
283
284/// Get the target value for a given enum.
285macro_rules! target_value {
286    ($name:ident $variant:ident $value:expr) => {
287        $value
288    };
289    ($name:ident $variant:ident) => {
290        super::public::modifier::$name::$variant
291    };
292}
293
294macro_rules! modifier {
295    ($(
296        enum $name:ident $(($target_ty:ty))? {
297            $(
298                $(#[$attr:meta])?
299                $variant:ident $(($target_value:expr))? = $parse_variant:literal
300            ),* $(,)?
301        }
302    )+) => {$(
303        #[derive(Default)]
304        enum $name {
305            $($(#[$attr])? $variant),*
306        }
307
308        impl $name {
309            /// Parse the modifier from its string representation.
310            fn from_modifier_value(value: &Spanned<&[u8]>) -> Result<Option<Self>, Error> {
311                $(if value.eq_ignore_ascii_case($parse_variant) {
312                    return Ok(Some(Self::$variant));
313                })*
314                Err(value.span.error("invalid modifier value"))
315            }
316        }
317
318        impl From<$name> for target_ty!($name $($target_ty)?) {
319            fn from(modifier: $name) -> Self {
320                match modifier {
321                    $($name::$variant => target_value!($name $variant $($target_value)?)),*
322                }
323            }
324        }
325    )+};
326}
327
328#[automatically_derived]
impl ::core::default::Default for YearRange {
    #[inline]
    fn default() -> YearRange { Self::Extended }
}
impl YearRange {
    /// Parse the modifier from its string representation.
    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! {
329    enum HourBase(bool) {
330        Twelve(true) = b"12",
331        #[default]
332        TwentyFour(false) = b"24",
333    }
334
335    enum MonthCaseSensitive(bool) {
336        False(false) = b"false",
337        #[default]
338        True(true) = b"true",
339    }
340
341    enum MonthRepr {
342        #[default]
343        Numerical = b"numerical",
344        Long = b"long",
345        Short = b"short",
346    }
347
348    enum Padding {
349        Space = b"space",
350        #[default]
351        Zero = b"zero",
352        None = b"none",
353    }
354
355    enum PeriodCase(bool) {
356        Lower(false) = b"lower",
357        #[default]
358        Upper(true) = b"upper",
359    }
360
361    enum PeriodCaseSensitive(bool) {
362        False(false) = b"false",
363        #[default]
364        True(true) = b"true",
365    }
366
367    enum SignBehavior(bool) {
368        #[default]
369        Automatic(false) = b"automatic",
370        Mandatory(true) = b"mandatory",
371    }
372
373    enum SubsecondDigits {
374        One = b"1",
375        Two = b"2",
376        Three = b"3",
377        Four = b"4",
378        Five = b"5",
379        Six = b"6",
380        Seven = b"7",
381        Eight = b"8",
382        Nine = b"9",
383        #[default]
384        OneOrMore = b"1+",
385    }
386
387    enum TrailingInput {
388        #[default]
389        Prohibit = b"prohibit",
390        Discard = b"discard",
391    }
392
393    enum UnixTimestampPrecision {
394        #[default]
395        Second = b"second",
396        Millisecond = b"millisecond",
397        Microsecond = b"microsecond",
398        Nanosecond = b"nanosecond",
399    }
400
401    enum WeekNumberRepr {
402        #[default]
403        Iso = b"iso",
404        Sunday = b"sunday",
405        Monday = b"monday",
406    }
407
408    enum WeekdayCaseSensitive(bool) {
409        False(false) = b"false",
410        #[default]
411        True(true) = b"true",
412    }
413
414    enum WeekdayOneIndexed(bool) {
415        False(false) = b"false",
416        #[default]
417        True(true) = b"true",
418    }
419
420    enum WeekdayRepr {
421        Short = b"short",
422        #[default]
423        Long = b"long",
424        Sunday = b"sunday",
425        Monday = b"monday",
426    }
427
428    enum YearBase(bool) {
429        #[default]
430        Calendar(false) = b"calendar",
431        IsoWeek(true) = b"iso_week",
432    }
433
434    enum YearRepr {
435        #[default]
436        Full = b"full",
437        Century = b"century",
438        LastTwo = b"last_two",
439    }
440
441    enum YearRange {
442        Standard = b"standard",
443        #[default]
444        Extended = b"extended",
445    }
446}
447
448fn parse_from_modifier_value<T: FromStr>(value: &Spanned<&[u8]>) -> Result<Option<T>, Error> {
449    str::from_utf8(value)
450        .ok()
451        .and_then(|val| val.parse::<T>().ok())
452        .map(|val| Some(val))
453        .ok_or_else(|| value.span.error("invalid modifier value"))
454}