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_some(source_field).flatten());
423 let meta_field = fields.iter().find(|f| f.is_meta()).cloned();
424 Ok(VariantInfo {
425 ident,
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_n(quote! { _ }, 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 generate_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.generate_from_impl()).map(|(ty, construct)| {
661 quote! {
662 impl #impl_generics ::core::convert::From<#ty> for #enum_ident #ty_generics #where_clause {
663 #[allow(unreachable_code)]
664 #[track_caller]
665 fn from(source: #ty) -> Self {
666 #construct
667 }
668 }
669 }
670 });
671
672 quote! {
673 impl #impl_generics ::n0_error::StackError for #enum_ident #ty_generics #where_clause {
674 fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
675 self
676 }
677 fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
678 self
679 }
680 fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
681 self
682 }
683 fn fmt_message(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
684 match self {
685 #( #match_fmt_message_arms, )*
686 }
687 }
688 fn meta(&self) -> Option<&::n0_error::Meta> {
689 match self {
690 #( #match_meta_arms, )*
691 }
692 }
693 fn source(&self) -> Option<::n0_error::ErrorRef<'_>> {
694 match self {
695 #( #match_source_arms )*
696 }
697 }
698 fn is_transparent(&self) -> bool {
699 match self {
700 #( #match_transparent_arms, )*
701 }
702 }
703 }
704
705 impl #impl_generics ::core::convert::From<#enum_ident #ty_generics> for ::n0_error::AnyError #where_clause {
706 fn from(value: #enum_ident #ty_generics) -> ::n0_error::AnyError {
707 ::n0_error::AnyError::from_stack(value)
708 }
709 }
710
711 impl #impl_generics ::std::fmt::Display for #enum_ident #ty_generics #where_clause {
712 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
713 let sources = f.alternate().then_some(::n0_error::SourceFormat::OneLine);
714 write!(f, "{}", ::n0_error::StackError::report(self).sources(sources))
715 }
716 }
717
718 impl #impl_generics ::std::fmt::Debug for #enum_ident #ty_generics #where_clause {
719 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
720 if f.alternate() {
721 match self {
722 #(#match_debug_arms)*
723 }
724 } else {
725 write!(f, "{}", ::n0_error::StackError::report(self).full())?;
726 }
727 Ok(())
728 }
729 }
730
731 impl #impl_generics ::std::error::Error for #enum_ident #ty_generics #where_clause {
732 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
733 match self {
734 #( #match_std_source_arms )*
735 }
736 }
737 }
738 #( #from_impls )*
739 }
740}
741
742fn generate_struct_impl(
743 item_ident: &Ident,
744 generics: &syn::Generics,
745 info: VariantInfo,
746) -> proc_macro2::TokenStream {
747 let constructor = {
748 let params = info.fields.iter().filter(|f| !f.is_meta()).map(|f| {
749 let ty = &f.field.ty;
750 let ident = f.ident.as_ident();
751 quote! { #ident: #ty }
752 });
753 let fields = info.fields.iter().map(|f| {
754 if f.is_meta() {
755 f.ident.init(quote!(::n0_error::Meta::new()))
756 } else {
757 let ident = f.ident.as_ident();
758 quote!(#ident)
759 }
760 });
761 let construct = info.spread(fields);
762 let doc = format!("Creates a new [`{}`] error.", item_ident);
763 quote! {
764 #[doc = #doc]
765 #[allow(unreachable_code)]
766 #[track_caller]
767 pub fn new(#(#params),*) -> Self { #construct }
768 }
769 };
770 let get_meta = if let Some(field) = &info.meta {
771 let get_expr = field.ident.self_expr();
772 quote! { Some(&#get_expr) }
773 } else {
774 quote! { None }
775 };
776
777 let get_error_source = match &info.source {
778 Some(src) => src.expr_error_ref(src.field.ident.self_expr()),
779 None => quote! { None },
780 };
781
782 let get_std_source = match &info.source {
783 Some(src) => src.expr_error_std(src.field.ident.self_expr()),
784 None => quote! { None },
785 };
786
787 let is_transparent = info.transparent().is_some();
788
789 let get_fmt_message = {
790 match &info.display {
791 DisplayArgs::Format(expr) => {
792 let pat = info.spread_all();
793 quote! {
794 #[allow(unused)]
795 let #pat = self;
796 #expr
797 }
798 }
799 DisplayArgs::Default | DisplayArgs::Transparent => {
800 let text = info.ident.item_ident().to_string();
801 quote! { write!(f, #text) }
802 }
803 }
804 };
805
806 let get_debug = {
807 let item_name = info.ident.item_ident().to_string();
808 match info.kind {
809 Kind::Unit => quote!(write!(f, #item_name)?;),
810 Kind::Named => {
811 let fields = info
812 .fields
813 .iter()
814 .filter_map(|f| f.ident.named())
815 .map(|ident| {
816 let ident_s = ident.to_string();
817 quote! { dbg.field(#ident_s, &self.#ident) }
818 });
819 quote! {
820 let mut dbg = f.debug_struct(#item_name);
821 #(#fields);*;
822 dbg.finish()?;
823 }
824 }
825 Kind::Tuple => {
826 let binds = (0..info.fields.len()).map(syn::Index::from);
827 quote! {
828 let mut dbg = f.debug_tuple(#item_name);
829 #( dbg.field(&self.#binds); )*;
830 dbg.finish()?;
831 }
832 }
833 }
834 };
835
836 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
838 let from_impl = info.generate_from_impl().map(|(ty, construct)| {
839 quote! {
840 impl #impl_generics ::core::convert::From<#ty> for #item_ident #ty_generics #where_clause {
841 #[allow(unreachable_code)]
842 #[track_caller]
843 fn from(source: #ty) -> Self { #construct }
844 }
845 }
846 });
847
848 quote! {
849 impl #impl_generics #item_ident #ty_generics #where_clause {
850 #constructor
851 }
852
853 impl #impl_generics ::n0_error::StackError for #item_ident #ty_generics #where_clause {
854 fn as_std(&self) -> &(dyn ::std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static) {
855 self
856 }
857 fn into_std(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn std::error::Error + ::std::marker::Send + ::std::marker::Sync> {
858 self
859 }
860 fn as_dyn(&self) -> &(dyn ::n0_error::StackError) {
861 self
862 }
863 fn meta(&self) -> Option<&::n0_error::Meta> {
864 #get_meta
865 }
866 fn source(&self) -> Option<::n0_error::ErrorRef<'_>> {
867 #get_error_source
868 }
869 fn is_transparent(&self) -> bool {
870 #is_transparent
871 }
872 fn fmt_message(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
873 #get_fmt_message
874 }
875 }
876
877
878 impl #impl_generics ::core::convert::From<#item_ident #ty_generics> for ::n0_error::AnyError #where_clause {
879 fn from(value: #item_ident #ty_generics) -> ::n0_error::AnyError {
880 ::n0_error::AnyError::from_stack(value)
881 }
882 }
883
884 impl #impl_generics ::std::fmt::Display for #item_ident #ty_generics #where_clause {
885 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
886 let sources = f.alternate().then_some(::n0_error::SourceFormat::OneLine);
887 write!(f, "{}", ::n0_error::StackError::report(self).sources(sources))
888 }
889 }
890
891 impl #impl_generics ::std::fmt::Debug for #item_ident #ty_generics #where_clause {
892 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
893 if f.alternate() {
894 #get_debug
895 } else {
896 write!(f, "{}", ::n0_error::StackError::report(self).full())?;
897 }
898 Ok(())
899 }
900 }
901
902 impl #impl_generics ::std::error::Error for #item_ident #ty_generics #where_clause {
903 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
904 #get_std_source
905 }
906 }
907
908 #from_impl
909 }
910}
911
912enum DisplayArgs {
913 Default,
914 Transparent,
915 Format(proc_macro2::TokenStream),
916}
917
918impl DisplayArgs {
919 fn is_transparent(&self) -> bool {
920 matches!(self, Self::Transparent)
921 }
922
923 fn parse(attrs: &[Attribute]) -> Result<DisplayArgs, syn::Error> {
924 let Some(attr) = attrs.iter().find(|a| a.path().is_ident("error")) else {
926 return Ok(DisplayArgs::Default);
927 };
928
929 let args: Punctuated<Expr, syn::Token![,]> =
930 attr.parse_args_with(Punctuated::<Expr, syn::Token![,]>::parse_terminated)?;
931
932 if args.is_empty() {
933 return Err(err(
934 attr,
935 "#[error(..)] requires arguments: a format string or `transparent`",
936 ));
937 }
938
939 if args.len() == 1 {
941 if let Expr::Path(p) = &args[0] {
942 if p.path.is_ident("transparent") {
943 return Ok(DisplayArgs::Transparent);
944 }
945 }
946 }
947
948 let mut args = args.into_iter();
950 let first = args.next().unwrap();
951 let fmt_lit = match first {
952 Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }) => s,
953 other => return Err(err(
954 other,
955 "first argument to #[error(\"...\")] must be a string literal, or use #[error(transparent)]",
956 ))
957 };
958
959 let rest: Vec<Expr> = args.collect();
960 Ok(DisplayArgs::Format(
961 quote! { write!(f, #fmt_lit #(, #rest)* ) },
962 ))
963 }
964}
965
966fn err(ident: impl ToTokens, err: impl ToString) -> syn::Error {
967 syn::Error::new_spanned(ident, err.to_string())
968}
969
970fn parse_error_attr_as_idents(attrs: &[Attribute]) -> Result<Vec<Ident>, syn::Error> {
971 let mut out = vec![];
972 for attr in attrs.iter().filter(|a| a.path().is_ident("error")) {
973 let idents = attr.parse_args_with(|input: syn::parse::ParseStream<'_>| {
974 let list: Punctuated<Ident, syn::Token![,]> =
975 Punctuated::<Ident, syn::Token![,]>::parse_terminated(input)?;
976 Ok(list.into_iter())
977 })?;
978 out.extend(idents);
979 }
980 Ok(out)
981}