1#![recursion_limit = "128"] extern crate proc_macro;
4
5use crate::parse::attributes_from_syn;
6use proc_macro::TokenStream;
7use quote::quote;
8use std::collections::{BTreeSet, VecDeque};
9use std::fmt;
10
11mod parse;
12mod shared;
13
14#[proc_macro_derive(Snafu, attributes(snafu))]
16pub fn snafu_derive(input: TokenStream) -> TokenStream {
17 let ast = syn::parse(input).expect("Could not parse type to derive Error for");
18
19 impl_snafu_macro(ast)
20}
21
22mod report;
23#[proc_macro_attribute]
24pub fn report(attr: TokenStream, item: TokenStream) -> TokenStream {
25 report::body(attr, item)
26 .unwrap_or_else(|e| e.to_compile_error())
27 .into()
28}
29
30type MultiSynResult<T> = std::result::Result<T, Vec<syn::Error>>;
31
32type UserInput = Box<dyn quote::ToTokens>;
34
35enum ModuleName {
36 Default,
37 Custom(syn::Ident),
38}
39
40enum SnafuInfo {
41 Enum(EnumInfo),
42 NamedStruct(NamedStructInfo),
43 TupleStruct(TupleStructInfo),
44}
45
46struct EnumInfo {
47 crate_root: UserInput,
48 name: syn::Ident,
49 generics: syn::Generics,
50 variants: Vec<FieldContainer>,
51 default_visibility: Option<UserInput>,
52 default_suffix: SuffixKind,
53 module: Option<ModuleName>,
54}
55
56struct FieldContainer {
57 name: syn::Ident,
58 backtrace_field: Option<Field>,
59 implicit_fields: Vec<Field>,
60 selector_kind: ContextSelectorKind,
61 display_format: Option<Display>,
62 doc_comment: Option<DocComment>,
63 visibility: Option<UserInput>,
64 module: Option<ModuleName>,
65 provides: Vec<Provide>,
66}
67
68impl FieldContainer {
69 fn user_fields(&self) -> &[Field] {
70 self.selector_kind.user_fields()
71 }
72
73 fn provides(&self) -> &[Provide] {
74 &self.provides
75 }
76}
77
78struct Provide {
79 is_chain: bool,
80 is_opt: bool,
81 is_priority: bool,
82 is_ref: bool,
83 ty: syn::Type,
84 expr: syn::Expr,
85}
86
87enum SuffixKind {
88 Default,
89 None,
90 Some(syn::Ident),
91}
92
93impl SuffixKind {
94 fn resolve_with_default<'a>(&'a self, def: &'a Self) -> &'a Self {
95 use SuffixKind::*;
96
97 match self {
98 Default => def,
99 None => self,
100 Some(_) => self,
101 }
102 }
103}
104
105enum ContextSelectorKind {
106 Context {
107 suffix: SuffixKind,
108 source_field: Option<SourceField>,
109 user_fields: Vec<Field>,
110 },
111
112 Whatever {
113 source_field: Option<SourceField>,
114 message_field: Field,
115 },
116
117 NoContext {
118 source_field: SourceField,
119 },
120}
121
122impl ContextSelectorKind {
123 fn is_whatever(&self) -> bool {
124 match self {
125 ContextSelectorKind::Whatever { .. } => true,
126 _ => false,
127 }
128 }
129
130 fn user_fields(&self) -> &[Field] {
131 match self {
132 ContextSelectorKind::Context { user_fields, .. } => user_fields,
133 ContextSelectorKind::Whatever { .. } => &[],
134 ContextSelectorKind::NoContext { .. } => &[],
135 }
136 }
137
138 fn source_field(&self) -> Option<&SourceField> {
139 match self {
140 ContextSelectorKind::Context { source_field, .. } => source_field.as_ref(),
141 ContextSelectorKind::Whatever { source_field, .. } => source_field.as_ref(),
142 ContextSelectorKind::NoContext { source_field } => Some(source_field),
143 }
144 }
145
146 fn message_field(&self) -> Option<&Field> {
147 match self {
148 ContextSelectorKind::Context { .. } => None,
149 ContextSelectorKind::Whatever { message_field, .. } => Some(message_field),
150 ContextSelectorKind::NoContext { .. } => None,
151 }
152 }
153}
154
155struct NamedStructInfo {
156 crate_root: UserInput,
157 field_container: FieldContainer,
158 generics: syn::Generics,
159}
160
161struct TupleStructInfo {
162 crate_root: UserInput,
163 name: syn::Ident,
164 generics: syn::Generics,
165 transformation: Transformation,
166 provides: Vec<Provide>,
167}
168
169#[derive(Clone)]
170pub(crate) struct Field {
171 name: syn::Ident,
172 ty: syn::Type,
173 provide: bool,
174 original: syn::Field,
175}
176
177impl Field {
178 fn name(&self) -> &syn::Ident {
179 &self.name
180 }
181}
182
183struct SourceField {
184 name: syn::Ident,
185 transformation: Transformation,
186 backtrace_delegate: bool,
187 provide: bool,
188}
189
190impl SourceField {
191 fn name(&self) -> &syn::Ident {
192 &self.name
193 }
194}
195
196enum Transformation {
197 None {
198 ty: syn::Type,
199 },
200 Transform {
201 source_ty: syn::Type,
202 target_ty: syn::Type,
203 expr: syn::Expr,
204 },
205}
206
207impl Transformation {
208 fn source_ty(&self) -> &syn::Type {
209 match self {
210 Transformation::None { ty } => ty,
211 Transformation::Transform { source_ty, .. } => source_ty,
212 }
213 }
214
215 fn target_ty(&self) -> &syn::Type {
216 match self {
217 Transformation::None { ty } => ty,
218 Transformation::Transform { target_ty, .. } => target_ty,
219 }
220 }
221
222 fn transformation(&self) -> proc_macro2::TokenStream {
223 match self {
224 Transformation::None { .. } => quote! { |v| v },
225 Transformation::Transform { expr, .. } => quote! { #expr },
226 }
227 }
228}
229
230enum ProvideKind {
231 Flag(bool),
232 Expression(Provide),
233}
234
235#[derive(Debug, Default)]
239struct SyntaxErrors {
240 inner: Vec<syn::Error>,
241}
242
243impl SyntaxErrors {
244 fn scoped(&mut self, scope: ErrorLocation) -> SyntaxErrorsScoped<'_> {
246 SyntaxErrorsScoped {
247 errors: self,
248 scope,
249 }
250 }
251
252 fn add(&mut self, tokens: impl quote::ToTokens, description: impl fmt::Display) {
255 self.inner
256 .push(syn::Error::new_spanned(tokens, description));
257 }
258
259 fn extend(&mut self, errors: impl IntoIterator<Item = syn::Error>) {
261 self.inner.extend(errors);
262 }
263
264 #[allow(dead_code)]
265 fn len(&self) -> usize {
267 self.inner.len()
268 }
269
270 fn finish(self) -> MultiSynResult<()> {
273 if self.inner.is_empty() {
274 Ok(())
275 } else {
276 Err(self.inner)
277 }
278 }
279
280 fn absorb<T>(mut self, res: MultiSynResult<T>) -> MultiSynResult<T> {
283 match res {
284 Ok(v) => self.finish().map(|()| v),
285 Err(e) => {
286 self.inner.extend(e);
287 Err(self.inner)
288 }
289 }
290 }
291}
292
293#[derive(Debug, Copy, Clone)]
294enum ErrorLocation {
295 OnEnum,
296 OnVariant,
297 InVariant,
298 OnField,
299 OnNamedStruct,
300 InNamedStruct,
301 OnTupleStruct,
302}
303
304impl fmt::Display for ErrorLocation {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 use crate::ErrorLocation::*;
307
308 match self {
309 OnEnum => "on an enum".fmt(f),
310 OnVariant => "on an enum variant".fmt(f),
311 InVariant => "within an enum variant".fmt(f),
312 OnField => "on a field".fmt(f),
313 OnNamedStruct => "on a named struct".fmt(f),
314 InNamedStruct => "within a named struct".fmt(f),
315 OnTupleStruct => "on a tuple struct".fmt(f),
316 }
317 }
318}
319
320trait ErrorForLocation {
321 fn for_location(&self, location: ErrorLocation) -> String;
322}
323
324struct SyntaxErrorsScoped<'a> {
325 errors: &'a mut SyntaxErrors,
326 scope: ErrorLocation,
327}
328
329impl SyntaxErrorsScoped<'_> {
330 fn add(&mut self, tokens: impl quote::ToTokens, description: impl ErrorForLocation) {
333 let description = description.for_location(self.scope);
334 self.errors.add(tokens, description)
335 }
336}
337
338#[derive(Debug)]
341struct OnlyValidOn {
342 attribute: &'static str,
344 valid_on: &'static str,
346}
347
348impl ErrorForLocation for OnlyValidOn {
349 fn for_location(&self, location: ErrorLocation) -> String {
350 format!(
351 "`{}` attribute is only valid on {}, not {}",
352 self.attribute, self.valid_on, location,
353 )
354 }
355}
356
357#[derive(Debug)]
360struct WrongField {
361 attribute: &'static str,
363 valid_field: &'static str,
365}
366
367impl ErrorForLocation for WrongField {
368 fn for_location(&self, _location: ErrorLocation) -> String {
369 format!(
370 r#"`{}` attribute is only valid on a field named "{}", not on other fields"#,
371 self.attribute, self.valid_field,
372 )
373 }
374}
375
376#[derive(Debug)]
379struct IncompatibleAttributes(&'static [&'static str]);
380
381impl ErrorForLocation for IncompatibleAttributes {
382 fn for_location(&self, location: ErrorLocation) -> String {
383 let attrs_string = self
384 .0
385 .iter()
386 .map(|attr| format!("`{}`", attr))
387 .collect::<Vec<_>>()
388 .join(", ");
389 format!(
390 "Incompatible attributes [{}] specified {}",
391 attrs_string, location,
392 )
393 }
394}
395
396#[derive(Debug)]
399struct DuplicateAttribute {
400 attribute: &'static str,
401 location: ErrorLocation,
402}
403
404impl fmt::Display for DuplicateAttribute {
405 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
406 write!(
407 f,
408 "Multiple `{}` attributes are not supported {}",
409 self.attribute, self.location,
410 )
411 }
412}
413
414#[derive(Debug)]
422struct AtMostOne<T, U>
423where
424 U: quote::ToTokens,
425{
426 name: &'static str,
427 location: ErrorLocation,
428 values: VecDeque<(T, U)>,
431 errors: SyntaxErrors,
432}
433
434impl<T, U> AtMostOne<T, U>
435where
436 U: quote::ToTokens + Clone,
437{
438 fn new(name: &'static str, location: ErrorLocation) -> Self {
442 Self {
443 name,
444 location,
445 values: VecDeque::new(),
446 errors: SyntaxErrors::default(),
447 }
448 }
449
450 fn add(&mut self, item: T, tokens: U) {
452 if !self.values.is_empty() {
453 self.errors.add(
454 tokens.clone(),
455 DuplicateAttribute {
456 attribute: self.name,
457 location: self.location,
458 },
459 );
460 }
461 self.values.push_back((item, tokens));
462 }
463
464 #[allow(dead_code)]
465 fn len(&self) -> usize {
467 self.values.len()
468 }
469
470 #[allow(dead_code)]
472 fn is_empty(&self) -> bool {
473 self.values.is_empty()
474 }
475
476 fn iter(&self) -> std::collections::vec_deque::Iter<(T, U)> {
481 self.values.iter()
482 }
483
484 fn finish(self) -> (Option<T>, Vec<syn::Error>) {
487 let (value, errors) = self.finish_with_location();
488 (value.map(|(val, _location)| val), errors)
489 }
490
491 fn finish_with_location(mut self) -> (Option<(T, U)>, Vec<syn::Error>) {
495 let errors = match self.errors.finish() {
496 Ok(()) => Vec::new(),
497 Err(vec) => vec,
498 };
499 (self.values.pop_front(), errors)
500 }
501}
502
503fn impl_snafu_macro(ty: syn::DeriveInput) -> TokenStream {
504 match parse_snafu_information(ty) {
505 Ok(info) => info.into(),
506 Err(e) => to_compile_errors(e).into(),
507 }
508}
509
510fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
511 let compile_errors = errors.iter().map(syn::Error::to_compile_error);
512 quote! { #(#compile_errors)* }
513}
514
515fn parse_snafu_information(ty: syn::DeriveInput) -> MultiSynResult<SnafuInfo> {
516 use syn::spanned::Spanned;
517 use syn::Data;
518
519 let span = ty.span();
520 let syn::DeriveInput {
521 ident,
522 generics,
523 data,
524 attrs,
525 ..
526 } = ty;
527
528 match data {
529 Data::Enum(enum_) => parse_snafu_enum(enum_, ident, generics, attrs).map(SnafuInfo::Enum),
530 Data::Struct(struct_) => parse_snafu_struct(struct_, ident, generics, attrs, span),
531 _ => Err(vec![syn::Error::new(
532 span,
533 "Can only derive `Snafu` for an enum or a newtype",
534 )]),
535 }
536}
537
538const ATTR_DISPLAY: OnlyValidOn = OnlyValidOn {
539 attribute: "display",
540 valid_on: "enum variants or structs with named fields",
541};
542
543const ATTR_SOURCE: OnlyValidOn = OnlyValidOn {
544 attribute: "source",
545 valid_on: "enum variant or struct fields with a name",
546};
547
548const ATTR_SOURCE_BOOL: OnlyValidOn = OnlyValidOn {
549 attribute: "source(bool)",
550 valid_on: "enum variant or struct fields with a name",
551};
552
553const ATTR_SOURCE_FALSE: WrongField = WrongField {
554 attribute: "source(false)",
555 valid_field: "source",
556};
557
558const ATTR_SOURCE_FROM: OnlyValidOn = OnlyValidOn {
559 attribute: "source(from)",
560 valid_on: "enum variant or struct fields with a name",
561};
562
563const ATTR_BACKTRACE: OnlyValidOn = OnlyValidOn {
564 attribute: "backtrace",
565 valid_on: "enum variant or struct fields with a name",
566};
567
568const ATTR_BACKTRACE_FALSE: WrongField = WrongField {
569 attribute: "backtrace(false)",
570 valid_field: "backtrace",
571};
572
573const ATTR_IMPLICIT: OnlyValidOn = OnlyValidOn {
574 attribute: "implicit",
575 valid_on: "enum variant or struct fields with a name",
576};
577
578const ATTR_IMPLICIT_FALSE: WrongField = WrongField {
579 attribute: "implicit(false)",
580 valid_field: "location",
581};
582
583const ATTR_VISIBILITY: OnlyValidOn = OnlyValidOn {
584 attribute: "visibility",
585 valid_on: "an enum, enum variants, or a struct with named fields",
586};
587
588const ATTR_MODULE: OnlyValidOn = OnlyValidOn {
589 attribute: "module",
590 valid_on: "an enum or structs with named fields",
591};
592
593const ATTR_PROVIDE_FLAG: OnlyValidOn = OnlyValidOn {
594 attribute: "provide",
595 valid_on: "enum variant or struct fields with a name",
596};
597
598const ATTR_PROVIDE_FALSE: WrongField = WrongField {
599 attribute: "provide(false)",
600 valid_field: r#"source" or "backtrace"#,
601};
602
603const ATTR_PROVIDE_EXPRESSION: OnlyValidOn = OnlyValidOn {
604 attribute: "provide(type => expression)",
605 valid_on: "enum variants, structs with named fields, or tuple structs",
606};
607
608const ATTR_CONTEXT: OnlyValidOn = OnlyValidOn {
609 attribute: "context",
610 valid_on: "enum variants or structs with named fields",
611};
612
613const ATTR_CONTEXT_FLAG: OnlyValidOn = OnlyValidOn {
614 attribute: "context(bool)",
615 valid_on: "enum variants or structs with named fields",
616};
617
618const ATTR_WHATEVER: OnlyValidOn = OnlyValidOn {
619 attribute: "whatever",
620 valid_on: "enum variants or structs with named fields",
621};
622
623const ATTR_CRATE_ROOT: OnlyValidOn = OnlyValidOn {
624 attribute: "crate_root",
625 valid_on: "an enum or a struct",
626};
627
628const SOURCE_BOOL_FROM_INCOMPATIBLE: IncompatibleAttributes =
629 IncompatibleAttributes(&["source(false)", "source(from)"]);
630
631fn parse_snafu_enum(
632 enum_: syn::DataEnum,
633 name: syn::Ident,
634 generics: syn::Generics,
635 attrs: Vec<syn::Attribute>,
636) -> MultiSynResult<EnumInfo> {
637 use syn::spanned::Spanned;
638 use syn::Fields;
639
640 let mut errors = SyntaxErrors::default();
641
642 let mut modules = AtMostOne::new("module", ErrorLocation::OnEnum);
643 let mut default_visibilities = AtMostOne::new("visibility", ErrorLocation::OnEnum);
644 let mut default_suffixes = AtMostOne::new("context(suffix)", ErrorLocation::OnEnum);
645 let mut crate_roots = AtMostOne::new("crate_root", ErrorLocation::OnEnum);
646 let mut enum_errors = errors.scoped(ErrorLocation::OnEnum);
647
648 for attr in attributes_from_syn(attrs)? {
649 use SnafuAttribute as Att;
650
651 match attr {
652 Att::Visibility(tokens, v) => default_visibilities.add(v, tokens),
653 Att::Display(tokens, ..) => enum_errors.add(tokens, ATTR_DISPLAY),
654 Att::Source(tokens, ss) => {
655 for s in ss {
656 match s {
657 Source::Flag(..) => enum_errors.add(tokens.clone(), ATTR_SOURCE_BOOL),
658 Source::From(..) => enum_errors.add(tokens.clone(), ATTR_SOURCE_FROM),
659 }
660 }
661 }
662 Att::CrateRoot(tokens, root) => crate_roots.add(root, tokens),
663 Att::Context(tokens, c) => match c {
664 Context::Suffix(s) => default_suffixes.add(s, tokens),
665 Context::Flag(_) => enum_errors.add(tokens, ATTR_CONTEXT_FLAG),
666 },
667 Att::Module(tokens, v) => modules.add(v, tokens),
668 Att::Provide(tokens, ProvideKind::Flag(..)) => {
669 enum_errors.add(tokens, ATTR_PROVIDE_FLAG)
670 }
671 Att::Provide(tokens, ProvideKind::Expression { .. }) => {
672 enum_errors.add(tokens, ATTR_PROVIDE_EXPRESSION)
673 }
674 Att::Backtrace(tokens, ..) => enum_errors.add(tokens, ATTR_BACKTRACE),
675 Att::Implicit(tokens, ..) => enum_errors.add(tokens, ATTR_IMPLICIT),
676 Att::Whatever(tokens) => enum_errors.add(tokens, ATTR_WHATEVER),
677 Att::DocComment(..) => { }
678 }
679 }
680
681 let (module, errs) = modules.finish();
682 errors.extend(errs);
683
684 let (default_visibility, errs) = default_visibilities.finish();
685 errors.extend(errs);
686
687 let (maybe_default_suffix, errs) = default_suffixes.finish();
688 let default_suffix = maybe_default_suffix.unwrap_or(SuffixKind::Default);
689 errors.extend(errs);
690
691 let (maybe_crate_root, errs) = crate_roots.finish();
692 let crate_root = maybe_crate_root.unwrap_or_else(default_crate_root);
693 errors.extend(errs);
694
695 let variants: sponge::AllErrors<_, _> = enum_
696 .variants
697 .into_iter()
698 .map(|variant| {
699 let fields = match variant.fields {
700 Fields::Named(f) => f.named.into_iter().collect(),
701 Fields::Unnamed(_) => {
702 return Err(vec![syn::Error::new(
703 variant.fields.span(),
704 "Can only derive `Snafu` for enums with struct-like and unit enum variants",
705 )]);
706 }
707 Fields::Unit => vec![],
708 };
709
710 let name = variant.ident;
711 let span = name.span();
712
713 let attrs = attributes_from_syn(variant.attrs)?;
714
715 field_container(
716 name,
717 span,
718 attrs,
719 fields,
720 &mut errors,
721 ErrorLocation::OnVariant,
722 ErrorLocation::InVariant,
723 )
724 })
725 .collect();
726
727 let variants = errors.absorb(variants.into_result())?;
728
729 Ok(EnumInfo {
730 crate_root,
731 name,
732 generics,
733 variants,
734 default_visibility,
735 default_suffix,
736 module,
737 })
738}
739
740fn field_container(
741 name: syn::Ident,
742 variant_span: proc_macro2::Span,
743 attrs: Vec<SnafuAttribute>,
744 fields: Vec<syn::Field>,
745 errors: &mut SyntaxErrors,
746 outer_error_location: ErrorLocation,
747 inner_error_location: ErrorLocation,
748) -> MultiSynResult<FieldContainer> {
749 use quote::ToTokens;
750 use syn::spanned::Spanned;
751
752 let mut outer_errors = errors.scoped(outer_error_location);
753
754 let mut modules = AtMostOne::new("module", outer_error_location);
755 let mut display_formats = AtMostOne::new("display", outer_error_location);
756 let mut visibilities = AtMostOne::new("visibility", outer_error_location);
757 let mut provides = Vec::new();
758
759 let mut contexts = AtMostOne::new("context", outer_error_location);
760 let mut whatevers = AtMostOne::new("whatever", outer_error_location);
761 let mut doc_comment = DocComment::default();
762 let mut reached_end_of_doc_comment = false;
763
764 for attr in attrs {
765 use SnafuAttribute as Att;
766
767 match attr {
768 Att::Module(tokens, n) => modules.add(n, tokens),
769 Att::Display(tokens, d) => display_formats.add(d, tokens),
770 Att::Visibility(tokens, v) => visibilities.add(v, tokens),
771 Att::Context(tokens, c) => contexts.add(c, tokens),
772 Att::Whatever(tokens) => whatevers.add((), tokens),
773 Att::Source(tokens, ..) => outer_errors.add(tokens, ATTR_SOURCE),
774 Att::Backtrace(tokens, ..) => outer_errors.add(tokens, ATTR_BACKTRACE),
775 Att::Implicit(tokens, ..) => outer_errors.add(tokens, ATTR_IMPLICIT),
776 Att::CrateRoot(tokens, ..) => outer_errors.add(tokens, ATTR_CRATE_ROOT),
777 Att::Provide(tokens, ProvideKind::Flag(..)) => {
778 outer_errors.add(tokens, ATTR_PROVIDE_FLAG)
779 }
780 Att::Provide(_tts, ProvideKind::Expression(provide)) => {
781 provides.push(provide);
783 }
784 Att::DocComment(_tts, doc_comment_line) => {
785 if !reached_end_of_doc_comment {
789 let trimmed = doc_comment_line.trim();
790 if trimmed.is_empty() {
791 reached_end_of_doc_comment = true;
792 } else {
793 doc_comment.push_str(trimmed);
794 }
795 }
796 }
797 }
798 }
799
800 let mut user_fields = Vec::new();
801 let mut source_fields = AtMostOne::new("source", inner_error_location);
802 let mut backtrace_fields = AtMostOne::new("backtrace", inner_error_location);
803 let mut implicit_fields = Vec::new();
804
805 for syn_field in fields {
806 let original = syn_field.clone();
807 let span = syn_field.span();
808 let name = syn_field
809 .ident
810 .as_ref()
811 .ok_or_else(|| vec![syn::Error::new(span, "Must have a named field")])?;
812
813 let mut source_attrs = AtMostOne::new("source", ErrorLocation::OnField);
822 let mut backtrace_attrs = AtMostOne::new("backtrace", ErrorLocation::OnField);
823 let mut implicit_attrs = AtMostOne::new("implicit", ErrorLocation::OnField);
824 let mut provide_attrs = AtMostOne::new("provide", ErrorLocation::OnField);
825
826 let mut source_opt_out = false;
829 let mut backtrace_opt_out = false;
830 let mut implicit_opt_out = false;
831 let mut provide_opt_out = false;
832
833 let mut field_errors = errors.scoped(ErrorLocation::OnField);
834
835 for attr in attributes_from_syn(syn_field.attrs.clone())? {
836 use SnafuAttribute as Att;
837
838 match attr {
839 Att::Source(tokens, ss) => {
840 for s in ss {
841 match s {
842 Source::Flag(v) => {
843 let seen_source_from = source_attrs
846 .iter()
847 .map(|(val, _location)| val)
848 .any(Option::is_some);
849 if !v && seen_source_from {
850 field_errors.add(tokens.clone(), SOURCE_BOOL_FROM_INCOMPATIBLE);
851 }
852 if v {
853 source_attrs.add(None, tokens.clone());
854 } else if is_implicit_source(name) {
855 source_opt_out = true;
856 } else {
857 field_errors.add(tokens.clone(), ATTR_SOURCE_FALSE);
858 }
859 }
860 Source::From(t, e) => {
861 if source_opt_out {
862 field_errors.add(tokens.clone(), SOURCE_BOOL_FROM_INCOMPATIBLE);
863 }
864 source_attrs.add(Some((t, e)), tokens.clone());
865 }
866 }
867 }
868 }
869 Att::Backtrace(tokens, v) => {
870 if v {
871 backtrace_attrs.add((), tokens);
872 } else if is_implicit_backtrace(name) {
873 backtrace_opt_out = true;
874 } else {
875 field_errors.add(tokens, ATTR_BACKTRACE_FALSE);
876 }
877 }
878 Att::Implicit(tokens, v) => {
879 if v {
880 implicit_attrs.add((), tokens);
881 } else if is_implicit_location(name) {
882 implicit_opt_out = true;
883 } else {
884 field_errors.add(tokens, ATTR_IMPLICIT_FALSE);
885 }
886 }
887 Att::Module(tokens, ..) => field_errors.add(tokens, ATTR_MODULE),
888 Att::Provide(tokens, ProvideKind::Flag(v)) => {
889 if v {
890 provide_attrs.add((), tokens);
891 } else if is_implicit_provide(name) {
892 provide_opt_out = true;
893 } else {
894 field_errors.add(tokens, ATTR_PROVIDE_FALSE)
895 }
896 }
897 Att::Provide(tokens, ProvideKind::Expression { .. }) => {
898 field_errors.add(tokens, ATTR_PROVIDE_EXPRESSION)
899 }
900 Att::Visibility(tokens, ..) => field_errors.add(tokens, ATTR_VISIBILITY),
901 Att::Display(tokens, ..) => field_errors.add(tokens, ATTR_DISPLAY),
902 Att::Context(tokens, ..) => field_errors.add(tokens, ATTR_CONTEXT),
903 Att::Whatever(tokens) => field_errors.add(tokens, ATTR_WHATEVER),
904 Att::CrateRoot(tokens, ..) => field_errors.add(tokens, ATTR_CRATE_ROOT),
905 Att::DocComment(..) => { }
906 }
907 }
908
909 let (source_attr, errs) = source_attrs.finish_with_location();
911 errors.extend(errs);
912 let (backtrace_attr, errs) = backtrace_attrs.finish_with_location();
913 errors.extend(errs);
914
915 let (implicit_attr, errs) = implicit_attrs.finish();
916 errors.extend(errs);
917
918 let (provide_attr, errs) = provide_attrs.finish();
919 errors.extend(errs);
920
921 let field = Field {
922 name: name.clone(),
923 ty: syn_field.ty.clone(),
924 provide: provide_attr.is_some() || (is_implicit_provide(&name) && !provide_opt_out),
925 original,
926 };
927
928 let source_attr = source_attr.or_else(|| {
929 if is_implicit_source(&field.name) && !source_opt_out {
930 Some((None, syn_field.clone().into_token_stream()))
931 } else {
932 None
933 }
934 });
935
936 let backtrace_attr = backtrace_attr.or_else(|| {
937 if is_implicit_backtrace(&field.name) && !backtrace_opt_out {
938 Some(((), syn_field.clone().into_token_stream()))
939 } else {
940 None
941 }
942 });
943
944 let implicit_attr =
945 implicit_attr.is_some() || (is_implicit_location(&field.name) && !implicit_opt_out);
946
947 if let Some((maybe_transformation, location)) = source_attr {
948 let Field {
949 name, ty, provide, ..
950 } = field;
951 let transformation = maybe_transformation
952 .map(|(source_ty, expr)| Transformation::Transform {
953 source_ty,
954 target_ty: ty.clone(),
955 expr,
956 })
957 .unwrap_or_else(|| Transformation::None { ty });
958
959 source_fields.add(
960 SourceField {
961 name,
962 transformation,
963 backtrace_delegate: backtrace_attr.is_some(),
966 provide,
967 },
968 location,
969 );
970 } else if let Some((_, location)) = backtrace_attr {
971 backtrace_fields.add(field, location);
972 } else if implicit_attr {
973 implicit_fields.push(field);
974 } else {
975 user_fields.push(field);
976 }
977 }
978
979 let (source, errs) = source_fields.finish_with_location();
980 errors.extend(errs);
981
982 let (backtrace, errs) = backtrace_fields.finish_with_location();
983 errors.extend(errs);
984
985 match (&source, &backtrace) {
986 (Some(source), Some(backtrace)) if source.0.backtrace_delegate => {
987 let source_location = source.1.clone();
988 let backtrace_location = backtrace.1.clone();
989 errors.add(
990 source_location,
991 "Cannot have `backtrace` field and `backtrace` attribute on a source field in the same variant",
992 );
993 errors.add(
994 backtrace_location,
995 "Cannot have `backtrace` field and `backtrace` attribute on a source field in the same variant",
996 );
997 }
998 _ => {} }
1000
1001 let (module, errs) = modules.finish();
1002 errors.extend(errs);
1003
1004 let (display_format, errs) = display_formats.finish();
1005 errors.extend(errs);
1006
1007 let (visibility, errs) = visibilities.finish();
1008 errors.extend(errs);
1009
1010 let (is_context, errs) = contexts.finish_with_location();
1011 let is_context = is_context.map(|(c, tt)| (c.into_enabled(), tt));
1012 errors.extend(errs);
1013
1014 let (is_whatever, errs) = whatevers.finish_with_location();
1015 errors.extend(errs);
1016
1017 let source_field = source.map(|(val, _tts)| val);
1018
1019 let selector_kind = match (is_context, is_whatever) {
1020 (Some(((true, _), c_tt)), Some(((), o_tt))) => {
1021 let txt = "Cannot be both a `context` and `whatever` error";
1022 return Err(vec![
1023 syn::Error::new_spanned(c_tt, txt),
1024 syn::Error::new_spanned(o_tt, txt),
1025 ]);
1026 }
1027
1028 (Some(((true, suffix), _)), None) => ContextSelectorKind::Context {
1029 suffix,
1030 source_field,
1031 user_fields,
1032 },
1033
1034 (None, None) => ContextSelectorKind::Context {
1035 suffix: SuffixKind::Default,
1036 source_field,
1037 user_fields,
1038 },
1039
1040 (Some(((false, _), _)), Some(_)) | (None, Some(_)) => {
1041 let mut messages = AtMostOne::new("message", outer_error_location);
1042
1043 for f in user_fields {
1044 if is_implicit_message(&f.name) {
1045 let l = f.original.clone();
1046 messages.add(f, l);
1047 } else {
1048 errors.add(
1049 f.original,
1050 "Whatever selectors must not have context fields",
1051 );
1052 }
1054 }
1055
1056 let (message_field, errs) = messages.finish();
1057 errors.extend(errs);
1058
1059 let message_field = message_field.ok_or_else(|| {
1060 vec![syn::Error::new(
1061 variant_span,
1062 "Whatever selectors must have a message field",
1063 )]
1064 })?;
1065
1066 ContextSelectorKind::Whatever {
1067 source_field,
1068 message_field,
1069 }
1070 }
1071
1072 (Some(((false, _), _)), None) => {
1073 errors.extend(user_fields.into_iter().map(|Field { original, .. }| {
1074 syn::Error::new_spanned(
1075 original,
1076 "Context selectors without context must not have context fields",
1077 )
1078 }));
1079
1080 let source_field = source_field.ok_or_else(|| {
1081 vec![syn::Error::new(
1082 variant_span,
1083 "Context selectors without context must have a source field",
1084 )]
1085 })?;
1086
1087 ContextSelectorKind::NoContext { source_field }
1088 }
1089 };
1090
1091 Ok(FieldContainer {
1092 name,
1093 backtrace_field: backtrace.map(|(val, _tts)| val),
1094 implicit_fields,
1095 selector_kind,
1096 display_format,
1097 doc_comment: doc_comment.finish(),
1098 visibility,
1099 module,
1100 provides,
1101 })
1102}
1103
1104const IMPLICIT_SOURCE_FIELD_NAME: &str = "source";
1105const IMPLICIT_BACKTRACE_FIELD_NAME: &str = "backtrace";
1106const IMPLICIT_MESSAGE_FIELD_NAME: &str = "message";
1107const IMPLICIT_LOCATION_FIELD_NAME: &str = "location";
1108
1109fn is_implicit_source(name: &proc_macro2::Ident) -> bool {
1110 name == IMPLICIT_SOURCE_FIELD_NAME
1111}
1112
1113fn is_implicit_backtrace(name: &proc_macro2::Ident) -> bool {
1114 name == IMPLICIT_BACKTRACE_FIELD_NAME
1115}
1116
1117fn is_implicit_message(name: &proc_macro2::Ident) -> bool {
1118 name == IMPLICIT_MESSAGE_FIELD_NAME
1119}
1120
1121fn is_implicit_location(name: &proc_macro2::Ident) -> bool {
1122 name == IMPLICIT_LOCATION_FIELD_NAME
1123}
1124
1125fn is_implicit_provide(name: &proc_macro2::Ident) -> bool {
1126 is_implicit_source(name) || is_implicit_backtrace(name)
1127}
1128
1129fn parse_snafu_struct(
1130 struct_: syn::DataStruct,
1131 name: syn::Ident,
1132 generics: syn::Generics,
1133 attrs: Vec<syn::Attribute>,
1134 span: proc_macro2::Span,
1135) -> MultiSynResult<SnafuInfo> {
1136 use syn::Fields;
1137
1138 match struct_.fields {
1139 Fields::Named(f) => {
1140 let f = f.named.into_iter().collect();
1141 parse_snafu_named_struct(f, name, generics, attrs, span).map(SnafuInfo::NamedStruct)
1142 }
1143 Fields::Unnamed(f) => {
1144 parse_snafu_tuple_struct(f, name, generics, attrs, span).map(SnafuInfo::TupleStruct)
1145 }
1146 Fields::Unit => parse_snafu_named_struct(vec![], name, generics, attrs, span)
1147 .map(SnafuInfo::NamedStruct),
1148 }
1149}
1150
1151fn parse_snafu_named_struct(
1152 fields: Vec<syn::Field>,
1153 name: syn::Ident,
1154 generics: syn::Generics,
1155 attrs: Vec<syn::Attribute>,
1156 span: proc_macro2::Span,
1157) -> MultiSynResult<NamedStructInfo> {
1158 let mut errors = SyntaxErrors::default();
1159
1160 let attrs = attributes_from_syn(attrs)?;
1161
1162 let mut crate_roots = AtMostOne::new("crate_root", ErrorLocation::OnNamedStruct);
1163
1164 let attrs = attrs
1165 .into_iter()
1166 .flat_map(|attr| match attr {
1167 SnafuAttribute::CrateRoot(tokens, root) => {
1168 crate_roots.add(root, tokens);
1169 None
1170 }
1171 other => Some(other),
1172 })
1173 .collect();
1174
1175 let field_container = field_container(
1176 name,
1177 span,
1178 attrs,
1179 fields,
1180 &mut errors,
1181 ErrorLocation::OnNamedStruct,
1182 ErrorLocation::InNamedStruct,
1183 )?;
1184
1185 let (maybe_crate_root, errs) = crate_roots.finish();
1186 let crate_root = maybe_crate_root.unwrap_or_else(default_crate_root);
1187 errors.extend(errs);
1188
1189 errors.finish()?;
1190
1191 Ok(NamedStructInfo {
1192 crate_root,
1193 field_container,
1194 generics,
1195 })
1196}
1197
1198fn parse_snafu_tuple_struct(
1199 mut fields: syn::FieldsUnnamed,
1200 name: syn::Ident,
1201 generics: syn::Generics,
1202 attrs: Vec<syn::Attribute>,
1203 span: proc_macro2::Span,
1204) -> MultiSynResult<TupleStructInfo> {
1205 let mut transformations = AtMostOne::new("source(from)", ErrorLocation::OnTupleStruct);
1206 let mut crate_roots = AtMostOne::new("crate_root", ErrorLocation::OnTupleStruct);
1207 let mut provides = Vec::new();
1208
1209 let mut errors = SyntaxErrors::default();
1210 let mut struct_errors = errors.scoped(ErrorLocation::OnTupleStruct);
1211
1212 for attr in attributes_from_syn(attrs)? {
1213 use SnafuAttribute as Att;
1214
1215 match attr {
1216 Att::Module(tokens, ..) => struct_errors.add(tokens, ATTR_MODULE),
1217 Att::Provide(tokens, ProvideKind::Flag(..)) => {
1218 struct_errors.add(tokens, ATTR_PROVIDE_FLAG)
1219 }
1220 Att::Provide(_tokens, ProvideKind::Expression(provide)) => {
1221 provides.push(provide);
1222 }
1223 Att::Display(tokens, ..) => struct_errors.add(tokens, ATTR_DISPLAY),
1224 Att::Visibility(tokens, ..) => struct_errors.add(tokens, ATTR_VISIBILITY),
1225 Att::Source(tokens, ss) => {
1226 for s in ss {
1227 match s {
1228 Source::Flag(..) => struct_errors.add(tokens.clone(), ATTR_SOURCE_BOOL),
1229 Source::From(t, e) => transformations.add((t, e), tokens.clone()),
1230 }
1231 }
1232 }
1233 Att::Backtrace(tokens, ..) => struct_errors.add(tokens, ATTR_BACKTRACE),
1234 Att::Implicit(tokens, ..) => struct_errors.add(tokens, ATTR_IMPLICIT),
1235 Att::Context(tokens, ..) => struct_errors.add(tokens, ATTR_CONTEXT),
1236 Att::Whatever(tokens) => struct_errors.add(tokens, ATTR_CONTEXT),
1237 Att::CrateRoot(tokens, root) => crate_roots.add(root, tokens),
1238 Att::DocComment(..) => { }
1239 }
1240 }
1241
1242 fn one_field_error(span: proc_macro2::Span) -> syn::Error {
1243 syn::Error::new(
1244 span,
1245 "Can only derive `Snafu` for tuple structs with exactly one field",
1246 )
1247 }
1248
1249 let inner = fields
1250 .unnamed
1251 .pop()
1252 .ok_or_else(|| vec![one_field_error(span)])?;
1253 if !fields.unnamed.is_empty() {
1254 return Err(vec![one_field_error(span)]);
1255 }
1256
1257 let ty = inner.into_value().ty;
1258 let (maybe_transformation, errs) = transformations.finish();
1259 let transformation = maybe_transformation
1260 .map(|(source_ty, expr)| Transformation::Transform {
1261 source_ty,
1262 target_ty: ty.clone(),
1263 expr,
1264 })
1265 .unwrap_or_else(|| Transformation::None { ty });
1266 errors.extend(errs);
1267
1268 let (maybe_crate_root, errs) = crate_roots.finish();
1269 let crate_root = maybe_crate_root.unwrap_or_else(default_crate_root);
1270 errors.extend(errs);
1271
1272 errors.finish()?;
1273
1274 Ok(TupleStructInfo {
1275 crate_root,
1276 name,
1277 generics,
1278 transformation,
1279 provides,
1280 })
1281}
1282
1283enum Context {
1284 Flag(bool),
1285 Suffix(SuffixKind),
1286}
1287
1288impl Context {
1289 fn into_enabled(self) -> (bool, SuffixKind) {
1290 match self {
1291 Context::Flag(b) => (b, SuffixKind::None),
1292 Context::Suffix(suffix) => (true, suffix),
1293 }
1294 }
1295}
1296
1297enum Source {
1298 Flag(bool),
1299 From(syn::Type, syn::Expr),
1300}
1301
1302struct Display {
1303 exprs: Vec<syn::Expr>,
1304 shorthand_names: BTreeSet<syn::Ident>,
1305 assigned_names: BTreeSet<syn::Ident>,
1306}
1307
1308#[derive(Default)]
1309struct DocComment {
1310 content: String,
1311 shorthand_names: BTreeSet<syn::Ident>,
1312}
1313
1314impl DocComment {
1315 fn push_str(&mut self, s: &str) {
1316 if !self.content.is_empty() {
1317 self.content.push_str(" ");
1318 }
1319 self.content.push_str(s);
1320 }
1321
1322 fn finish(mut self) -> Option<Self> {
1323 if self.content.is_empty() {
1324 None
1325 } else {
1326 self.shorthand_names.extend(
1327 crate::parse::extract_field_names(&self.content)
1328 .map(|n| quote::format_ident!("{}", n)),
1329 );
1330
1331 Some(self)
1332 }
1333 }
1334}
1335
1336enum SnafuAttribute {
1344 Backtrace(proc_macro2::TokenStream, bool),
1345 Context(proc_macro2::TokenStream, Context),
1346 CrateRoot(proc_macro2::TokenStream, UserInput),
1347 Display(proc_macro2::TokenStream, Display),
1348 DocComment(proc_macro2::TokenStream, String),
1349 Implicit(proc_macro2::TokenStream, bool),
1350 Module(proc_macro2::TokenStream, ModuleName),
1351 Provide(proc_macro2::TokenStream, ProvideKind),
1352 Source(proc_macro2::TokenStream, Vec<Source>),
1353 Visibility(proc_macro2::TokenStream, UserInput),
1354 Whatever(proc_macro2::TokenStream),
1355}
1356
1357fn default_crate_root() -> UserInput {
1358 Box::new(quote! { ::snafu })
1359}
1360
1361fn private_visibility() -> UserInput {
1362 Box::new(quote! {})
1363}
1364
1365fn default_context_selector_visibility_in_module() -> proc_macro2::TokenStream {
1368 quote! { pub(super) }
1369}
1370
1371impl From<SnafuInfo> for proc_macro::TokenStream {
1372 fn from(other: SnafuInfo) -> proc_macro::TokenStream {
1373 match other {
1374 SnafuInfo::Enum(e) => e.into(),
1375 SnafuInfo::NamedStruct(s) => s.into(),
1376 SnafuInfo::TupleStruct(s) => s.into(),
1377 }
1378 }
1379}
1380
1381impl From<EnumInfo> for proc_macro::TokenStream {
1382 fn from(other: EnumInfo) -> proc_macro::TokenStream {
1383 other.generate_snafu().into()
1384 }
1385}
1386
1387impl From<NamedStructInfo> for proc_macro::TokenStream {
1388 fn from(other: NamedStructInfo) -> proc_macro::TokenStream {
1389 other.generate_snafu().into()
1390 }
1391}
1392
1393impl From<TupleStructInfo> for proc_macro::TokenStream {
1394 fn from(other: TupleStructInfo) -> proc_macro::TokenStream {
1395 other.generate_snafu().into()
1396 }
1397}
1398
1399trait GenericAwareNames {
1400 fn name(&self) -> &syn::Ident;
1401
1402 fn generics(&self) -> &syn::Generics;
1403
1404 fn parameterized_name(&self) -> UserInput {
1405 let enum_name = self.name();
1406 let original_generics = self.provided_generic_names();
1407
1408 Box::new(quote! { #enum_name<#(#original_generics,)*> })
1409 }
1410
1411 fn provided_generic_types_without_defaults(&self) -> Vec<proc_macro2::TokenStream> {
1412 use syn::TypeParam;
1413 self.generics()
1414 .type_params()
1415 .map(|t: &TypeParam| {
1416 let TypeParam {
1417 attrs,
1418 ident,
1419 colon_token,
1420 bounds,
1421 ..
1422 } = t;
1423 quote! {
1424 #(#attrs)*
1425 #ident
1426 #colon_token
1427 #bounds
1428 }
1429 })
1430 .collect()
1431 }
1432
1433 fn provided_generics_without_defaults(&self) -> Vec<proc_macro2::TokenStream> {
1434 self.provided_generic_lifetimes()
1435 .into_iter()
1436 .chain(self.provided_generic_types_without_defaults().into_iter())
1437 .collect()
1438 }
1439
1440 fn provided_generic_lifetimes(&self) -> Vec<proc_macro2::TokenStream> {
1441 use syn::{GenericParam, LifetimeDef};
1442
1443 self.generics()
1444 .params
1445 .iter()
1446 .flat_map(|p| match p {
1447 GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => Some(quote! { #lifetime }),
1448 _ => None,
1449 })
1450 .collect()
1451 }
1452
1453 fn provided_generic_names(&self) -> Vec<proc_macro2::TokenStream> {
1454 use syn::{ConstParam, GenericParam, LifetimeDef, TypeParam};
1455
1456 self.generics()
1457 .params
1458 .iter()
1459 .map(|p| match p {
1460 GenericParam::Type(TypeParam { ident, .. }) => quote! { #ident },
1461 GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => quote! { #lifetime },
1462 GenericParam::Const(ConstParam { ident, .. }) => quote! { #ident },
1463 })
1464 .collect()
1465 }
1466
1467 fn provided_where_clauses(&self) -> Vec<proc_macro2::TokenStream> {
1468 self.generics()
1469 .where_clause
1470 .iter()
1471 .flat_map(|c| c.predicates.iter().map(|p| quote! { #p }))
1472 .collect()
1473 }
1474}
1475
1476impl EnumInfo {
1477 fn generate_snafu(self) -> proc_macro2::TokenStream {
1478 let context_selectors = ContextSelectors(&self);
1479 let display_impl = DisplayImpl(&self);
1480 let error_impl = ErrorImpl(&self);
1481 let error_compat_impl = ErrorCompatImpl(&self);
1482
1483 let context = match &self.module {
1484 None => quote! { #context_selectors },
1485 Some(module_name) => {
1486 use crate::shared::ContextModule;
1487
1488 let context_module = ContextModule {
1489 container_name: self.name(),
1490 body: &context_selectors,
1491 visibility: Some(&self.default_visibility),
1492 module_name,
1493 };
1494
1495 quote! { #context_module }
1496 }
1497 };
1498
1499 quote! {
1500 #context
1501 #display_impl
1502 #error_impl
1503 #error_compat_impl
1504 }
1505 }
1506}
1507
1508impl GenericAwareNames for EnumInfo {
1509 fn name(&self) -> &syn::Ident {
1510 &self.name
1511 }
1512
1513 fn generics(&self) -> &syn::Generics {
1514 &self.generics
1515 }
1516}
1517
1518struct ContextSelectors<'a>(&'a EnumInfo);
1519
1520impl<'a> quote::ToTokens for ContextSelectors<'a> {
1521 fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1522 let context_selectors = self
1523 .0
1524 .variants
1525 .iter()
1526 .map(|variant| ContextSelector(self.0, variant));
1527
1528 stream.extend({
1529 quote! {
1530 #(#context_selectors)*
1531 }
1532 })
1533 }
1534}
1535
1536struct ContextSelector<'a>(&'a EnumInfo, &'a FieldContainer);
1537
1538impl<'a> quote::ToTokens for ContextSelector<'a> {
1539 fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1540 use crate::shared::ContextSelector;
1541
1542 let enum_name = &self.0.name;
1543 let default_suffix = &self.0.default_suffix;
1544
1545 let FieldContainer {
1546 name: variant_name,
1547 selector_kind,
1548 ..
1549 } = self.1;
1550
1551 let default_visibility;
1552 let selector_visibility = match (
1553 &self.1.visibility,
1554 &self.0.default_visibility,
1555 &self.0.module,
1556 ) {
1557 (Some(v), _, _) | (_, Some(v), _) => Some(&**v),
1558 (None, None, Some(_)) => {
1559 default_visibility = default_context_selector_visibility_in_module();
1560 Some(&default_visibility as _)
1561 }
1562 (None, None, None) => None,
1563 };
1564
1565 let selector_doc_string = format!(
1566 "SNAFU context selector for the `{}::{}` variant",
1567 enum_name, variant_name,
1568 );
1569
1570 let context_selector = ContextSelector {
1571 backtrace_field: self.1.backtrace_field.as_ref(),
1572 implicit_fields: &self.1.implicit_fields,
1573 crate_root: &self.0.crate_root,
1574 error_constructor_name: "e! { #enum_name::#variant_name },
1575 original_generics_without_defaults: &self.0.provided_generics_without_defaults(),
1576 parameterized_error_name: &self.0.parameterized_name(),
1577 selector_doc_string: &selector_doc_string,
1578 selector_kind: &selector_kind,
1579 selector_name: variant_name,
1580 user_fields: &selector_kind.user_fields(),
1581 visibility: selector_visibility,
1582 where_clauses: &self.0.provided_where_clauses(),
1583 default_suffix,
1584 };
1585
1586 stream.extend(quote! { #context_selector });
1587 }
1588}
1589
1590struct DisplayImpl<'a>(&'a EnumInfo);
1591
1592impl<'a> quote::ToTokens for DisplayImpl<'a> {
1593 fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1594 use self::shared::{Display, DisplayMatchArm};
1595
1596 let enum_name = &self.0.name;
1597
1598 let arms: Vec<_> = self
1599 .0
1600 .variants
1601 .iter()
1602 .map(|variant| {
1603 let FieldContainer {
1604 display_format,
1605 doc_comment,
1606 name: variant_name,
1607 selector_kind,
1608 ..
1609 } = variant;
1610
1611 let arm = DisplayMatchArm {
1612 field_container: variant,
1613 default_name: &variant_name,
1614 display_format: display_format.as_ref(),
1615 doc_comment: doc_comment.as_ref(),
1616 pattern_ident: "e! { #enum_name::#variant_name },
1617 selector_kind,
1618 };
1619
1620 quote! { #arm }
1621 })
1622 .collect();
1623
1624 let display = Display {
1625 arms: &arms,
1626 original_generics: &self.0.provided_generics_without_defaults(),
1627 parameterized_error_name: &self.0.parameterized_name(),
1628 where_clauses: &self.0.provided_where_clauses(),
1629 };
1630
1631 let display_impl = quote! { #display };
1632
1633 stream.extend(display_impl)
1634 }
1635}
1636
1637struct ErrorImpl<'a>(&'a EnumInfo);
1638
1639impl<'a> quote::ToTokens for ErrorImpl<'a> {
1640 fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1641 use self::shared::{Error, ErrorProvideMatchArm, ErrorSourceMatchArm};
1642
1643 let crate_root = &self.0.crate_root;
1644
1645 let mut variants_to_description = Vec::with_capacity(self.0.variants.len());
1646 let mut variants_to_source = Vec::with_capacity(self.0.variants.len());
1647 let mut variants_to_provide = Vec::with_capacity(self.0.variants.len());
1648
1649 for field_container in &self.0.variants {
1650 let enum_name = &self.0.name;
1651 let variant_name = &field_container.name;
1652 let pattern_ident = "e! { #enum_name::#variant_name };
1653
1654 let error_description_match_arm = quote! {
1655 #pattern_ident { .. } => stringify!(#pattern_ident),
1656 };
1657
1658 let error_source_match_arm = ErrorSourceMatchArm {
1659 field_container,
1660 pattern_ident,
1661 };
1662 let error_source_match_arm = quote! { #error_source_match_arm };
1663
1664 let error_provide_match_arm = ErrorProvideMatchArm {
1665 crate_root,
1666 field_container,
1667 pattern_ident,
1668 };
1669 let error_provide_match_arm = quote! { #error_provide_match_arm };
1670
1671 variants_to_description.push(error_description_match_arm);
1672 variants_to_source.push(error_source_match_arm);
1673 variants_to_provide.push(error_provide_match_arm);
1674 }
1675
1676 let error_impl = Error {
1677 crate_root,
1678 parameterized_error_name: &self.0.parameterized_name(),
1679 description_arms: &variants_to_description,
1680 source_arms: &variants_to_source,
1681 original_generics: &self.0.provided_generics_without_defaults(),
1682 where_clauses: &self.0.provided_where_clauses(),
1683 provide_arms: &variants_to_provide,
1684 };
1685 let error_impl = quote! { #error_impl };
1686
1687 stream.extend(error_impl);
1688 }
1689}
1690
1691struct ErrorCompatImpl<'a>(&'a EnumInfo);
1692
1693impl<'a> quote::ToTokens for ErrorCompatImpl<'a> {
1694 fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
1695 use self::shared::{ErrorCompat, ErrorCompatBacktraceMatchArm};
1696
1697 let variants_to_backtrace: Vec<_> = self
1698 .0
1699 .variants
1700 .iter()
1701 .map(|field_container| {
1702 let crate_root = &self.0.crate_root;
1703 let enum_name = &self.0.name;
1704 let variant_name = &field_container.name;
1705
1706 let match_arm = ErrorCompatBacktraceMatchArm {
1707 field_container,
1708 crate_root,
1709 pattern_ident: "e! { #enum_name::#variant_name },
1710 };
1711
1712 quote! { #match_arm }
1713 })
1714 .collect();
1715
1716 let error_compat_impl = ErrorCompat {
1717 crate_root: &self.0.crate_root,
1718 parameterized_error_name: &self.0.parameterized_name(),
1719 backtrace_arms: &variants_to_backtrace,
1720 original_generics: &self.0.provided_generics_without_defaults(),
1721 where_clauses: &self.0.provided_where_clauses(),
1722 };
1723
1724 let error_compat_impl = quote! { #error_compat_impl };
1725
1726 stream.extend(error_compat_impl);
1727 }
1728}
1729
1730impl NamedStructInfo {
1731 fn generate_snafu(self) -> proc_macro2::TokenStream {
1732 let parameterized_struct_name = self.parameterized_name();
1733 let original_generics = self.provided_generics_without_defaults();
1734 let where_clauses = self.provided_where_clauses();
1735
1736 let Self {
1737 crate_root,
1738 field_container:
1739 FieldContainer {
1740 name,
1741 selector_kind,
1742 backtrace_field,
1743 implicit_fields,
1744 display_format,
1745 doc_comment,
1746 visibility,
1747 module,
1748 ..
1749 },
1750 ..
1751 } = &self;
1752 let field_container = &self.field_container;
1753
1754 let user_fields = selector_kind.user_fields();
1755
1756 use crate::shared::{Error, ErrorProvideMatchArm, ErrorSourceMatchArm};
1757
1758 let pattern_ident = "e! { Self };
1759
1760 let error_description_match_arm = quote! {
1761 #pattern_ident { .. } => stringify!(#name),
1762 };
1763
1764 let error_source_match_arm = ErrorSourceMatchArm {
1765 field_container: &field_container,
1766 pattern_ident,
1767 };
1768 let error_source_match_arm = quote! { #error_source_match_arm };
1769
1770 let error_provide_match_arm = ErrorProvideMatchArm {
1771 crate_root: &crate_root,
1772 field_container,
1773 pattern_ident,
1774 };
1775 let error_provide_match_arm = quote! { #error_provide_match_arm };
1776
1777 let error_impl = Error {
1778 crate_root: &crate_root,
1779 description_arms: &[error_description_match_arm],
1780 original_generics: &original_generics,
1781 parameterized_error_name: ¶meterized_struct_name,
1782 provide_arms: &[error_provide_match_arm],
1783 source_arms: &[error_source_match_arm],
1784 where_clauses: &where_clauses,
1785 };
1786 let error_impl = quote! { #error_impl };
1787
1788 use self::shared::{ErrorCompat, ErrorCompatBacktraceMatchArm};
1789
1790 let match_arm = ErrorCompatBacktraceMatchArm {
1791 field_container,
1792 crate_root: &crate_root,
1793 pattern_ident: "e! { Self },
1794 };
1795 let match_arm = quote! { #match_arm };
1796
1797 let error_compat_impl = ErrorCompat {
1798 crate_root: &crate_root,
1799 parameterized_error_name: ¶meterized_struct_name,
1800 backtrace_arms: &[match_arm],
1801 original_generics: &original_generics,
1802 where_clauses: &where_clauses,
1803 };
1804
1805 use crate::shared::{Display, DisplayMatchArm};
1806
1807 let arm = DisplayMatchArm {
1808 field_container,
1809 default_name: &name,
1810 display_format: display_format.as_ref(),
1811 doc_comment: doc_comment.as_ref(),
1812 pattern_ident: "e! { Self },
1813 selector_kind: &selector_kind,
1814 };
1815 let arm = quote! { #arm };
1816
1817 let display_impl = Display {
1818 arms: &[arm],
1819 original_generics: &original_generics,
1820 parameterized_error_name: ¶meterized_struct_name,
1821 where_clauses: &where_clauses,
1822 };
1823
1824 use crate::shared::ContextSelector;
1825
1826 let selector_doc_string = format!("SNAFU context selector for the `{}` error", name);
1827
1828 let default_visibility;
1829 let selector_visibility = match (visibility, module) {
1830 (Some(v), _) => Some(&**v),
1831 (None, Some(_)) => {
1832 default_visibility = default_context_selector_visibility_in_module();
1833 Some(&default_visibility as _)
1834 }
1835 (None, None) => None,
1836 };
1837
1838 let context_selector = ContextSelector {
1839 backtrace_field: backtrace_field.as_ref(),
1840 implicit_fields: implicit_fields,
1841 crate_root: &crate_root,
1842 error_constructor_name: &name,
1843 original_generics_without_defaults: &original_generics,
1844 parameterized_error_name: ¶meterized_struct_name,
1845 selector_doc_string: &selector_doc_string,
1846 selector_kind: &selector_kind,
1847 selector_name: &field_container.name,
1848 user_fields: &user_fields,
1849 visibility: selector_visibility,
1850 where_clauses: &where_clauses,
1851 default_suffix: &SuffixKind::Default,
1852 };
1853
1854 let context = match module {
1855 None => quote! { #context_selector },
1856 Some(module_name) => {
1857 use crate::shared::ContextModule;
1858
1859 let context_module = ContextModule {
1860 container_name: self.name(),
1861 body: &context_selector,
1862 visibility: visibility.as_ref().map(|x| &**x),
1863 module_name,
1864 };
1865
1866 quote! { #context_module }
1867 }
1868 };
1869
1870 quote! {
1871 #error_impl
1872 #error_compat_impl
1873 #display_impl
1874 #context
1875 }
1876 }
1877}
1878
1879impl GenericAwareNames for NamedStructInfo {
1880 fn name(&self) -> &syn::Ident {
1881 &self.field_container.name
1882 }
1883
1884 fn generics(&self) -> &syn::Generics {
1885 &self.generics
1886 }
1887}
1888
1889impl TupleStructInfo {
1890 fn generate_snafu(self) -> proc_macro2::TokenStream {
1891 let parameterized_struct_name = self.parameterized_name();
1892
1893 let TupleStructInfo {
1894 crate_root,
1895 generics,
1896 name,
1897 transformation,
1898 provides,
1899 } = self;
1900
1901 let inner_type = transformation.source_ty();
1902 let transformation = transformation.transformation();
1903
1904 let where_clauses: Vec<_> = generics
1905 .where_clause
1906 .iter()
1907 .flat_map(|c| c.predicates.iter().map(|p| quote! { #p }))
1908 .collect();
1909
1910 let description_fn = quote! {
1911 fn description(&self) -> &str {
1912 #crate_root::Error::description(&self.0)
1913 }
1914 };
1915
1916 let cause_fn = quote! {
1917 fn cause(&self) -> ::core::option::Option<&dyn #crate_root::Error> {
1918 #crate_root::Error::cause(&self.0)
1919 }
1920 };
1921
1922 let source_fn = quote! {
1923 fn source(&self) -> ::core::option::Option<&(dyn #crate_root::Error + 'static)> {
1924 #crate_root::Error::source(&self.0)
1925 }
1926 };
1927
1928 let backtrace_fn = quote! {
1929 fn backtrace(&self) -> ::core::option::Option<&#crate_root::Backtrace> {
1930 #crate_root::ErrorCompat::backtrace(&self.0)
1931 }
1932 };
1933
1934 let std_backtrace_fn = if cfg!(feature = "unstable-backtraces-impl-std") {
1935 quote! {
1936 fn backtrace(&self) -> ::core::option::Option<&std::backtrace::Backtrace> {
1937 #crate_root::ErrorCompat::backtrace(self)
1938 }
1939 }
1940 } else {
1941 quote! {}
1942 };
1943
1944 let provide_fn = if cfg!(feature = "unstable-provider-api") {
1945 use shared::error::PROVIDE_ARG;
1946
1947 let provides = shared::error::enhance_provider_list(&provides);
1948 let cached_expressions = shared::error::quote_cached_expressions(&provides);
1949 let user_chained = shared::error::quote_chained(&provides);
1950
1951 let (hi_explicit_calls, lo_explicit_calls) =
1952 shared::error::build_explicit_provide_calls(&provides);
1953
1954 Some(quote! {
1955 fn provide<'a>(&'a self, #PROVIDE_ARG: &mut core::any::Demand<'a>) {
1956 match self {
1957 Self(v) => {
1958 #(#cached_expressions;)*
1959 #(#hi_explicit_calls;)*
1960 v.provide(#PROVIDE_ARG);
1961 #(#user_chained;)*
1962 #(#lo_explicit_calls;)*
1963 }
1964 };
1965 }
1966 })
1967 } else {
1968 None
1969 };
1970
1971 let error_impl = quote! {
1972 #[allow(single_use_lifetimes)]
1973 impl#generics #crate_root::Error for #parameterized_struct_name
1974 where
1975 #(#where_clauses),*
1976 {
1977 #description_fn
1978 #cause_fn
1979 #source_fn
1980 #std_backtrace_fn
1981 #provide_fn
1982 }
1983 };
1984
1985 let error_compat_impl = quote! {
1986 #[allow(single_use_lifetimes)]
1987 impl#generics #crate_root::ErrorCompat for #parameterized_struct_name
1988 where
1989 #(#where_clauses),*
1990 {
1991 #backtrace_fn
1992 }
1993 };
1994
1995 let display_impl = quote! {
1996 #[allow(single_use_lifetimes)]
1997 impl#generics ::core::fmt::Display for #parameterized_struct_name
1998 where
1999 #(#where_clauses),*
2000 {
2001 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
2002 ::core::fmt::Display::fmt(&self.0, f)
2003 }
2004 }
2005 };
2006
2007 let from_impl = quote! {
2008 impl#generics ::core::convert::From<#inner_type> for #parameterized_struct_name
2009 where
2010 #(#where_clauses),*
2011 {
2012 fn from(other: #inner_type) -> Self {
2013 #name((#transformation)(other))
2014 }
2015 }
2016 };
2017
2018 quote! {
2019 #error_impl
2020 #error_compat_impl
2021 #display_impl
2022 #from_impl
2023 }
2024 }
2025}
2026
2027impl GenericAwareNames for TupleStructInfo {
2028 fn name(&self) -> &syn::Ident {
2029 &self.name
2030 }
2031
2032 fn generics(&self) -> &syn::Generics {
2033 &self.generics
2034 }
2035}
2036
2037trait Transpose<T, E> {
2038 fn my_transpose(self) -> Result<Option<T>, E>;
2039}
2040
2041impl<T, E> Transpose<T, E> for Option<Result<T, E>> {
2042 fn my_transpose(self) -> Result<Option<T>, E> {
2043 match self {
2044 Some(Ok(v)) => Ok(Some(v)),
2045 Some(Err(e)) => Err(e),
2046 None => Ok(None),
2047 }
2048 }
2049}
2050
2051mod sponge {
2052 use std::iter::FromIterator;
2053
2054 pub struct AllErrors<T, E>(Result<T, Vec<E>>);
2055
2056 impl<T, E> AllErrors<T, E> {
2057 pub fn into_result(self) -> Result<T, Vec<E>> {
2058 self.0
2059 }
2060 }
2061
2062 impl<C, T, E> FromIterator<Result<C, E>> for AllErrors<T, E>
2063 where
2064 T: FromIterator<C>,
2065 {
2066 fn from_iter<I>(i: I) -> Self
2067 where
2068 I: IntoIterator<Item = Result<C, E>>,
2069 {
2070 let mut errors = Vec::new();
2071
2072 let inner = i
2073 .into_iter()
2074 .flat_map(|v| match v {
2075 Ok(v) => Ok(v),
2076 Err(e) => {
2077 errors.push(e);
2078 Err(())
2079 }
2080 })
2081 .collect();
2082
2083 if errors.is_empty() {
2084 AllErrors(Ok(inner))
2085 } else {
2086 AllErrors(Err(errors))
2087 }
2088 }
2089 }
2090
2091 impl<C, T, E> FromIterator<Result<C, Vec<E>>> for AllErrors<T, E>
2092 where
2093 T: FromIterator<C>,
2094 {
2095 fn from_iter<I>(i: I) -> Self
2096 where
2097 I: IntoIterator<Item = Result<C, Vec<E>>>,
2098 {
2099 let mut errors = Vec::new();
2100
2101 let inner = i
2102 .into_iter()
2103 .flat_map(|v| match v {
2104 Ok(v) => Ok(v),
2105 Err(e) => {
2106 errors.extend(e);
2107 Err(())
2108 }
2109 })
2110 .collect();
2111
2112 if errors.is_empty() {
2113 AllErrors(Ok(inner))
2114 } else {
2115 AllErrors(Err(errors))
2116 }
2117 }
2118 }
2119}