bare_err_tree_proc/
boiler.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7use std::iter;
8
9use proc_macro::TokenStream;
10use quote::quote;
11use syn::{Attribute, Generics, Ident, Meta};
12
13/// Derives intended to minimize friction introduced by the wrapper.
14///
15/// Derives transparent Error, Debug, From (both ways), and Deref(Mut).
16/// If known derivable traits are in scope, re-derives those as well.
17pub fn wrapper_boilerplate(
18    ident: &Ident,
19    generics: &Generics,
20    attrs: &[Attribute],
21    name_attribute: &Ident,
22) -> TokenStream {
23    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
24
25    // Core set
26    let universal: TokenStream = quote! {
27        #[automatically_derived]
28        impl #impl_generics ::core::error::Error for #name_attribute #ty_generics #where_clause {
29            fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
30                ::core::error::Error::source(&self.inner)
31            }
32        }
33
34        #[automatically_derived]
35        impl #impl_generics ::core::fmt::Debug for #name_attribute #ty_generics #where_clause {
36            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), ::core::fmt::Error> {
37                ::core::fmt::Debug::fmt(&self.inner, f)
38            }
39        }
40
41        #[automatically_derived]
42        impl #impl_generics ::core::fmt::Display for #name_attribute #ty_generics #where_clause {
43            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> Result<(), ::core::fmt::Error> {
44                ::core::fmt::Display::fmt(&self.inner, f)
45            }
46        }
47
48        #[automatically_derived]
49        impl #impl_generics ::core::convert::From<#ident #ty_generics> for #name_attribute #ty_generics #where_clause {
50            #[track_caller]
51            fn from(inner: #ident #ty_generics) -> Self {
52                Self::_tree(inner)
53            }
54        }
55
56        #[automatically_derived]
57        impl #impl_generics ::core::convert::From<#name_attribute #ty_generics> for #ident #ty_generics #where_clause {
58            fn from(value: #name_attribute #ty_generics) -> Self {
59                value.inner
60            }
61        }
62
63        #[automatically_derived]
64        impl #impl_generics ::core::ops::Deref for #name_attribute #ty_generics #where_clause {
65            type Target = #ident #ty_generics;
66            fn deref(&self) -> &Self::Target {
67                &self.inner
68            }
69        }
70
71        #[automatically_derived]
72        impl #impl_generics ::core::ops::DerefMut for #name_attribute #ty_generics #where_clause {
73            fn deref_mut(&mut self) -> &mut Self::Target {
74                &mut self.inner
75            }
76        }
77    }
78    .into();
79
80    // Look for viable extra derives
81    let mut extra_derive = Vec::new();
82    attrs.iter().for_each(|x| {
83        if let Meta::List(list) = &x.meta {
84            if list.path.get_ident().map(|x| x.to_string()) == Some("derive".to_string()) {
85                let _ = list.parse_nested_meta(|meta| {
86                    if let Some(ident) = meta.path.get_ident() {
87                        extra_derive.push(ident.clone());
88                    }
89                    Ok(())
90                });
91            }
92        }
93    });
94
95    // Optimistically added extra derives
96    let extra_derive_tokens =
97        extra_derive
98            .into_iter()
99            .map(|extra| match extra.to_string().to_lowercase().as_str() {
100                "eq" => quote! {
101                    #[automatically_derived]
102                    impl #impl_generics ::core::cmp::Eq for #name_attribute #ty_generics #where_clause {}
103                }
104                .into(),
105                "partialeq" => quote! {
106                    #[automatically_derived]
107                    impl #impl_generics ::core::cmp::PartialEq<#name_attribute #ty_generics> for #name_attribute #ty_generics #where_clause {
108                        fn eq(&self, other: &#name_attribute #ty_generics) -> bool {
109                            self.inner == other.inner
110                        }
111                    }
112                }
113                .into(),
114                "ord" => quote! {
115                    #[automatically_derived]
116                    impl #impl_generics ::core::cmp::Ord for #name_attribute #ty_generics #where_clause {
117                        fn ord(&self, other: &Self) -> bool {
118                            <#ident #ty_generics #where_clause as ::core::cmp::Ord>::ord(self.inner, other.inner)
119                        }
120                    }
121                }
122                .into(),
123                "partialord" => quote! {
124                    #[automatically_derived]
125                    impl #impl_generics ::core::cmp::PartialOrd for #name_attribute #ty_generics #where_clause {
126                        fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
127                            <#ident #ty_generics #where_clause as ::core::cmp::ParitalOrd>::partial_cmp(self.inner, other.inner)
128                        }
129                    }
130                }
131                .into(),
132                "clone" => quote! {
133                    #[automatically_derived]
134                    impl #impl_generics ::core::clone::Clone for #name_attribute #ty_generics #where_clause {
135                        fn clone(&self) -> Self {
136                            Self {
137                                inner: self.inner.clone(),
138                                _err_tree_pkg: self._err_tree_pkg.clone()
139                            }
140                        }
141                    }
142                }
143                .into(),
144                "hash" => quote! {
145                    #[automatically_derived]
146                    impl #impl_generics ::core::hash::Hash for #name_attribute #ty_generics #where_clause {
147                        fn hash<H>(&self, state: &mut H)
148                            where H: ::core::hash::Hasher
149                        {
150                            self.inner.hash(state)
151                        }
152                    }
153                }
154                .into(),
155                "default" => quote! {
156                    #[automatically_derived]
157                    impl #impl_generics ::core::default::Default for #name_attribute #ty_generics #where_clause {
158                        #[track_caller]
159                        fn default() -> Self {
160                            Self {
161                                inner: #ident ::default(),
162                                _err_tree_pkg: ::bare_err_tree::ErrTreePkg::default(),
163                            }
164                        }
165                    }
166                }
167                .into(),
168                _ => quote! {}.into(),
169            });
170
171    TokenStream::from_iter(iter::once(universal).chain(extra_derive_tokens))
172}