const_format_proc_macros/
format_macro.rs

1use crate::{
2    format_args::{ExpandInto, FormatArgs, FormatIfArgs, LocalVariable, WriteArgs},
3    parse_utils::TokenStream2Ext,
4    shared_arg_parsing::{ExprArg, ExprArgs},
5    Error,
6};
7
8use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
9
10use quote::{quote, quote_spanned};
11
12#[cfg(test)]
13mod tests;
14
15////////////////////////////////////////////////////////////////////////////////
16
17pub(crate) fn concatcp_impl(value: ExprArgs) -> Result<TokenStream2, crate::Error> {
18    let fmt_var = Ident::new("fmt", Span::mixed_site());
19
20    let concat_args = value.args.iter().map(|ExprArg { expr, span }| {
21        quote_spanned!(span.start=>
22            __cf_osRcTFl4A::pmr::PConvWrapper(#expr).to_pargument_display(#fmt_var)
23        )
24    });
25
26    Ok(quote!(({
27        // The suffix is to avoid name collisions with identifiers in the passed-in expression.
28        #[doc(hidden)]
29        #[allow(unused_mut, non_snake_case)]
30        const CONCATP_NHPMWYD3NJA : &[__cf_osRcTFl4A::pmr::PArgument] = {
31            let #fmt_var = __cf_osRcTFl4A::pmr::FormattingFlags::NEW;
32
33            &[
34                #( #concat_args ),*
35            ]
36        };
37
38        __cf_osRcTFl4A::__concatcp_inner!(CONCATP_NHPMWYD3NJA)
39    })))
40}
41
42////////////////////////////////////////////////////////////////////////////////
43
44pub(crate) fn formatcp_if_macro_impl(value: FormatIfArgs) -> Result<TokenStream2, crate::Error> {
45    formatcp_impl(value.inner)
46}
47
48pub(crate) fn formatcp_impl(fmt_args: FormatArgs) -> Result<TokenStream2, crate::Error> {
49    let locals = fmt_args
50        .local_variables
51        .iter()
52        .map(|LocalVariable { ident, expr }| {
53            let span = ident.span();
54            quote_spanned!(span=> let #ident = #expr;)
55        });
56
57    for ei in fmt_args.expanded_into.iter() {
58        if let ExpandInto::WithFormatter(wf) = ei {
59            return Err(crate::Error::new(
60                wf.fmt_ident.span(),
61                "Can't do custom formatting in the `formatcp` macro",
62            ));
63        }
64    }
65
66    let parg_constructor = fmt_args.expanded_into.iter().map(|ei| match ei {
67        ExpandInto::Str(str, rawness) => {
68            let str_tokens = rawness.tokenize_sub(str);
69            quote!(
70                __cf_osRcTFl4A::pmr::PConvWrapper(#str_tokens)
71                    .to_pargument_display(__cf_osRcTFl4A::pmr::FormattingFlags::NEW)
72            )
73        }
74        ExpandInto::Formatted(fmted) => {
75            let to_pargument_m = fmted.format.to_pargument_method_name();
76            let formatting = fmted.format;
77            let local_variable = &fmted.local_variable;
78            let span = local_variable.span();
79            // I had to use `set_span_recursive` to set the span to that of the argument,
80            // quote_span doesn't work for that somehow.
81            quote!(
82                __cf_osRcTFl4A::pmr::PConvWrapper(#local_variable).#to_pargument_m(#formatting)
83            )
84            .set_span_recursive(span)
85        }
86        ExpandInto::WithFormatter { .. } => unreachable!(),
87    });
88
89    let fmt_if_true = quote!({
90        let mut len = 0usize;
91
92        #( #locals )*
93
94        &[
95            #( #parg_constructor ),*
96        ]
97    });
98
99    if let Some(cond) = fmt_args.condition {
100        Ok(quote!(({
101            enum __Fooosrctfl4a {}
102
103            // This is generic so that the constant is only evaluated when it's needed.
104            impl<T> __cf_osRcTFl4A::pmr::ConcatArgsIf<T, true> for __Fooosrctfl4a {
105                #[doc(hidden)]
106                const PARGUMENTS : &'static [__cf_osRcTFl4A::pmr::PArgument] = #fmt_if_true;
107            }
108
109            __cf_osRcTFl4A::__concatcp_inner!(
110                <__Fooosrctfl4a as __cf_osRcTFl4A::pmr::ConcatArgsIf<(), #cond>>::PARGUMENTS
111            )
112        })))
113    } else {
114        Ok(quote!(({
115            // The suffix is to avoid name collisions with identifiers in the passed-in expression.
116            #[doc(hidden)]
117            #[allow(unused_mut, non_snake_case)]
118            const CONCATP_NHPMWYD3NJA : &[__cf_osRcTFl4A::pmr::PArgument] = #fmt_if_true;
119
120            __cf_osRcTFl4A::__concatcp_inner!(CONCATP_NHPMWYD3NJA)
121        })))
122    }
123}
124
125////////////////////////////////////////////////////////////////////////////////
126
127pub(crate) fn formatc_if_macro_impl(value: FormatIfArgs) -> Result<TokenStream2, crate::Error> {
128    formatc_macro_impl(value.inner)
129}
130
131////////////////////////////////////////////////////////////////////////////////
132
133pub(crate) fn formatc_macro_impl(fmt_args: FormatArgs) -> Result<TokenStream2, crate::Error> {
134    let locals = fmt_args.local_variables.iter().map(|arg| &arg.ident);
135    let expr = fmt_args.local_variables.iter().map(|arg| &arg.expr);
136
137    let strwriter = Ident::new("strwriter", Span::mixed_site());
138
139    let writing_formatted = fmt_args
140        .expanded_into
141        .iter()
142        .map(|ei| ei.fmt_call(&strwriter));
143
144    let cond_a = fmt_args.condition.iter();
145
146    Ok(quote!(({
147        #[doc(hidden)]
148        #[allow(non_snake_case)]
149        const fn fmt_NHPMWYD3NJA(
150            mut #strwriter: __cf_osRcTFl4A::fmt::Formatter<'_>,
151        ) -> __cf_osRcTFl4A::Result {
152            match (#(&(#expr),)*) {
153                (#(#locals,)*) => {
154                    #(
155                        __cf_osRcTFl4A::try_!(#writing_formatted);
156                    )*
157                },
158            }
159            __cf_osRcTFl4A::pmr::Ok(())
160        }
161
162        __cf_osRcTFl4A::__concatc_inner!(
163            fmt_NHPMWYD3NJA,
164            #((#cond_a) && )* true,
165            ____
166        )
167    })))
168}
169
170pub(crate) fn writec_macro_impl(value: WriteArgs) -> Result<TokenStream2, Error> {
171    let writer_expr = value.writer_expr;
172    let writer_span = value.writer_span;
173    let FormatArgs {
174        condition: _,
175        expanded_into,
176        local_variables,
177    } = value.format_args;
178
179    let locals = local_variables.iter().map(|arg| &arg.ident);
180    let expr = local_variables.iter().map(|arg| &arg.expr);
181
182    let strwriter = Ident::new("strwriter", Span::mixed_site());
183
184    let writing_formatted = expanded_into.iter().map(|ei| ei.fmt_call(&strwriter));
185
186    let borrow_mutably = quote_spanned!(writer_span=> ((#writer_expr).borrow_mutably()));
187
188    let make_formatter = quote_spanned!(writer_span =>
189        let mut marker = __cf_osRcTFl4A::pmr::IsAWriteMarker::NEW;
190        if false {
191            marker = marker.infer_type(&#strwriter);
192        }
193        let mut #strwriter = marker.coerce(#strwriter);
194        let mut #strwriter =
195            #strwriter.make_formatter(__cf_osRcTFl4A::FormattingFlags::NEW);
196    );
197
198    Ok(quote! {({
199
200        #[allow(non_snake_case)]
201        match (#borrow_mutably, #(&(#expr),)*) {
202            (#strwriter, #(#locals,)*) => {
203                #make_formatter
204
205                loop {
206                    #(
207                        __cf_osRcTFl4A::unwrap_or_else!(
208                            #writing_formatted,
209                            |e| break __cf_osRcTFl4A::pmr::Err(e)
210                        );
211                    )*
212                    break __cf_osRcTFl4A::pmr::Ok(());
213                }
214            }
215        }
216    })})
217}