Alioth Code Coverage

errors.rs90.00%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use proc_macro::TokenStream;
16use quote::quote;
17use syn::parse::{Parse, Parser};
18use syn::{DeriveInput, GenericArgument, PathArguments, Type, parse_macro_input, parse_quote};
19
20fn extract_type_from_box(ty: &Type) -> Option<&Type> {506x
21 let Type::Path(type_path) = ty else {506x
22 return None;
23 };
24 if type_path.path.segments.first()?.ident != "Box" {506x
25 return None;322x
26 }184x
27 let arguments = &type_path.path.segments.first()?.arguments;184x
28 let PathArguments::AngleBracketed(angle_bracketed) = arguments else {184x
29 return None;
30 };
31 let generic_arg = angle_bracketed.args.first()?;184x
32 let GenericArgument::Type(ty) = generic_arg else {184x
33 return None;
34 };
35 if matches!(ty, Type::TraitObject(_)) {184x
36 None24x
37 } else {
38 Some(ty)160x
39 }
40}506x
41
42pub fn trace_error(_attr: TokenStream, item: TokenStream) -> TokenStream {91x
43 let mut input = parse_macro_input!(item as DeriveInput);91x
44 let syn::Data::Enum(enum_data) = &mut input.data else {91x
45 panic!("not an enum")
46 };
47 for variant in enum_data.variants.iter_mut() {916x
48 if matches!(variant.fields, syn::Fields::Unit) {916x
49 variant.fields =136x
50 syn::Fields::Named(syn::FieldsNamed::parse.parse2(quote! {{}}).unwrap());136x
51 }780x
52 let syn::Fields::Named(field) = &mut variant.fields else {916x
53 panic!("not a named field ")
54 };
55 field.named.push(916x
56 syn::Field::parse_named
57 .parse2(quote! {#[snafu(implicit)] _location: ::snafu::Location})916x
58 .unwrap(),916x
59 );
60 if let Some(source) = field.named.iter_mut().find(|f| {1412x
61 let name = f.ident.as_ref().unwrap();1412x
62 name == "source" || name == "error"1412x
63 }) {1412x
64 if let Some(inner_type) = extract_type_from_box(&source.ty) {506x
65 source160x
66 .attrs160x
67 .push(parse_quote! {#[snafu(source(from(#inner_type, Box::new)))]})160x
68 } else {
69 source.attrs.push(parse_quote! {#[snafu(source)]})346x
70 }
71 }410x
72 }
73
74 quote! { #input }.into()91x
75}91x
76
77pub fn derive_debug_trace(input: TokenStream) -> TokenStream {91x
78 let mut input = parse_macro_input!(input as DeriveInput);91x
79 let name = &input.ident;91x
80 let syn::Data::Enum(enum_data) = &mut input.data else {91x
81 panic!("not an enum")
82 };
83 let mut debug_trace_arms = vec![];91x
84 for variant in enum_data.variants.iter_mut() {883x
85 let syn::Fields::Named(field) = &mut variant.fields else {883x
86 panic!("not a named field ")
87 };
88 let mut cfg_attrs = vec![];883x
89 for attr in &variant.attrs {921x
90 if attr.path().is_ident("cfg") {921x
91 cfg_attrs.push(attr);38x
92 }883x
93 }
94 let is_source = |f: &syn::Field| f.ident.as_ref().unwrap() == "source";1646x
95 let has_source = field.named.iter().any(is_source);883x
96 let is_error = |f: &syn::Field| f.ident.as_ref().unwrap() == "error";1565x
97 let has_error = field.named.iter().any(is_error);883x
98
99 let variant_name = &variant.ident;883x
100 let debug_trace_arm = if has_source {883x
101 quote! {204x
102 #(#cfg_attrs)*
103 #name::#variant_name {_location, source, ..} => {
104 let level = source.debug_trace(f)?;
105 writeln!(f, "{level}: {self}, at {_location}")?;
106 Ok(level + 1)
107 }
108 }
109 } else if has_error {679x
110 quote! {285x
111 #(#cfg_attrs)*
112 #name::#variant_name {_location, error, ..} => {
113 writeln!(f, "0: {error}")?;
114 writeln!(f, "1: {self}, at {_location}")?;
115 Ok(2)
116 }
117 }
118 } else {
119 quote! {394x
120 #(#cfg_attrs)*
121 #name::#variant_name {_location, .. } => {
122 writeln!(f, "0: {self}, at {_location}")?;
123 Ok(1)
124 }
125 }
126 };
127 debug_trace_arms.push(debug_trace_arm);883x
128 }
129
130 quote! {91x
131 impl DebugTrace for #name {
132 #[inline(never)]
133 fn debug_trace(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<u32, ::std::fmt::Error> {
134 match self {
135 #(#debug_trace_arms)*
136 }
137 }
138 }
139
140 impl ::std::fmt::Debug for #name {
141 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
142 writeln!(f, "{self}")?;
143 DebugTrace::debug_trace(self, f)?;
144 Ok(())
145 }
146 }
147 }
148 .into()91x
149}91x
150