1#![recursion_limit = "128"]
2#![allow(clippy::large_enum_variant)]
3
4extern 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}