Skip to main content

parse_display_derive/
lib.rs

1#![recursion_limit = "128"]
2#![allow(clippy::large_enum_variant)]
3
4//! The documentation for this crate is found in the parse-display crate.
5
6extern crate proc_macro;
7
8#[macro_use]
9mod regex_utils;
10
11#[macro_use]
12mod syn_utils;
13
14mod bound;
15mod format_syntax;
16mod parser_builder;
17
18use crate::{format_syntax::*, syn_utils::*};
19use bound::{Bound, Bounds};
20use parser_builder::{ParseVariantCode, ParserBuilder};
21use proc_macro2::{Span, TokenStream};
22use quote::{ToTokens, format_ident, quote, quote_spanned};
23use regex_syntax::escape;
24use std::{
25    collections::BTreeMap,
26    fmt::{Display, Formatter},
27};
28use structmeta::{Flag, StructMeta, ToTokens};
29use syn::{
30    Attribute, Data, DataEnum, DataStruct, DeriveInput, Expr, Field, Fields, FieldsNamed,
31    FieldsUnnamed, GenericArgument, Ident, LitStr, Member, Path, PathArguments, Result, Token,
32    Type, Variant,
33    ext::IdentExt,
34    parse::{Parse, ParseStream},
35    parse_macro_input, parse_quote, parse_str,
36    spanned::Spanned,
37};
38
39#[proc_macro_derive(Display, attributes(display))]
40pub fn derive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
41    let input = parse_macro_input!(input as DeriveInput);
42    into_macro_output(match &input.data {
43        Data::Struct(data) => derive_display_for_struct(&input, data),
44        Data::Enum(data) => derive_display_for_enum(&input, data),
45        Data::Union(_) => panic!("`#[derive(Display)]` supports only enum or struct."),
46    })
47}
48
49fn derive_display_for_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
50    let hattrs = HelperAttributes::from(&input.attrs, false)?;
51    let vb = VarBase::Struct { data };
52
53    let mut format = hattrs.format;
54    if format.is_none() {
55        format = DisplayFormat::from_newtype_struct(data);
56    }
57    let Some(format) = format else {
58        bail!(
59            input.span(),
60            r#"`#[display("format")]` is required except newtype pattern."#,
61        )
62    };
63    let mut bounds = Bounds::from_data(hattrs.bound_display);
64    let generics = &GenericParamSet::new(&input.generics);
65    let cx = CodeContext {
66        generics,
67        crate_path: &hattrs.crate_path,
68    };
69    let write = format
70        .format_args(&vb, &None, &mut bounds, &cx)?
71        .build_write(quote!(f))?;
72    let trait_path = parse_quote!(::core::fmt::Display);
73    let wheres = bounds.build_wheres(&trait_path);
74    impl_trait_result(
75        input,
76        &trait_path,
77        &wheres,
78        quote! {
79            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
80                #write
81            }
82        },
83        hattrs.dump_display,
84    )
85}
86fn derive_display_for_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
87    fn make_arm(
88        hattrs_enum: &HelperAttributes,
89        variant: &Variant,
90        bounds: &mut Bounds,
91        cx: &CodeContext,
92    ) -> Result<TokenStream> {
93        let fields = match &variant.fields {
94            Fields::Named(fields) => {
95                let fields = FieldKey::from_fields_named(fields).map(|(key, ..)| {
96                    let var = key.binding_var();
97                    quote! { #key : ref #var }
98                });
99                quote! { { #(#fields,)* } }
100            }
101            Fields::Unnamed(fields) => {
102                let fields = FieldKey::from_fields_unnamed(fields).map(|(key, ..)| {
103                    let var = key.binding_var();
104                    quote! { ref #var }
105                });
106                quote! { ( #(#fields,)* ) }
107            }
108            Fields::Unit => quote! {},
109        };
110        let hattrs_variant = HelperAttributes::from(&variant.attrs, false)?;
111        let style = DisplayStyle::from_helper_attributes(hattrs_enum, &hattrs_variant);
112        let mut format = hattrs_variant.format;
113        if format.is_none() {
114            format.clone_from(&hattrs_enum.format);
115        }
116        if format.is_none() {
117            format = DisplayFormat::from_unit_variant(variant)?;
118        }
119        let Some(format) = format else {
120            bail!(
121                variant.span(),
122                r#"`#[display(\"format\")]` is required except unit variant."#
123            )
124        };
125        let variant_ident = &variant.ident;
126        let write = format
127            .format_args(
128                &VarBase::Variant { variant, style },
129                &None,
130                &mut bounds.child(hattrs_variant.bound_display),
131                cx,
132            )?
133            .build_write(quote!(f))?;
134        Ok(quote! {
135            & Self::#variant_ident #fields => {
136                #write
137            },
138        })
139    }
140    let hattrs = HelperAttributes::from(&input.attrs, false)?;
141    let mut bounds = Bounds::from_data(hattrs.bound_display.clone());
142    let mut arms = Vec::new();
143    let generics = &GenericParamSet::new(&input.generics);
144    let cx = CodeContext {
145        generics,
146        crate_path: &hattrs.crate_path,
147    };
148    for variant in &data.variants {
149        arms.push(make_arm(&hattrs, variant, &mut bounds, &cx)?);
150    }
151    let trait_path = parse_quote!(::core::fmt::Display);
152    let contents = quote! {
153        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
154            match self {
155                #(#arms)*
156            }
157        }
158    };
159    let wheres = bounds.build_wheres(&trait_path);
160    impl_trait_result(input, &trait_path, &wheres, contents, hattrs.dump_display)
161}
162
163#[proc_macro_derive(FromStr, attributes(display, from_str))]
164pub fn derive_from_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
165    let input = parse_macro_input!(input as DeriveInput);
166    into_macro_output(match &input.data {
167        Data::Struct(data) => derive_from_str_for_struct(&input, data),
168        Data::Enum(data) => derive_from_str_for_enum(&input, data),
169        Data::Union(_) => panic!("`#[derive(FromStr)]` supports only enum or struct."),
170    })
171}
172fn derive_from_str_for_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
173    let hattrs = HelperAttributes::from(&input.attrs, true)?;
174    let p = ParserBuilder::from_struct(&hattrs, data)?;
175    let crate_path = &hattrs.crate_path;
176    let warnings = hattrs.deprecated_default_fields_warnings();
177    let trait_path = parse_quote!(::core::str::FromStr);
178    let body = p.build_from_str_body(parse_quote!(Self))?;
179    let generics = GenericParamSet::new(&input.generics);
180    let mut bounds = Bounds::from_data(hattrs.bound_from_str_resolved());
181    p.build_bounds(&generics, &mut bounds)?;
182    let wheres = bounds.build_wheres(&trait_path);
183    let mut ts = TokenStream::new();
184    ts.extend(impl_trait(
185        input,
186        &trait_path,
187        &wheres,
188        quote! {
189            type Err = #crate_path::ParseError;
190            fn from_str(s: &str) -> ::core::result::Result<Self, Self::Err> {
191                #warnings
192                #body
193            }
194        },
195    ));
196
197    if cfg!(feature = "std") {
198        let body = p.build_from_str_regex_body()?;
199        ts.extend(impl_trait(
200            input,
201            &parse_quote!(#crate_path::FromStrRegex),
202            &wheres,
203            quote! {
204                fn from_str_regex() -> String {
205                    #body
206                }
207            },
208        ));
209    }
210    dump_if(hattrs.dump_from_str, &ts);
211    Ok(ts)
212}
213fn derive_from_str_for_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
214    let hattrs_enum = HelperAttributes::from(&input.attrs, true)?;
215    if let Some(span) = hattrs_enum.default_self {
216        bail!(span, "`#[from_str(default)]` cannot be specified for enum.");
217    }
218    let crate_path = &hattrs_enum.crate_path;
219    let trait_path = parse_quote!(::core::str::FromStr);
220    let mut bounds = Bounds::from_data(hattrs_enum.bound_from_str_resolved());
221    let generics = GenericParamSet::new(&input.generics);
222    let mut bodys = Vec::new();
223    let mut arms = Vec::new();
224    let mut regex_fmts = Vec::new();
225    let mut regex_args = Vec::new();
226    let mut deprecated_default_fields_warning_spans =
227        hattrs_enum.deprecated_default_fields_warning_spans.clone();
228    for variant in &data.variants {
229        let hattrs_variant = HelperAttributes::from(&variant.attrs, true)?;
230        deprecated_default_fields_warning_spans.extend(
231            hattrs_variant
232                .deprecated_default_fields_warning_spans
233                .iter(),
234        );
235        if hattrs_variant.ignore.value() {
236            continue;
237        }
238        let variant_ident = &variant.ident;
239        let constructor = parse_quote!(Self::#variant_ident);
240        let p = ParserBuilder::from_variant(&hattrs_variant, &hattrs_enum, variant)?;
241        let mut bounds = bounds.child(hattrs_variant.bound_from_str_resolved());
242        p.build_bounds(&generics, &mut bounds)?;
243        match p.build_parse_variant_code(constructor)? {
244            ParseVariantCode::MatchArm(arm) => arms.push(arm),
245            ParseVariantCode::Statement(body) => bodys.push(body),
246        }
247        p.build_regex_fmts_args(&mut regex_fmts, &mut regex_args)?;
248    }
249    let match_body = if arms.is_empty() {
250        quote! {}
251    } else {
252        quote! {
253            match s {
254                #(#arms,)*
255                _ => { }
256            }
257        }
258    };
259    let wheres = bounds.build_wheres(&trait_path);
260    let warnings =
261        deprecated_default_fields_warnings(crate_path, &deprecated_default_fields_warning_spans);
262
263    let mut ts = TokenStream::new();
264    ts.extend(impl_trait(
265        input,
266        &trait_path,
267        &wheres,
268        quote! {
269            type Err = #crate_path::ParseError;
270            fn from_str(s: &str) -> ::core::result::Result<Self, Self::Err> {
271                #warnings
272                #match_body
273                #({ #bodys })*
274                ::core::result::Result::Err(#crate_path::ParseError::new())
275            }
276        },
277    ));
278    if cfg!(feature = "std") {
279        let body = if regex_args.is_empty() {
280            let fmts = regex_fmts
281                .into_iter()
282                .map(|s| escape(&s.unwrap()))
283                .collect::<Vec<_>>();
284            let s = fmts.join("|");
285            quote! { #s.into() }
286        } else {
287            let fmts = regex_fmts
288                .into_iter()
289                .map(|s| match s {
290                    Some(s) => format!("({})", escape_fmt(&escape(&s))),
291                    None => "{}".to_string(),
292                })
293                .collect::<Vec<_>>();
294            let fmt = fmts.join("|");
295            quote! { format!(#fmt, #(#regex_args,)*) }
296        };
297
298        ts.extend(impl_trait(
299            input,
300            &parse_quote!(#crate_path::FromStrRegex),
301            &wheres,
302            quote! {
303                fn from_str_regex() -> String {
304                    #body
305                }
306            },
307        ));
308    }
309    dump_if(hattrs_enum.dump_from_str, &ts);
310    Ok(ts)
311}
312
313fn get_newtype_field(data: &DataStruct) -> Option<String> {
314    let fields: Vec<_> = data.fields.iter().collect();
315    if fields.len() == 1 {
316        if let Some(ident) = &fields[0].ident {
317            Some(ident.to_string())
318        } else {
319            Some("0".into())
320        }
321    } else {
322        None
323    }
324}
325
326struct With {
327    capture: String,
328    key: FieldKey,
329    expr: Expr,
330    ty: Type,
331}
332impl With {
333    fn new(capture: String, key: &FieldKey, expr: &Expr, ty: &Type) -> Self {
334        Self {
335            capture,
336            key: key.clone(),
337            expr: expr.clone(),
338            ty: ty.clone(),
339        }
340    }
341}
342
343#[derive(StructMeta)]
344struct DisplayArgs {
345    #[struct_meta(unnamed)]
346    format: Option<LitStr>,
347    with: Option<Expr>,
348    opt: Flag,
349    style: Option<LitStr>,
350    bound: Option<Vec<Quotable<Bound>>>,
351    #[struct_meta(name = "crate")]
352    crate_path: Option<Path>,
353    dump: bool,
354}
355
356#[derive(Clone, ToTokens)]
357struct DefaultField(Member);
358
359impl Parse for DefaultField {
360    fn parse(input: ParseStream) -> Result<Self> {
361        if input.peek(Ident::peek_any) {
362            Ok(Self(Member::Named(Ident::parse_any(input)?)))
363        } else {
364            Ok(Self(input.parse()?))
365        }
366    }
367}
368
369#[derive(StructMeta)]
370struct FromStrArgs {
371    regex: Option<LitStr>,
372    regex_infer: Flag,
373    with: Option<Expr>,
374    new: Option<Expr>,
375    bound: Option<Vec<Quotable<Bound>>>,
376    default: Flag,
377    default_fields: Option<Vec<Quotable<DefaultField>>>,
378    ignore: Flag,
379    dump: bool,
380}
381
382#[derive(Clone)]
383struct HelperAttributes {
384    format: Option<DisplayFormat>,
385    with: Option<Expr>,
386    opt: Flag,
387    style: Option<DisplayStyle>,
388    bound_display: Option<Vec<Bound>>,
389    bound_from_str: Option<Vec<Bound>>,
390    regex: Option<LitStr>,
391    regex_infer: bool,
392    default_self: Option<Span>,
393    default_fields: Vec<DefaultField>,
394    deprecated_default_fields_warning_spans: Vec<Span>,
395    new_expr: Option<Expr>,
396    ignore: Flag,
397    dump_display: bool,
398    dump_from_str: bool,
399    crate_path: Path,
400}
401impl HelperAttributes {
402    fn from(attrs: &[Attribute], use_from_str: bool) -> Result<Self> {
403        let mut hattrs = Self {
404            format: None,
405            with: None,
406            opt: Flag::NONE,
407            style: None,
408            bound_display: None,
409            bound_from_str: None,
410            regex: None,
411            regex_infer: false,
412            new_expr: None,
413            default_self: None,
414            default_fields: Vec::new(),
415            deprecated_default_fields_warning_spans: Vec::new(),
416            ignore: Flag::NONE,
417            dump_display: false,
418            dump_from_str: false,
419            crate_path: parse_quote!(::parse_display),
420        };
421        for a in attrs {
422            if a.path().is_ident("display") {
423                hattrs.set_display_args(a.parse_args()?)?;
424            }
425            if use_from_str && a.path().is_ident("from_str") {
426                hattrs.push_from_str_warning_spans(a)?;
427                hattrs.set_from_str_args(a.parse_args()?);
428            }
429        }
430        Ok(hattrs)
431    }
432    fn set_display_args(&mut self, args: DisplayArgs) -> Result<()> {
433        if let Some(format) = &args.format {
434            self.format = Some(DisplayFormat::parse_lit_str(format)?);
435        }
436        if let Some(with) = args.with {
437            self.with = Some(with);
438        }
439        if args.opt.value() {
440            self.opt = args.opt;
441        }
442        if let Some(style) = &args.style {
443            self.style = Some(DisplayStyle::parse_lit_str(style)?);
444        }
445        if let Some(bounds) = args.bound {
446            let list = self.bound_display.get_or_insert(Vec::new());
447            for bound in bounds {
448                for bound in bound.into_iter() {
449                    list.push(bound);
450                }
451            }
452        }
453        if let Some(crate_path) = &args.crate_path {
454            self.crate_path = crate_path.clone();
455        }
456        self.dump_from_str |= args.dump;
457        self.dump_display |= args.dump;
458        Ok(())
459    }
460    fn set_from_str_args(&mut self, args: FromStrArgs) {
461        if let Some(regex) = args.regex {
462            self.regex = Some(regex);
463        }
464        self.regex_infer |= args.regex_infer.value();
465        if let Some(with) = args.with {
466            self.with = Some(with);
467        }
468        if let Some(new) = args.new {
469            self.new_expr = Some(new);
470        }
471        if let Some(bound) = args.bound {
472            let list = self.bound_from_str.get_or_insert(Vec::new());
473            for bound in bound {
474                for bound in bound.into_iter() {
475                    list.push(bound);
476                }
477            }
478        }
479        if let Some(span) = args.default.span {
480            self.default_self = Some(span);
481        }
482        if let Some(fields) = args.default_fields {
483            for field in fields {
484                for field in field.into_iter() {
485                    self.default_fields.push(field);
486                }
487            }
488        }
489        if args.ignore.value() {
490            self.ignore = args.ignore;
491        }
492        self.dump_from_str |= args.dump;
493    }
494    fn push_from_str_warning_spans(&mut self, attr: &Attribute) -> Result<()> {
495        attr.parse_nested_meta(|meta| {
496            if meta.path.is_ident("default_fields") {
497                self.deprecated_default_fields_warning_spans
498                    .push(meta.path.span());
499            }
500            if meta.input.peek(Token![=]) {
501                meta.value()?.parse::<TokenStream>()?;
502            } else if meta.input.peek(syn::token::Paren) {
503                let content;
504                syn::parenthesized!(content in meta.input);
505                content.parse::<TokenStream>()?;
506            }
507            Ok(())
508        })
509    }
510    fn deprecated_default_fields_warnings(&self) -> TokenStream {
511        deprecated_default_fields_warnings(
512            &self.crate_path,
513            &self.deprecated_default_fields_warning_spans,
514        )
515    }
516    fn span_of_from_str_format(&self) -> Option<Span> {
517        if let Some(lit) = &self.regex {
518            return Some(lit.span());
519        }
520        if let Some(format) = &self.format {
521            return Some(format.span);
522        }
523        None
524    }
525    fn bound_from_str_resolved(&self) -> Option<Vec<Bound>> {
526        self.bound_from_str
527            .clone()
528            .or_else(|| self.bound_display.clone())
529    }
530}
531
532fn deprecated_default_fields_warnings(crate_path: &Path, spans: &[Span]) -> TokenStream {
533    let warnings = spans.iter().map(|span| {
534        set_span(
535            quote_spanned! { *span=>
536                #crate_path::helpers::from_str_default_fields();
537            },
538            *span,
539        )
540    });
541    quote! { #(#warnings)* }
542}
543
544#[derive(Copy, Clone)]
545enum DisplayStyle {
546    None,
547    LowerCase,
548    UpperCase,
549    LowerSnakeCase,
550    UpperSnakeCase,
551    LowerCamelCase,
552    UpperCamelCase,
553    LowerKebabCase,
554    UpperKebabCase,
555    TitleCase,
556    TitleCaseHead,
557    TitleCaseLower,
558    TitleCaseUpper,
559}
560
561impl DisplayStyle {
562    fn parse_lit_str(s: &LitStr) -> Result<Self> {
563        const ERROR_MESSAGE: &str = "Invalid display style. \
564        The following values are available: \
565        \"none\", \
566        \"lowercase\", \
567        \"UPPERCASE\", \
568        \"snake_case\", \
569        \"SNAKE_CASE\", \
570        \"camelCase\", \
571        \"CamelCase\", \
572        \"kebab-case\", \
573        \"KEBAB-CASE\", \
574        \"Title Case\", \
575        \"Title case\", \
576        \"title case\", \
577        \"TITLE CASE\"";
578        match Self::parse(&s.value()) {
579            Err(_) => bail!(s.span(), "{ERROR_MESSAGE}"),
580            Ok(value) => Ok(value),
581        }
582    }
583    fn parse(s: &str) -> std::result::Result<Self, ParseDisplayStyleError> {
584        use DisplayStyle::*;
585        Ok(match s {
586            "none" => None,
587            "lowercase" => LowerCase,
588            "UPPERCASE" => UpperCase,
589            "snake_case" => LowerSnakeCase,
590            "SNAKE_CASE" => UpperSnakeCase,
591            "camelCase" => LowerCamelCase,
592            "CamelCase" => UpperCamelCase,
593            "kebab-case" => LowerKebabCase,
594            "KEBAB-CASE" => UpperKebabCase,
595            "Title Case" => TitleCase,
596            "Title case" => TitleCaseHead,
597            "title case" => TitleCaseLower,
598            "TITLE CASE" => TitleCaseUpper,
599            _ => return Err(ParseDisplayStyleError),
600        })
601    }
602    fn from_helper_attributes(
603        hattrs_enum: &HelperAttributes,
604        hattrs_variant: &HelperAttributes,
605    ) -> Self {
606        hattrs_variant
607            .style
608            .or(hattrs_enum.style)
609            .unwrap_or(DisplayStyle::None)
610    }
611    fn apply(self, ident: &Ident) -> String {
612        fn convert_case(c: char, to_upper: bool) -> char {
613            if to_upper {
614                c.to_ascii_uppercase()
615            } else {
616                c.to_ascii_lowercase()
617            }
618        }
619
620        let s = ident.to_string();
621        let (line_head, word_head, normal, sep) = match self {
622            DisplayStyle::None => {
623                return s;
624            }
625            DisplayStyle::LowerCase => (false, false, false, ""),
626            DisplayStyle::UpperCase => (true, true, true, ""),
627            DisplayStyle::LowerSnakeCase => (false, false, false, "_"),
628            DisplayStyle::UpperSnakeCase => (true, true, true, "_"),
629            DisplayStyle::LowerCamelCase => (false, true, false, ""),
630            DisplayStyle::UpperCamelCase => (true, true, false, ""),
631            DisplayStyle::LowerKebabCase => (false, false, false, "-"),
632            DisplayStyle::UpperKebabCase => (true, true, true, "-"),
633            DisplayStyle::TitleCase => (true, true, false, " "),
634            DisplayStyle::TitleCaseUpper => (true, true, true, " "),
635            DisplayStyle::TitleCaseLower => (false, false, false, " "),
636            DisplayStyle::TitleCaseHead => (true, false, false, " "),
637        };
638        let mut is_line_head = true;
639        let mut is_word_head = true;
640        let mut last = '\0';
641
642        let mut r = String::new();
643        for c in s.chars() {
644            if !c.is_alphanumeric() && !c.is_ascii_digit() {
645                is_word_head = true;
646                continue;
647            }
648            is_word_head = is_word_head || (!last.is_ascii_uppercase() && c.is_ascii_uppercase());
649            last = c;
650            let (to_upper, sep) = match (is_line_head, is_word_head) {
651                (true, _) => (line_head, ""),
652                (false, true) => (word_head, sep),
653                (false, false) => (normal, ""),
654            };
655            r.push_str(sep);
656            r.push(convert_case(c, to_upper));
657            is_word_head = false;
658            is_line_head = false;
659        }
660        r
661    }
662}
663
664#[derive(Clone)]
665struct DisplayFormat {
666    parts: Vec<DisplayFormatPart>,
667    span: Span,
668}
669impl DisplayFormat {
670    fn parse_lit_str(s: &LitStr) -> Result<DisplayFormat> {
671        Self::parse(&s.value(), s.span())
672    }
673    fn parse(mut s: &str, span: Span) -> Result<DisplayFormat> {
674        let regex_str = regex!(r"^[^{}]+");
675        let regex_var = regex!(r"^\{([^:{}]*)(?::([^}]*))?\}");
676        let mut parts = Vec::new();
677        while !s.is_empty() {
678            if s.starts_with("{{") {
679                parts.push(DisplayFormatPart::EscapedBeginBracket);
680                s = &s[2..];
681                continue;
682            }
683            if s.starts_with("}}") {
684                parts.push(DisplayFormatPart::EscapedEndBracket);
685                s = &s[2..];
686                continue;
687            }
688            if let Some(m) = regex_str.find(s) {
689                parts.push(DisplayFormatPart::Str(m.as_str().into()));
690                s = &s[m.end()..];
691                continue;
692            }
693            if let Some(c) = regex_var.captures(s) {
694                let arg = c.get(1).unwrap().as_str().into();
695                let format_spec = c.get(2).map_or("", |x| x.as_str()).into();
696                parts.push(DisplayFormatPart::Var { arg, format_spec });
697                s = &s[c.get(0).unwrap().end()..];
698                continue;
699            }
700            bail!(span, "invalid display format.");
701        }
702        Ok(Self { parts, span })
703    }
704    fn from_newtype_struct(data: &DataStruct) -> Option<Self> {
705        let p = DisplayFormatPart::Var {
706            arg: get_newtype_field(data)?,
707            format_spec: String::new(),
708        };
709        Some(Self {
710            parts: vec![p],
711            span: Span::call_site(),
712        })
713    }
714    fn from_unit_variant(variant: &Variant) -> Result<Option<Self>> {
715        Ok(if let Fields::Unit = &variant.fields {
716            Some(Self::parse("{}", variant.span())?)
717        } else {
718            None
719        })
720    }
721
722    fn format_args(
723        &self,
724        vb: &VarBase,
725        with: &Option<Expr>,
726        bounds: &mut Bounds,
727        cx: &CodeContext,
728    ) -> Result<FormatArgs> {
729        let mut format_str = String::new();
730        let mut format_args = Vec::new();
731        for p in &self.parts {
732            use DisplayFormatPart::*;
733            match p {
734                Str(s) => format_str.push_str(s.as_str()),
735                EscapedBeginBracket => format_str.push_str("{{"),
736                EscapedEndBracket => format_str.push_str("}}"),
737                Var { arg, format_spec } => {
738                    format_str.push('{');
739                    if !format_spec.is_empty() {
740                        format_str.push(':');
741                        format_str.push_str(format_spec);
742                    }
743                    format_str.push('}');
744                    let format_spec = FormatSpec::parse_with_span(format_spec, self.span)?;
745                    let format_arg =
746                        vb.format_arg(arg, &format_spec, self.span, with, bounds, cx)?;
747                    let mut expr = quote!(&#format_arg);
748                    if format_spec.format_type == FormatType::Pointer {
749                        let crate_path = &cx.crate_path;
750                        expr = quote!(#crate_path::helpers::fmt_pointer(#expr));
751                    }
752                    expr = set_span(expr, self.span);
753                    format_args.push(expr);
754                }
755            }
756        }
757        Ok(FormatArgs {
758            format_str,
759            format_args,
760            span: self.span,
761        })
762    }
763
764    fn try_unescape(&self) -> Option<String> {
765        let mut s = String::new();
766        for p in &self.parts {
767            s.push_str(p.try_unescape()?);
768        }
769        Some(s)
770    }
771}
772
773struct FormatArgs {
774    format_str: String,
775    format_args: Vec<TokenStream>,
776    span: Span,
777}
778impl FormatArgs {
779    fn build_write(&self, f: TokenStream) -> Result<TokenStream> {
780        if self.format_args.is_empty() {
781            if let Some(s) = DisplayFormat::parse(&self.format_str, self.span)?.try_unescape() {
782                return Ok(quote! { #f.write_str(#s) });
783            }
784        }
785        Ok(quote! { ::core::write!(#f, #self) })
786    }
787}
788impl ToTokens for FormatArgs {
789    fn to_tokens(&self, tokens: &mut TokenStream) {
790        let format_str = LitStr::new(&self.format_str, self.span);
791        let format_args = &self.format_args;
792        tokens.extend(quote!(#format_str #(,#format_args)*));
793    }
794}
795
796#[derive(Clone)]
797enum DisplayFormatPart {
798    Str(String),
799    EscapedBeginBracket,
800    EscapedEndBracket,
801    Var { arg: String, format_spec: String },
802}
803impl DisplayFormatPart {
804    fn try_unescape(&self) -> Option<&str> {
805        match self {
806            Self::Str(value) => Some(value),
807            Self::EscapedBeginBracket => Some("{"),
808            Self::EscapedEndBracket => Some("}"),
809            Self::Var { .. } => None,
810        }
811    }
812}
813
814struct CodeContext<'a> {
815    generics: &'a GenericParamSet,
816    crate_path: &'a Path,
817}
818
819enum VarBase<'a> {
820    Struct {
821        data: &'a DataStruct,
822    },
823    Variant {
824        variant: &'a Variant,
825        style: DisplayStyle,
826    },
827    Field {
828        parent: &'a VarBase<'a>,
829        field: &'a Field,
830        key: &'a FieldKey,
831    },
832    FieldSome {
833        key: &'a FieldKey,
834        ty: &'a Type,
835    },
836}
837
838impl VarBase<'_> {
839    fn format_arg(
840        &self,
841        arg: &str,
842        format_spec: &FormatSpec,
843        span: Span,
844        with: &Option<Expr>,
845        bounds: &mut Bounds,
846        cx: &CodeContext,
847    ) -> Result<TokenStream> {
848        let keys = FieldKey::from_str_deep(arg);
849        if keys.is_empty() {
850            if matches!(self, VarBase::Struct { .. } | VarBase::Variant { .. })
851                && format_spec.format_type != FormatType::Display
852            {
853                return Ok(quote!(self));
854            }
855            return Ok(match self {
856                VarBase::Struct { .. } => {
857                    bail!(span, "{{}} is not allowed in struct format.")
858                }
859                VarBase::Field { parent, field, key } => format_arg(
860                    parent.field_expr(key),
861                    &field.ty,
862                    format_spec,
863                    span,
864                    with,
865                    bounds,
866                    cx,
867                )?,
868                VarBase::FieldSome { key, ty, .. } => {
869                    let ident = key.binding_var();
870                    format_arg(quote!(*#ident), ty, format_spec, span, with, bounds, cx)?
871                }
872                VarBase::Variant { variant, style, .. } => {
873                    let s = style.apply(&variant.ident);
874                    quote! { #s }
875                }
876            });
877        }
878
879        if keys.len() == 1 {
880            if let Some(fields) = self.fields() {
881                let key = &keys[0];
882                let m = field_map(fields);
883                let Some(field) = m.get(key) else {
884                    bail!(span, "unknown field '{key}'.");
885                };
886                return self.format_arg_of_field(key, field, format_spec, span, bounds, cx);
887            }
888        }
889        let mut expr = self.field_expr(&keys[0]);
890        for key in &keys[1..] {
891            expr.extend(quote! { .#key });
892        }
893        Ok(expr)
894    }
895    fn format_arg_of_field(
896        &self,
897        key: &FieldKey,
898        field: &Field,
899        format_spec: &FormatSpec,
900        span: Span,
901        bounds: &mut Bounds,
902        cx: &CodeContext,
903    ) -> Result<TokenStream> {
904        let hattrs = HelperAttributes::from(&field.attrs, false)?;
905        let mut bounds = bounds.child(hattrs.bound_display);
906        let vb = VarBase::Field {
907            parent: self,
908            field,
909            key,
910        };
911        if let Some(opt_span) = hattrs.opt.span {
912            let Some(inner_ty) = get_option_element(&field.ty) else {
913                bail!(
914                    opt_span,
915                    "`#[display(opt)]` is only allowed for `Option<T>`."
916                );
917            };
918            let crate_path = cx.crate_path;
919            let in_expr = self.field_expr(key);
920            let formatter_ident = Ident::new("_formatter", Span::call_site());
921            let vb = VarBase::FieldSome { key, ty: inner_ty };
922            let out_expr = vb.format_arg_from_some_format(
923                hattrs.format,
924                format_spec,
925                span,
926                &hattrs.with,
927                &mut bounds,
928                cx,
929            )?;
930            let ident = key.binding_var();
931            Ok(quote! {
932                (
933                    #crate_path::helpers::OptionFormatHelper::<#inner_ty, _> {
934                        value: &#in_expr,
935                        f: |#ident : &#inner_ty, #formatter_ident : &mut ::core::fmt::Formatter| {
936                             ::core::write!(#formatter_ident, "{}", #out_expr)
937                        },
938                        none_value: "",
939                    }
940                )
941            })
942        } else {
943            vb.format_arg_from_some_format(
944                hattrs.format,
945                format_spec,
946                span,
947                &hattrs.with,
948                &mut bounds,
949                cx,
950            )
951        }
952    }
953    fn format_arg_from_some_format(
954        &self,
955        format: Option<DisplayFormat>,
956        format_spec: &FormatSpec,
957        span: Span,
958        with: &Option<Expr>,
959        bounds: &mut Bounds,
960        cx: &CodeContext,
961    ) -> Result<TokenStream> {
962        if let Some(format) = format {
963            let args = format.format_args(self, with, bounds, cx)?;
964            Ok(quote! { format_args!(#args) })
965        } else {
966            self.format_arg("", format_spec, span, with, bounds, cx)
967        }
968    }
969
970    fn field_expr(&self, key: &FieldKey) -> TokenStream {
971        match self {
972            VarBase::Struct { .. } => quote! { self.#key },
973            VarBase::Variant { .. } => {
974                let var = key.binding_var();
975                quote! { (*#var) }
976            }
977            VarBase::Field {
978                parent,
979                key: parent_key,
980                ..
981            } => {
982                let expr = parent.field_expr(parent_key);
983                quote! { #expr.#key }
984            }
985            VarBase::FieldSome { key: key_base, .. } => {
986                let ident = key_base.binding_var();
987                quote! { #ident.#key }
988            }
989        }
990    }
991
992    fn default_from_str_format(&self) -> Result<DisplayFormat> {
993        const ERROR_MESSAGE_FOR_STRUCT: &str = "`#[display(\"format\")]` or `#[from_str(regex = \"regex\")]` is required except newtype pattern.";
994        const ERROR_MESSAGE_FOR_VARIANT: &str = "`#[display(\"format\")]` or `#[from_str(regex = \"regex\")]` is required except unit variant.";
995        Ok(match self {
996            VarBase::Struct { data, .. } => {
997                DisplayFormat::from_newtype_struct(data).expect(ERROR_MESSAGE_FOR_STRUCT)
998            }
999            VarBase::Variant { variant, .. } => {
1000                DisplayFormat::from_unit_variant(variant)?.expect(ERROR_MESSAGE_FOR_VARIANT)
1001            }
1002            VarBase::Field { field, .. } => DisplayFormat::parse("{}", field.span())?,
1003            VarBase::FieldSome { ty, .. } => DisplayFormat::parse("{}", ty.span())?,
1004        })
1005    }
1006    fn fields(&self) -> Option<&Fields> {
1007        match self {
1008            VarBase::Struct { data, .. } => Some(&data.fields),
1009            VarBase::Variant { variant, .. } => Some(&variant.fields),
1010            VarBase::Field { .. } => None,
1011            VarBase::FieldSome { .. } => None,
1012        }
1013    }
1014}
1015#[allow(clippy::too_many_arguments)]
1016fn format_arg(
1017    mut expr: TokenStream,
1018    ty: &Type,
1019    format_spec: &FormatSpec,
1020    span: Span,
1021    with: &Option<Expr>,
1022    bounds: &mut Bounds,
1023    cx: &CodeContext,
1024) -> Result<TokenStream> {
1025    if with.is_none() && cx.generics.contains_in_type(ty) {
1026        let tr = format_spec.format_type.trait_name();
1027        let tr: Ident = parse_str(tr).unwrap();
1028        if bounds.can_extend {
1029            bounds.pred.push(parse_quote!(#ty : ::core::fmt::#tr));
1030        }
1031    }
1032    if let Some(with) = with {
1033        if format_spec.format_type != FormatType::Display {
1034            bail!(
1035                span,
1036                "Since `with = ...` is specified, the `{}` format cannot be used.",
1037                format_spec.format_type
1038            );
1039        }
1040        let crate_path = cx.crate_path;
1041        let unref_ty = unref_ty(ty);
1042        expr = quote! {
1043            #crate_path::helpers::Formatted::<'_, #unref_ty, _> {
1044                value : &#expr,
1045                format : #with,
1046            }
1047        };
1048    }
1049    Ok(expr)
1050}
1051
1052#[derive(Debug)]
1053struct ParseDisplayStyleError;
1054impl std::error::Error for ParseDisplayStyleError {}
1055
1056impl Display for ParseDisplayStyleError {
1057    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1058        write!(f, "invalid display style")
1059    }
1060}
1061
1062#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
1063enum FieldKey {
1064    Named(String),
1065    Unnamed(usize),
1066}
1067
1068impl FieldKey {
1069    fn from_str(s: &str) -> FieldKey {
1070        if let Ok(idx) = s.parse() {
1071            FieldKey::Unnamed(idx)
1072        } else {
1073            FieldKey::Named(trim_raw(s).to_string())
1074        }
1075    }
1076    fn from_string(mut s: String) -> FieldKey {
1077        if let Ok(idx) = s.parse() {
1078            FieldKey::Unnamed(idx)
1079        } else {
1080            if s.starts_with("r#") {
1081                s.drain(0..2);
1082            }
1083            FieldKey::Named(s)
1084        }
1085    }
1086    fn from_ident(ident: &Ident) -> FieldKey {
1087        Self::from_string(ident.to_string())
1088    }
1089    fn from_str_deep(s: &str) -> Vec<FieldKey> {
1090        if s.is_empty() {
1091            Vec::new()
1092        } else {
1093            s.split('.').map(Self::from_str).collect()
1094        }
1095    }
1096    fn from_fields_named(fields: &FieldsNamed) -> impl Iterator<Item = (FieldKey, &Field)> {
1097        fields
1098            .named
1099            .iter()
1100            .map(|field| (Self::from_ident(field.ident.as_ref().unwrap()), field))
1101    }
1102    fn from_fields_unnamed(fields: &FieldsUnnamed) -> impl Iterator<Item = (FieldKey, &Field)> {
1103        fields
1104            .unnamed
1105            .iter()
1106            .enumerate()
1107            .map(|(idx, field)| (FieldKey::Unnamed(idx), field))
1108    }
1109    fn from_member(member: &Member) -> Self {
1110        match member {
1111            Member::Named(ident) => Self::from_ident(ident),
1112            Member::Unnamed(index) => Self::Unnamed(index.index as usize),
1113        }
1114    }
1115    fn from_field(index: usize, field: &Field) -> Self {
1116        if let Some(ident) = &field.ident {
1117            Self::from_ident(ident)
1118        } else {
1119            Self::Unnamed(index)
1120        }
1121    }
1122
1123    fn to_member(&self) -> Member {
1124        match self {
1125            FieldKey::Named(s) => Member::Named(format_ident!("r#{s}")),
1126            FieldKey::Unnamed(idx) => Member::Unnamed(parse_str(&format!("{idx}")).unwrap()),
1127        }
1128    }
1129    fn binding_var(&self) -> Ident {
1130        parse_str(&format!("_value_{self}")).unwrap()
1131    }
1132    fn new_arg_var(&self) -> Ident {
1133        match self {
1134            Self::Named(s) => parse_str(s),
1135            Self::Unnamed(idx) => parse_str(&format!("_{idx}")),
1136        }
1137        .unwrap()
1138    }
1139}
1140impl std::fmt::Display for FieldKey {
1141    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1142        match self {
1143            FieldKey::Named(s) => write!(f, "{s}"),
1144            FieldKey::Unnamed(idx) => write!(f, "{idx}"),
1145        }
1146    }
1147}
1148impl ToTokens for FieldKey {
1149    fn to_tokens(&self, tokens: &mut TokenStream) {
1150        self.to_member().to_tokens(tokens);
1151    }
1152}
1153
1154fn field_map(fields: &Fields) -> BTreeMap<FieldKey, &Field> {
1155    let mut m = BTreeMap::new();
1156    for (idx, field) in fields.iter().enumerate() {
1157        let key = if let Some(ident) = &field.ident {
1158            FieldKey::from_ident(ident)
1159        } else {
1160            FieldKey::Unnamed(idx)
1161        };
1162        m.insert(key, field);
1163    }
1164    m
1165}
1166
1167fn join<T: std::fmt::Display>(s: impl IntoIterator<Item = T>, sep: &str) -> String {
1168    use std::fmt::Write as _;
1169    let mut sep_current = "";
1170    let mut buf = String::new();
1171    for i in s {
1172        write!(&mut buf, "{sep_current}{i}").unwrap();
1173        sep_current = sep;
1174    }
1175    buf
1176}
1177fn trim_raw(s: &str) -> &str {
1178    if let Some(s) = s.strip_prefix("r#") {
1179        s
1180    } else {
1181        s
1182    }
1183}
1184
1185fn set_span(ts: TokenStream, span: Span) -> TokenStream {
1186    ts.into_iter()
1187        .map(|mut ts| {
1188            ts.set_span(span);
1189            ts
1190        })
1191        .collect()
1192}
1193
1194fn unref_ty(ty: &Type) -> Type {
1195    if let Type::Reference(ty) = ty {
1196        unref_ty(&ty.elem)
1197    } else {
1198        ty.clone()
1199    }
1200}
1201
1202fn escape_fmt(s: &str) -> String {
1203    let mut out = String::new();
1204    for c in s.chars() {
1205        match c {
1206            '{' => out.push_str("{{"),
1207            '}' => out.push_str("}}"),
1208            c => out.push(c),
1209        }
1210    }
1211    out
1212}
1213
1214fn get_option_element(ty: &Type) -> Option<&Type> {
1215    get_element(ty, &[&["std", "option"], &["core", "option"]], "Option")
1216}
1217fn get_element<'a>(ty: &'a Type, ns: &[&[&str]], name: &str) -> Option<&'a Type> {
1218    if let PathArguments::AngleBracketed(args) = get_arguments_of(ty, ns, name)? {
1219        if args.args.len() == 1 {
1220            if let GenericArgument::Type(ty) = &args.args[0] {
1221                return Some(ty);
1222            }
1223        }
1224    }
1225    None
1226}