1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::{quote, ToTokens};
4use syn::{
5 parse_macro_input, parse_quote, punctuated::Punctuated, Attribute, DeriveInput, Expr, Field,
6 Fields, FieldsNamed, Ident,
7};
8
9#[proc_macro_attribute]
53pub fn stack_error(args: TokenStream, item: TokenStream) -> TokenStream {
54 match stack_error_inner(args, parse_macro_input!(item as syn::Item)) {
55 Err(err) => err.to_compile_error().into(),
56 Ok(tokens) => tokens.into(),
57 }
58}
59
60fn stack_error_inner(
61 args: TokenStream,
62 mut input: syn::Item,
63) -> Result<proc_macro2::TokenStream, syn::Error> {
64 let args: StackErrAttrArgs = syn::parse(args.clone())?;
65 match &mut input {
66 syn::Item::Enum(item) => {
67 if args.add_meta {
68 for variant in item.variants.iter_mut() {
69 add_meta_field(&mut variant.fields);
70 }
71 }
72 modify_attrs(&args, &mut item.attrs)?;
73 Ok(quote! { #item })
74 }
75 syn::Item::Struct(item) => {
76 if args.add_meta {
77 add_meta_field(&mut item.fields);
78 }
79 modify_attrs(&args, &mut item.attrs)?;
80 Ok(quote! { #item })
81 }
82 _ => Err(err(
83 &input,
84 "#[stack_error] only supports enums and structs",
85 )),
86 }
87}
88
89fn modify_attrs(args: &StackErrAttrArgs, attrs: &mut Vec<Attribute>) -> Result<(), syn::Error> {
90 attrs.retain(|attr| !attr.path().is_ident("stackerr"));
91 if args.derive {
92 attrs.insert(0, parse_quote!(#[derive(::n0_error::StackError)]));
93 }
94 let error_args: Vec<_> = [
95 args.from_sources.then(|| quote!(from_sources)),
96 args.std_sources.then(|| quote!(std_sources)),
97 ]
98 .into_iter()
99 .flatten()
100 .collect();
101 if !error_args.is_empty() {
102 attrs.push(parse_quote!(#[error(#(#error_args),*)]))
103 }
104
105 Ok(())
106}
107
108fn add_meta_field(fields: &mut Fields) {
109 let doc = "Captured call-site metadata";
110 match fields {
111 Fields::Named(fields) => {
112 let field: syn::Field = parse_quote! { #[doc = #doc] meta: ::n0_error::Meta };
113 fields.named.push(field);
114 }
115 Fields::Unit => {
116 let mut named = FieldsNamed {
117 brace_token: Default::default(),
118 named: Default::default(),
119 };
120 let field: syn::Field = parse_quote! { #[doc = #doc] meta: ::n0_error::Meta };
121 named.named.push(field);
122 *fields = Fields::Named(named);
123 }
124 Fields::Unnamed(fields) => {
125 let field: syn::Field = parse_quote! { #[doc = #doc] #[error(meta)] ::n0_error::Meta };
126 fields.unnamed.push(field);
127 }
128 }
129}
130
131#[proc_macro_derive(StackError, attributes(error))]
163pub fn derive_stack_error(input: TokenStream) -> TokenStream {
164 let input = parse_macro_input!(input as syn::DeriveInput);
165 match derive_error_inner(input) {
166 Err(err) => err.to_compile_error().into(),
167 Ok(tokens) => tokens.into(),
168 }
169}
170
171fn derive_error_inner(input: DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
172 match &input.data {
173 syn::Data::Enum(item) => {
174 let args = EnumAttrArgs::from_attributes(&input.attrs)?;
175 let infos = item
176 .variants
177 .iter()
178 .map(|v| {
179 let ident = VariantIdent::Variant(&input.ident, &v.ident);
180 VariantInfo::parse(ident, &v.fields, &v.attrs, &args)
181 })
182 .collect::<Result<Vec<_>, _>>()?;
183 Ok(generate_enum_impls(&input.ident, &input.generics, infos))
184 }
185 syn::Data::Struct(item) => {
186 let ident = VariantIdent::Struct(&input.ident);
187 let info = VariantInfo::parse(ident, &item.fields, &input.attrs, &Default::default())?;
188 Ok(generate_struct_impl(&input.ident, &input.generics, info))
189 }
190 _ => Err(err(
191 &input,
192 "#[derive(StackError)] only supports enums or structs",
193 )),
194 }
195}
196
197struct SourceField<'a> {
198 kind: SourceKind,
199 field: FieldInfo<'a>,
200}
201
202impl<'a> SourceField<'a> {
203 fn expr_error_ref(&self, expr: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
204 match self.kind {
205 SourceKind::Std => quote! { Some(::n0_error::ErrorRef::std(#expr)) },
206 SourceKind::Stack => quote! { Some(::n0_error::ErrorRef::stack(#expr)) },
207 }
208 }
209
210 fn expr_error_std(&self, expr: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
211 match self.kind {
212 SourceKind::Std => quote! { Some(#expr as &dyn ::std::error::Error) },
213 SourceKind::Stack => quote! { Some(::n0_error::StackError::as_std(#expr)) },
214 }
215 }
216}
217
218enum SourceKind {
219 Stack,
220 Std,
221}
222
223#[derive(Default, Clone, Copy)]
224struct StackErrAttrArgs {
225 add_meta: bool,
226 derive: bool,
227 from_sources: bool,
228 std_sources: bool,
229}
230
231impl syn::parse::Parse for StackErrAttrArgs {
232 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
233 let mut out = Self::default();
234 while !input.is_empty() {
235 let ident: Ident = input.parse()?;
236 match ident.to_string().as_str() {
237 "add_meta" => out.add_meta = true,
238 "derive" => out.derive = true,
239 "from_sources" => out.from_sources = true,
240 "std_sources" => out.std_sources = true,
241 other => Err(err(
242 ident,
243 format!("unknown stack_error option `{}`", other),
244 ))?,
245 }
246 if input.peek(syn::Token![,]) {
247 let _ = input.parse::<syn::Token![,]>()?;
248 }
249 }
250 Ok(out)
251 }
252}
253
254#[derive(Default, Clone, Copy)]
255struct EnumAttrArgs {
256 from_sources: bool,
257 std_sources: bool,
258}
259
260impl EnumAttrArgs {
261 fn from_attributes(attrs: &[Attribute]) -> Result<Self, syn::Error> {
262 let mut out = Self::default();
263 for ident in parse_error_attr_as_idents(attrs)? {
264 match ident.to_string().as_str() {
265 "from_sources" => out.from_sources = true,
266 "std_sources" => out.std_sources = true,
267 _ => Err(err(
268 ident,
269 "Invalid argument for the `error` attribute on fields",
270 ))?,
271 }
272 }
273 Ok(out)
274 }
275}
276
277#[derive(Default, Clone, Copy)]
278struct FieldAttrArgs {
279 source: bool,
280 from: bool,
281 std_err: bool,
282 stack_err: bool,
283 meta: bool,
284}
285
286impl FieldAttrArgs {
287 fn from_attributes(attrs: &[Attribute]) -> Result<Self, syn::Error> {
288 let mut out = Self::default();
289 for ident in parse_error_attr_as_idents(attrs)? {
290 match ident.to_string().as_str() {
291 "source" => out.source = true,
292 "from" => out.from = true,
293 "std_err" => out.std_err = true,
294 "stack_err" => out.stack_err = true,
295 "meta" => out.meta = true,
296 _ => Err(err(
297 ident,
298 "Invalid argument for the `error` attribute on fields",
299 ))?,
300 }
301 }
302 Ok(out)
303 }
304}
305
306#[derive(Debug, Clone, Copy)]
307enum VariantIdent<'a> {
308 Struct(&'a Ident),
309 Variant(&'a Ident, &'a Ident),
310}
311
312impl<'a> VariantIdent<'a> {
313 fn self_ident(&self) -> proc_macro2::TokenStream {
314 match self {
315 VariantIdent::Struct(_) => quote!(Self),
316 VariantIdent::Variant(_, variant) => quote!(Self::#variant),
317 }
318 }
319 fn item_ident(&self) -> &Ident {
320 match self {
321 VariantIdent::Struct(ident) => &ident,
322 VariantIdent::Variant(ident, _) => &ident,
323 }
324 }
325
326 fn inner(&self) -> &Ident {
327 match self {
328 VariantIdent::Struct(ident) => &ident,
329 VariantIdent::Variant(_, ident) => &ident,
330 }
331 }
332}
333
334#[derive(Clone, Copy, PartialEq, Eq)]
335enum Kind {
336 Named,
337 Unit,
338 Tuple,
339}
340
341struct VariantInfo<'a> {
342 ident: VariantIdent<'a>,
343 fields: Vec<FieldInfo<'a>>,
344 kind: Kind,
345 display: DisplayArgs,
346 source: Option<SourceField<'a>>,
347 from: Option<FieldInfo<'a>>,
349 meta: Option<FieldInfo<'a>>,
351}
352
353impl<'a> VariantInfo<'a> {
354 fn parse(
355 ident: VariantIdent<'a>,
356 fields: &'a Fields,
357 attrs: &[Attribute],
358 args: &EnumAttrArgs,
359 ) -> Result<VariantInfo<'a>, syn::Error> {
360 let display = DisplayArgs::parse(&attrs)?;
361 let (kind, fields): (Kind, Vec<FieldInfo>) = match fields {
362 Fields::Named(ref fields) => (
363 Kind::Named,
364 fields
365 .named
366 .iter()
367 .map(FieldInfo::from_named)
368 .collect::<Result<_, _>>()?,
369 ),
370 Fields::Unit => (Kind::Unit, Vec::new()),
371 Fields::Unnamed(ref fields) => (
372 Kind::Tuple,
373 fields
374 .unnamed
375 .iter()
376 .enumerate()
377 .map(|(i, f)| FieldInfo::from_unnamed(i, f))
378 .collect::<Result<_, _>>()?,
379 ),
380 };
381
382 if fields.iter().filter(|f| f.args.source).count() > 1 {
383 return Err(err(
384 ident.inner(),
385 "Only one field per variant may have #[error(source)]",
386 ));
387 }
388 let source_field = fields
389 .iter()
390 .find(|f| f.args.source)
391 .or_else(|| match kind {
392 Kind::Named => fields
393 .iter()
394 .find(|f| matches!(f.ident, FieldIdent::Named(name) if name == "source")),
395 Kind::Tuple if display.is_transparent() => fields.first(),
396 _ => None,
397 });
398
399 if display.is_transparent() && source_field.is_none() {
400 return Err(err(
401 ident.inner(),
402 "Variants with #[error(transparent)] require a source field",
403 ));
404 }
405
406 let source = source_field.as_ref().map(|field| {
408 let kind = if field.args.std_err || (args.std_sources && !field.args.stack_err) {
409 SourceKind::Std
410 } else {
411 SourceKind::Stack
412 };
413 SourceField {
414 kind,
415 field: (*field).clone(),
416 }
417 });
418
419 let from_field = fields
420 .iter()
421 .find(|f| f.args.from)
422 .or_else(|| args.from_sources.then(|| source_field).flatten());
423 let meta_field = fields.iter().find(|f| f.is_meta()).cloned();
424 Ok(VariantInfo {
425 ident: ident.clone(),
426 kind,
427 display,
428 from: from_field.cloned(),
429 meta: meta_field,
430 source,
431 fields,
432 })
433 }
434
435 fn transparent(&self) -> Option<&FieldInfo<'_>> {
436 match self.display {
437 DisplayArgs::Transparent => self.source.as_ref().map(|s| &s.field),
438 _ => None,
439 }
440 }
441
442 fn spread_field(&self, field: &FieldInfo<'a>, bind: &Ident) -> proc_macro2::TokenStream {
443 let slf = self.ident.self_ident();
444 match field.ident {
445 FieldIdent::Named(ident) => {
446 quote! { #slf { #ident: #bind, .. } }
447 }
448 FieldIdent::Unnamed(_) => {
449 let pats = self.fields.iter().map(|f| {
450 if f.ident == field.ident {
451 quote!(#bind)
452 } else {
453 quote!(_)
454 }
455 });
456 quote! { #slf ( #(#pats),* ) }
457 }
458 }
459 }
460
461 fn spread_empty(&self) -> proc_macro2::TokenStream {
462 let slf = self.ident.self_ident();
463 match self.kind {
464 Kind::Unit => quote! { #slf },
465 Kind::Named => quote! { #slf { .. } },
466 Kind::Tuple => {
467 let pats = std::iter::repeat(quote! { _ }).take(self.fields.len());
468 quote! { #slf ( #(#pats),* ) }
469 }
470 }
471 }
472
473 fn spread_all(&self) -> proc_macro2::TokenStream {
474 let binds = self.fields.iter().map(|f| f.ident.as_ident());
475 self.spread(binds.map(|i| quote!(#i)))
476 }
477
478 fn spread(
479 &self,
480 fields: impl Iterator<Item = proc_macro2::TokenStream>,
481 ) -> proc_macro2::TokenStream {
482 let slf = self.ident.self_ident();
483 match self.kind {
484 Kind::Unit => quote! { #slf },
485 Kind::Named => quote! { #slf { #(#fields),* } },
486 Kind::Tuple => quote! { #slf ( #(#fields),* ) },
487 }
488 }
489
490 fn from_impl(&self) -> Option<(&syn::Type, proc_macro2::TokenStream)> {
491 self.from.as_ref().map(|from_field| {
492 let ty = &from_field.field.ty;
493 let fields = self.fields.iter().map(|field| {
494 if field.ident == from_field.ident {
495 field.ident.init(quote!(source))
496 } else if field.is_meta() {
497 field.ident.init(quote!(::n0_error::Meta::new()))
498 } else {
499 field.ident.init(quote!(::std::default::Default::default()))
500 }
501 });
502 let construct = self.spread(fields);
503 (ty, construct)
504 })
505 }
506}
507
508#[derive(Clone)]
509struct FieldInfo<'a> {
510 field: &'a Field,
511 args: FieldAttrArgs,
512 ident: FieldIdent<'a>,
513}
514
515impl<'a> FieldInfo<'a> {
516 fn from_named(field: &'a Field) -> Result<Self, syn::Error> {
517 Ok(Self {
518 args: FieldAttrArgs::from_attributes(&field.attrs)?,
519 ident: FieldIdent::Named(field.ident.as_ref().unwrap()),
520 field,
521 })
522 }
523 fn from_unnamed(index: usize, field: &'a Field) -> Result<Self, syn::Error> {
524 Ok(Self {
525 args: FieldAttrArgs::from_attributes(&field.attrs)?,
526 ident: FieldIdent::Unnamed(index),
527 field,
528 })
529 }
530
531 fn is_meta(&self) -> bool {
532 self.args.meta || matches!(self.ident, FieldIdent::Named(ident) if ident == "meta")
533 }
534}
535
536#[derive(Clone, Copy, Eq, PartialEq)]
537enum FieldIdent<'a> {
538 Named(&'a Ident),
539 Unnamed(usize),
540}
541
542impl<'a> FieldIdent<'a> {
543 fn named(&self) -> Option<&'a Ident> {
544 match self {
545 FieldIdent::Named(ident) => Some(ident),
546 _ => None,
547 }
548 }
549
550 fn as_ident(&self) -> Ident {
551 match self {
552 FieldIdent::Named(ident) => (*ident).clone(),
553 FieldIdent::Unnamed(i) => syn::Ident::new(&format!("_{}", i), Span::call_site()),
554 }
555 }
556
557 fn self_expr(&self) -> proc_macro2::TokenStream {
558 match self {
559 FieldIdent::Named(ident) => quote!(self.#ident),
560 FieldIdent::Unnamed(i) => {
561 let idx = syn::Index::from(*i);
562 quote!(self.#idx)
563 }
564 }
565 }
566
567 fn init(&self, expr: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
568 match self {
569 FieldIdent::Named(ident) => quote!(#ident: #expr),
570 FieldIdent::Unnamed(_) => quote!(#expr),
571 }
572 }
573}
574
575fn generate_enum_impls(
576 enum_ident: &Ident,
577 generics: &syn::Generics,
578 variants: Vec<VariantInfo>,
579) -> proc_macro2::TokenStream {
580 let match_meta_arms = variants.iter().map(|vi| {
581 if let Some(meta_field) = &vi.meta {
582 let bind = syn::Ident::new("__meta", Span::call_site());
583 let pat = vi.spread_field(meta_field, &bind);
584 quote! { #pat => Some(&#bind) }
585 } else {
586 let pat = vi.spread_empty();
587 quote! { #pat => None }
588 }
589 });
590
591 let match_source_arms = variants.iter().map(|vi| match &vi.source {
592 Some(src) => {
593 let bind = syn::Ident::new("__source", Span::call_site());
594 let pat = vi.spread_field(&src.field, &bind);
595 let expr = src.expr_error_ref(quote!(#bind));
596 quote! { #pat => #expr, }
597 }
598 None => {
599 let pat = vi.spread_empty();
600 quote! { #pat => None, }
601 }
602 });
603
604 let match_std_source_arms = variants.iter().map(|vi| match &vi.source {
605 Some(src) => {
606 let bind = syn::Ident::new("__source", Span::call_site());
607 let pat = vi.spread_field(&src.field, &bind);
608 let expr = src.expr_error_std(quote!(#bind));
609 quote! { #pat => #expr, }
610 }
611 None => {
612 let pat = vi.spread_empty();
613 quote! { #pat => None, }
614 }
615 });
616
617 let match_transparent_arms = variants.iter().map(|vi| {
618 let value = vi.transparent().is_some();
619 let pat = vi.spread_empty();
620 quote! { #pat => #value }
621 });
622
623 let match_fmt_message_arms = variants.iter().map(|vi| match &vi.display {
624 DisplayArgs::Format(expr) => {
625 let pat = vi.spread_all();
626 quote! {
627 #[allow(unused)]
628 #pat => { #expr }
629 }
630 }
631 DisplayArgs::Default | DisplayArgs::Transparent => {
632 let text = format!("{}::{}", vi.ident.item_ident(), vi.ident.inner());
633 let pat = vi.spread_empty();
634 quote! { #pat => write!(f, #text) }
635 }
636 });
637
638 let match_debug_arms = variants.iter().map(|vi| {
639 let v_name = vi.ident.inner().to_string();
640 let binds = vi.fields.iter().map(|f| f.ident.as_ident());
641 let pat = vi.spread_all();
642 let labels: Vec<String> = vi
643 .fields
644 .iter()
645 .map(|f| match f.ident {
646 FieldIdent::Named(id) => id.to_string(),
647 FieldIdent::Unnamed(i) => i.to_string(),
648 })
649 .collect();
650 quote! {
651 #pat => {
652 let mut dbg = f.debug_struct(#v_name);
653 #( dbg.field(#labels, &#binds); )*; dbg.finish()?;
654 }
655 }
656 });
657
658 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
660 let from_impls = variants.iter().filter_map(|vi| vi.from_impl()).map(|(ty, construct)| {
661 quote! {
662 impl #impl_generics ::core::convert::From<#ty> for #enum_ident #ty_generics #where_clause {
663 #[track_caller]
664 fn from(source: #ty) -> Self {
665 #construct
666 }
667 }
668 }
669 });
670
671 quote! {
672 impl #impl_generics ::n0_error::StackError for #enum_ident #ty_generics #where_clause {
673 fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
674 self
675 }
676 fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
677 self
678 }
679 fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
680 self
681 }
682 fn fmt_message(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
683 match self {
684 #( #match_fmt_message_arms, )*
685 }
686 }
687 fn meta(&self) -> Option<&::n0_error::Meta> {
688 match self {
689 #( #match_meta_arms, )*
690 }
691 }
692 fn source(&self) -> Option<::n0_error::ErrorRef<'_>> {
693 match self {
694 #( #match_source_arms )*
695 }
696 }
697 fn is_transparent(&self) -> bool {
698 match self {
699 #( #match_transparent_arms, )*
700 }
701 }
702 }
703
704 impl #impl_generics ::core::convert::From<#enum_ident #ty_generics> for ::n0_error::AnyError #where_clause {
705 fn from(value: #enum_ident #ty_generics) -> ::n0_error::AnyError {
706 ::n0_error::AnyError::from_stack(value)
707 }
708 }
709
710 impl #impl_generics ::std::fmt::Display for #enum_ident #ty_generics #where_clause {
711 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
712 let sources = f.alternate().then_some(::n0_error::SourceFormat::OneLine);
713 write!(f, "{}", ::n0_error::StackError::report(self).sources(sources))
714 }
715 }
716
717 impl #impl_generics ::std::fmt::Debug for #enum_ident #ty_generics #where_clause {
718 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
719 if f.alternate() {
720 match self {
721 #(#match_debug_arms)*
722 }
723 } else {
724 write!(f, "{}", ::n0_error::StackError::report(self).full())?;
725 }
726 Ok(())
727 }
728 }
729
730 impl #impl_generics ::std::error::Error for #enum_ident #ty_generics #where_clause {
731 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
732 match self {
733 #( #match_std_source_arms )*
734 }
735 }
736 }
737 #( #from_impls )*
738 }
739}
740
741fn generate_struct_impl(
742 item_ident: &Ident,
743 generics: &syn::Generics,
744 info: VariantInfo,
745) -> proc_macro2::TokenStream {
746 let constructor = {
747 let params = info.fields.iter().filter(|f| !f.is_meta()).map(|f| {
748 let ty = &f.field.ty;
749 let ident = f.ident.as_ident();
750 quote! { #ident: #ty }
751 });
752 let fields = info.fields.iter().map(|f| {
753 if f.is_meta() {
754 f.ident.init(quote!(::n0_error::Meta::new()))
755 } else {
756 let ident = f.ident.as_ident();
757 quote!(#ident)
758 }
759 });
760 let construct = info.spread(fields);
761 let doc = format!("Creates a new [`{}`] error.", item_ident);
762 quote! {
763 #[doc = #doc]
764 #[track_caller]
765 pub fn new(#(#params),*) -> Self { #construct }
766 }
767 };
768 let get_meta = if let Some(field) = &info.meta {
769 let get_expr = field.ident.self_expr();
770 quote! { Some(&#get_expr) }
771 } else {
772 quote! { None }
773 };
774
775 let get_error_source = match &info.source {
776 Some(src) => src.expr_error_ref(src.field.ident.self_expr()),
777 None => quote! { None },
778 };
779
780 let get_std_source = match &info.source {
781 Some(src) => src.expr_error_std(src.field.ident.self_expr()),
782 None => quote! { None },
783 };
784
785 let is_transparent = info.transparent().is_some();
786
787 let get_fmt_message = {
788 match &info.display {
789 DisplayArgs::Format(expr) => {
790 let pat = info.spread_all();
791 quote! {
792 #[allow(unused)]
793 let #pat = self;
794 #expr
795 }
796 }
797 DisplayArgs::Default | DisplayArgs::Transparent => {
798 let text = info.ident.item_ident().to_string();
799 quote! { write!(f, #text) }
800 }
801 }
802 };
803
804 let get_debug = {
805 let item_name = info.ident.item_ident().to_string();
806 match info.kind {
807 Kind::Unit => quote!(write!(f, #item_name)?;),
808 Kind::Named => {
809 let fields = info
810 .fields
811 .iter()
812 .filter_map(|f| f.ident.named())
813 .map(|ident| {
814 let ident_s = ident.to_string();
815 quote! { dbg.field(#ident_s, &self.#ident) }
816 });
817 quote! {
818 let mut dbg = f.debug_struct(#item_name);
819 #(#fields);*;
820 dbg.finish()?;
821 }
822 }
823 Kind::Tuple => {
824 let binds = (0..info.fields.len()).map(|i| syn::Index::from(i));
825 quote! {
826 let mut dbg = f.debug_tuple(#item_name);
827 #( dbg.field(&self.#binds); )*;
828 dbg.finish()?;
829 }
830 }
831 }
832 };
833
834 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
836 let from_impl = info.from_impl().map(|(ty, construct)| {
837 quote! {
838 impl #impl_generics ::core::convert::From<#ty> for #item_ident #ty_generics #where_clause {
839 #[track_caller]
840 fn from(source: #ty) -> Self { #construct }
841 }
842 }
843 });
844
845 quote! {
846 impl #impl_generics #item_ident #ty_generics #where_clause {
847 #constructor
848 }
849
850 impl #impl_generics ::n0_error::StackError for #item_ident #ty_generics #where_clause {
851 fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
852 self
853 }
854 fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
855 self
856 }
857 fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
858 self
859 }
860 fn meta(&self) -> Option<&::n0_error::Meta> {
861 #get_meta
862 }
863 fn source(&self) -> Option<::n0_error::ErrorRef<'_>> {
864 #get_error_source
865 }
866 fn is_transparent(&self) -> bool {
867 #is_transparent
868 }
869 fn fmt_message(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
870 #get_fmt_message
871 }
872 }
873
874
875 impl #impl_generics ::core::convert::From<#item_ident #ty_generics> for ::n0_error::AnyError #where_clause {
876 fn from(value: #item_ident #ty_generics) -> ::n0_error::AnyError {
877 ::n0_error::AnyError::from_stack(value)
878 }
879 }
880
881 impl #impl_generics ::std::fmt::Display for #item_ident #ty_generics #where_clause {
882 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
883 let sources = f.alternate().then_some(::n0_error::SourceFormat::OneLine);
884 write!(f, "{}", ::n0_error::StackError::report(self).sources(sources))
885 }
886 }
887
888 impl #impl_generics ::std::fmt::Debug for #item_ident #ty_generics #where_clause {
889 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
890 if f.alternate() {
891 #get_debug
892 } else {
893 write!(f, "{}", ::n0_error::StackError::report(self).full())?;
894 }
895 Ok(())
896 }
897 }
898
899 impl #impl_generics ::std::error::Error for #item_ident #ty_generics #where_clause {
900 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
901 #get_std_source
902 }
903 }
904
905 #from_impl
906 }
907}
908
909enum DisplayArgs {
910 Default,
911 Transparent,
912 Format(proc_macro2::TokenStream),
913}
914
915impl DisplayArgs {
916 fn is_transparent(&self) -> bool {
917 matches!(self, Self::Transparent)
918 }
919
920 fn parse(attrs: &[Attribute]) -> Result<DisplayArgs, syn::Error> {
921 let Some(attr) = attrs.iter().find(|a| a.path().is_ident("error")) else {
923 return Ok(DisplayArgs::Default);
924 };
925
926 let args: Punctuated<Expr, syn::Token![,]> =
927 attr.parse_args_with(Punctuated::<Expr, syn::Token![,]>::parse_terminated)?;
928
929 if args.is_empty() {
930 return Err(err(
931 attr,
932 "#[error(..)] requires arguments: a format string or `transparent`",
933 ));
934 }
935
936 if args.len() == 1 {
938 if let Expr::Path(p) = &args[0] {
939 if p.path.is_ident("transparent") {
940 return Ok(DisplayArgs::Transparent);
941 }
942 }
943 }
944
945 let mut args = args.into_iter();
947 let first = args.next().unwrap();
948 let fmt_lit = match first {
949 Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }) => s,
950 other => return Err(err(
951 other,
952 "first argument to #[error(\"...\")] must be a string literal, or use #[error(transparent)]",
953 ))
954 };
955
956 let rest: Vec<Expr> = args.collect();
957 Ok(DisplayArgs::Format(
958 quote! { write!(f, #fmt_lit #(, #rest)* ) },
959 ))
960 }
961}
962
963fn err(ident: impl ToTokens, err: impl ToString) -> syn::Error {
964 syn::Error::new_spanned(ident, err.to_string())
965}
966
967fn parse_error_attr_as_idents(attrs: &[Attribute]) -> Result<Vec<Ident>, syn::Error> {
968 let mut out = vec![];
969 for attr in attrs.iter().filter(|a| a.path().is_ident("error")) {
970 let idents = attr.parse_args_with(|input: syn::parse::ParseStream<'_>| {
971 let list: Punctuated<Ident, syn::Token![,]> =
972 Punctuated::<Ident, syn::Token![,]>::parse_terminated(input)?;
973 Ok(list.into_iter())
974 })?;
975 out.extend(idents);
976 }
977 Ok(out)
978}