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