Skip to main content

time_macros/format_description/public/
mod.rs

1mod component;
2pub(super) mod modifier;
3mod optimizing;
4
5use proc_macro::TokenStream;
6
7pub(crate) use self::component::Component;
8use crate::FormatDescriptionVersion;
9use crate::to_tokens::ToTokenStream;
10
11#[derive(Clone)]
12pub(crate) struct OwnedFormatItem {
13    pub(crate) version: FormatDescriptionVersion,
14    pub(crate) inner: OwnedFormatItemInner,
15}
16
17#[derive(Clone)]
18pub(crate) enum OwnedFormatItemInner {
19    Literal(Vec<u8>),
20    StringLiteral(String),
21    Component(Component),
22    Compound(Vec<Self>),
23    Optional { format: bool, item: Box<Self> },
24    First(Vec<Self>),
25}
26
27impl ToTokenStream for OwnedFormatItem {
28    fn append_to(self, ts: &mut TokenStream) {
29        match self.version {
30            FormatDescriptionVersion::V1 | FormatDescriptionVersion::V2 => match self.inner {
31                OwnedFormatItemInner::Literal(bytes) => quote_append! { ts
32                    BorrowedFormatItem::Literal(#(Literal::byte_string(bytes.as_ref())))
33                },
34                OwnedFormatItemInner::StringLiteral(string) => quote_append! { ts
35                    BorrowedFormatItem::StringLiteral(#(string.as_ref()))
36                },
37                OwnedFormatItemInner::Component(component) => quote_append! { ts
38                    BorrowedFormatItem::Component { 0: #S(component) }
39                },
40                OwnedFormatItemInner::Compound(items) => {
41                    let items = items
42                        .into_iter()
43                        .map(|item| {
44                            quote_! { #S(Self { version: self.version, inner: item }), }
45                        })
46                        .collect::<TokenStream>();
47                    quote_append! { ts
48                        BorrowedFormatItem::Compound { 0: &[#S(items)] }
49                    }
50                }
51                OwnedFormatItemInner::Optional { format, item } => {
52                    if !format {
53                        bug!("v1 and v2 format descriptions must format optional items")
54                    }
55                    quote_append! { ts
56                        BorrowedFormatItem::Optional {
57                            0: &#S(Self { version: self.version, inner: *item })
58                        }
59                    }
60                }
61                OwnedFormatItemInner::First(items) => {
62                    let items = items
63                        .into_iter()
64                        .map(|item| {
65                            quote_! { #S(Self { version: self.version, inner: item }), }
66                        })
67                        .collect::<TokenStream>();
68                    quote_append! { ts
69                        BorrowedFormatItem::First { 0: &[#S(items)] }
70                    }
71                }
72            },
73            FormatDescriptionVersion::V3 => match self.inner {
74                OwnedFormatItemInner::Literal(_) => {
75                    bug!("v3 format descriptions should never have non-UTF8 literals")
76                }
77                OwnedFormatItemInner::StringLiteral(string) => quote_append! { ts
78                    FormatDescriptionV3Inner::BorrowedLiteral(#(string.as_ref()))
79                },
80                OwnedFormatItemInner::Component(component) => {
81                    // v3 format descriptions have components directly on the item, not as a
82                    // sub-enum. Swap out the name of the enum that is being constructed.
83                    let tokens = component.into_token_stream();
84                    let mut iter = tokens.into_iter().peekable();
85                    if let Some(first) = iter.peek_mut() {
86                        *first = proc_macro::TokenTree::Ident(proc_macro::Ident::new(
87                            "FormatDescriptionV3Inner",
88                            proc_macro::Span::mixed_site(),
89                        ));
90                    } else {
91                        bug!("component should have at least one token")
92                    }
93                    ts.extend(iter);
94                }
95                OwnedFormatItemInner::Compound(items) => {
96                    let items = items
97                        .into_iter()
98                        .map(|item| {
99                            quote_! { #S(Self { version: self.version, inner: item }), }
100                        })
101                        .collect::<TokenStream>();
102                    quote_append! { ts
103                        FormatDescriptionV3Inner::BorrowedCompound(const { &[#S(items)] })
104                    }
105                }
106                OwnedFormatItemInner::Optional { format, item } => {
107                    quote_append! { ts
108                        FormatDescriptionV3Inner::BorrowedOptional {
109                            format: #S(format),
110                            item: &#S(Self { version: self.version, inner: *item })
111                        }
112                    }
113                }
114                OwnedFormatItemInner::First(items) => {
115                    let items = items
116                        .into_iter()
117                        .map(|item| {
118                            quote_! { #S(Self { version: self.version, inner: item }), }
119                        })
120                        .collect::<TokenStream>();
121                    quote_append! { ts
122                        FormatDescriptionV3Inner::BorrowedFirst(const { &[#S(items)] })
123                    }
124                }
125            },
126        }
127    }
128}