fack_codegen/
structure.rs

1//! The structure error type.
2
3use alloc::{format, vec::Vec};
4use proc_macro2::TokenStream;
5use syn::{FieldsNamed, FieldsUnnamed, Generics, Ident, Type};
6
7use super::common::{FieldRef, Format, ImportRoot, InlineOptions, Transparent};
8
9/// A structure that corresponds to an **error** type.
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11pub struct Structure {
12    /// The inline behavior for this structure.
13    pub inline_opts: Option<InlineOptions>,
14
15    /// The import root for this structure.
16    pub root_import: Option<ImportRoot>,
17
18    /// The name of this structure.
19    pub name_ident: Ident,
20
21    /// The generic parameters of this structure.
22    pub generics: Generics,
23
24    /// The field list of this structure.
25    pub field_list: FieldList,
26
27    /// The options for this structure.
28    pub options: StructureOptions,
29}
30
31/// The options for this structure in terms of its behavior.
32#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33#[allow(clippy::large_enum_variant)]
34pub enum StructureOptions {
35    /// The structure is a new, standalone error type.
36    Standalone {
37        /// The field to be used as the source of the error.
38        source_field: Option<FieldRef>,
39
40        /// The format parameters for this structure.
41        format_args: Format,
42    },
43
44    /// A structure that is transparent over some downstream type.
45    Transparent(Transparent),
46
47    /// A structure that is a forwarder to one of its fields.
48    Forward {
49        /// The field to be used as the source of the error.
50        source_field: Option<FieldRef>,
51
52        /// A reference to the field that is being forwarded.
53        field_ref: FieldRef,
54
55        /// The type of the forwarded field.
56        field_type: Type,
57
58        /// The format parameters for this structure.
59        format_args: Format,
60    },
61}
62
63/// The field list of a structure.
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub enum FieldList {
66    /// A named field list, which is a list of fields that are named.
67    Named(Vec<NamedField>),
68
69    /// An unnamed field list, which is a list of fields that are not named.
70    Unnamed(Vec<UnnamedField>),
71
72    /// A unit field list, which is equivalent to an absence of fields.
73    Unit,
74}
75
76impl FieldList {
77    /// Convert a target [`syn::Fields`] into a [`FieldList`].
78    #[inline]
79    pub fn raw(fields: &syn::Fields) -> syn::Result<Self> {
80        match fields {
81            syn::Fields::Named(FieldsNamed { named, .. }) => {
82                let field_list = named
83                    .iter()
84                    .map(|syn::Field { ident, ty, .. }| {
85                        let name = ident
86                            .clone()
87                            .ok_or_else(|| syn::Error::new_spanned(ident, "expected a named field"))?;
88
89                        let ty = ty.clone();
90
91                        Ok(NamedField { name, ty })
92                    })
93                    .collect::<syn::Result<Vec<_>>>()?;
94
95                Ok(FieldList::Named(field_list))
96            }
97            syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
98                let field_list = unnamed
99                    .iter()
100                    .map(|syn::Field { ty, .. }| {
101                        let ty = ty.clone();
102
103                        Ok(UnnamedField { ty })
104                    })
105                    .collect::<syn::Result<Vec<_>>>()?;
106
107                Ok(FieldList::Unnamed(field_list))
108            }
109            syn::Fields::Unit => Ok(FieldList::Unit),
110        }
111    }
112
113    /// Expect exactly one field in the target [`syn::Fields`].
114    pub fn exactly_one(fields: &syn::Fields) -> syn::Result<(FieldRef, Type)> {
115        const DEFAULT_FIELD: usize = 0;
116
117        match fields {
118            syn::Fields::Named(FieldsNamed { named, .. }) => {
119                if named.len() != 1 {
120                    return Err(syn::Error::new_spanned(fields, "expected exactly one field"));
121                }
122
123                let field = &named[DEFAULT_FIELD];
124
125                let name = field.ident.clone();
126
127                let ty = field.ty.clone();
128
129                Ok((name.map_or(FieldRef::Indexed(DEFAULT_FIELD), FieldRef::Named), ty))
130            }
131            syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
132                if unnamed.len() != 1 {
133                    return Err(syn::Error::new_spanned(fields, "expected exactly one field"));
134                }
135
136                let field = &unnamed[DEFAULT_FIELD];
137
138                let ty = field.ty.clone();
139
140                Ok((FieldRef::Indexed(DEFAULT_FIELD), ty))
141            }
142            syn::Fields::Unit => Err(syn::Error::new_spanned(fields, "expected at least one field")),
143        }
144    }
145
146    /// Compute an irrefutable pattern for this field list.
147    pub fn pattern(&self) -> syn::Result<TokenStream> {
148        match self {
149            FieldList::Named(named) => {
150                let pattern = named.iter().map(|field| {
151                    let name = &field.name;
152
153                    quote::quote! { ref #name }
154                });
155
156                Ok(quote::quote! { { #(#pattern),* } })
157            }
158            FieldList::Unnamed(unnamed) => {
159                let pattern = unnamed.iter().enumerate().map(|(index, _)| {
160                    let ident = Ident::new(&format!("_{index}"), proc_macro2::Span::call_site());
161
162                    quote::quote! { ref #ident }
163                });
164
165                Ok(quote::quote! { ( #(#pattern),* ) })
166            }
167            FieldList::Unit => Ok(quote::quote! {}),
168        }
169    }
170}
171
172/// A named field that is part of a structure.
173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174pub struct NamedField {
175    /// The name of the field.
176    name: Ident,
177
178    /// The type of the field.
179    ty: Type,
180}
181
182/// An unnamed field that is part of a structure.
183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
184pub struct UnnamedField {
185    /// The type of the field.
186    ty: Type,
187}