fack_codegen/
structure.rs1use 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#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11pub struct Structure {
12 pub inline_opts: Option<InlineOptions>,
14
15 pub root_import: Option<ImportRoot>,
17
18 pub name_ident: Ident,
20
21 pub generics: Generics,
23
24 pub field_list: FieldList,
26
27 pub options: StructureOptions,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33#[allow(clippy::large_enum_variant)]
34pub enum StructureOptions {
35 Standalone {
37 source_field: Option<FieldRef>,
39
40 format_args: Format,
42 },
43
44 Transparent(Transparent),
46
47 Forward {
49 source_field: Option<FieldRef>,
51
52 field_ref: FieldRef,
54
55 field_type: Type,
57
58 format_args: Format,
60 },
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub enum FieldList {
66 Named(Vec<NamedField>),
68
69 Unnamed(Vec<UnnamedField>),
71
72 Unit,
74}
75
76impl FieldList {
77 #[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 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 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174pub struct NamedField {
175 name: Ident,
177
178 ty: Type,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Hash)]
184pub struct UnnamedField {
185 ty: Type,
187}