1use proc_macro2::Ident;
4#[cfg(not(feature = "nightly"))]
5use {
6 proc_macro2::{Span, TokenStream},
7 quote::{quote, ToTokens},
8 syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Meta, Result, Token, Variant},
9};
10
11#[cfg(not(feature = "nightly"))]
12use crate::Error;
13use crate::{Data, Incomparable, Trait};
14
15#[cfg_attr(test, derive(Debug))]
17#[allow(clippy::large_enum_variant)]
18pub enum Item<'a> {
19 Enum {
21 #[cfg(not(feature = "nightly"))]
22 discriminant: Discriminant,
24 ident: &'a Ident,
26 incomparable: Incomparable,
28 variants: Vec<Data<'a>>,
30 },
31 Item(Data<'a>),
33}
34
35impl Item<'_> {
36 pub fn ident(&self) -> &Ident {
38 match self {
39 Item::Item(data) => data.ident,
40 Item::Enum { ident, .. } => ident,
41 }
42 }
43
44 pub fn is_enum(&self) -> bool {
46 match self {
47 Item::Enum { .. } => true,
48 Item::Item(_) => false,
49 }
50 }
51
52 pub fn any_skip_trait(&self, trait_: Trait) -> bool {
54 match self {
55 Item::Item(data) => data.any_skip_trait(trait_),
56 Item::Enum { variants, .. } => variants.iter().any(|data| data.any_skip_trait(trait_)),
57 }
58 }
59
60 #[cfg(feature = "zeroize")]
62 pub fn any_fqs(&self) -> bool {
63 use crate::Either;
64
65 match self {
66 Item::Item(data) => match data.fields() {
67 Either::Left(fields) => fields.fields.iter().any(|field| field.attr.zeroize_fqs.0),
68 Either::Right(_) => false,
69 },
70 Item::Enum { variants, .. } => variants.iter().any(|data| match data.fields() {
71 Either::Left(fields) => fields.fields.iter().any(|field| field.attr.zeroize_fqs.0),
72 Either::Right(_) => false,
73 }),
74 }
75 }
76
77 pub fn is_empty(&self, trait_: Trait) -> bool {
80 match self {
81 Item::Enum { variants, .. } => variants.iter().all(|data| data.is_empty(trait_)),
82 Item::Item(data) => data.is_empty(trait_),
83 }
84 }
85
86 pub fn is_incomparable(&self) -> bool {
89 match self {
90 Item::Enum {
91 variants,
92 incomparable,
93 ..
94 } => {
95 incomparable.0.is_some()
96 || !variants.is_empty() && variants.iter().all(Data::is_incomparable)
97 }
98 Item::Item(data) => data.is_incomparable(),
99 }
100 }
101}
102
103#[derive(Clone, Copy)]
105#[cfg_attr(test, derive(Debug))]
106#[cfg(not(feature = "nightly"))]
107pub enum Discriminant {
108 Single,
110 Unit,
112 Data,
114 UnitRepr(Representation),
116 DataRepr(Representation),
118}
119
120#[cfg(not(feature = "nightly"))]
121impl Discriminant {
122 pub fn parse(attrs: &[Attribute], variants: &Punctuated<Variant, Token![,]>) -> Result<Self> {
124 if variants.len() == 1 {
125 return Ok(Self::Single);
126 }
127
128 let mut has_repr = None;
129
130 for attr in attrs {
131 if attr.path().is_ident("repr") {
132 if let Meta::List(list) = &attr.meta {
133 let list =
134 list.parse_args_with(Punctuated::<Ident, Token![,]>::parse_terminated)?;
135
136 for ident in list {
137 if let Some(repr) = Representation::parse(&ident) {
138 has_repr = Some(repr);
139 break;
140 } else if ident != "C" && ident != "Rust" && ident != "align" {
141 return Err(Error::repr_unknown(ident.span()));
142 }
143 }
144 } else {
145 unreachable!("found invalid `repr` attribute")
146 }
147 }
148 }
149
150 let is_unit = variants.iter().all(|variant| variant.fields.is_empty());
151
152 Ok(if let Some(repr) = has_repr {
153 if is_unit {
154 Self::UnitRepr(repr)
155 } else {
156 Self::DataRepr(repr)
157 }
158 } else if is_unit {
159 Self::Unit
160 } else {
161 let discriminant = variants
162 .iter()
163 .find_map(|variant| variant.discriminant.as_ref());
164
165 if let Some(discriminant) = discriminant {
166 return Err(Error::repr_discriminant_invalid(discriminant.1.span()));
167 }
168
169 Self::Data
170 })
171 }
172}
173
174#[derive(Clone, Copy)]
176#[cfg_attr(test, derive(Debug))]
177#[cfg(not(feature = "nightly"))]
178pub enum Representation {
179 U8,
181 U16,
183 U32,
185 U64,
187 U128,
189 USize,
191 I8,
193 I16,
195 I32,
197 I64,
199 I128,
201 ISize,
203}
204
205#[cfg(not(feature = "nightly"))]
206impl Representation {
207 fn parse(ident: &Ident) -> Option<Self> {
209 Some(if ident == "u8" {
210 Self::U8
211 } else if ident == "u16" {
212 Self::U16
213 } else if ident == "u32" {
214 Self::U32
215 } else if ident == "u64" {
216 Self::U64
217 } else if ident == "u128" {
218 Self::U128
219 } else if ident == "usize" {
220 Self::USize
221 } else if ident == "i8" {
222 Self::I8
223 } else if ident == "i16" {
224 Self::I16
225 } else if ident == "i32" {
226 Self::I32
227 } else if ident == "i64" {
228 Self::I64
229 } else if ident == "i128" {
230 Self::I128
231 } else if ident == "isize" {
232 Self::ISize
233 } else {
234 return None;
235 })
236 }
237
238 pub fn to_token(self) -> TokenStream {
240 let ident = match self {
241 Representation::U8 => "u8",
242 Representation::U16 => "u16",
243 Representation::U32 => "u32",
244 Representation::U64 => "u64",
245 Representation::U128 => "u128",
246 Representation::USize => "usize",
247 Representation::I8 => "i8",
248 Representation::I16 => "i16",
249 Representation::I32 => "i32",
250 Representation::I64 => "i64",
251 Representation::I128 => "i128",
252 Representation::ISize => "isize",
253 };
254
255 let ident = Ident::new(ident, Span::call_site());
256
257 quote! {::core::primitive::#ident}
258 }
259}
260
261#[cfg(not(feature = "nightly"))]
262impl ToTokens for Representation {
263 fn to_tokens(&self, tokens: &mut TokenStream) {
264 tokens.extend(self.to_token());
265 }
266}