inkwell_internals/
enum.rs
1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::fold::Fold;
4use syn::parse::{Error, Parse, ParseStream, Result};
5use syn::parse_quote;
6use syn::spanned::Spanned;
7use syn::{Arm, PatPath, Path};
8use syn::{Attribute, Ident, Variant};
9
10struct EnumVariant {
13 llvm_variant: Ident,
14 rust_variant: Ident,
15 attrs: Vec<Attribute>,
16}
17
18impl EnumVariant {
19 fn new(variant: &Variant) -> Self {
20 let rust_variant = variant.ident.clone();
21 let llvm_variant = Ident::new(&format!("LLVM{}", rust_variant), variant.span());
22 let mut attrs = variant.attrs.clone();
23 attrs.retain(|attr| !attr.path().is_ident("llvm_variant"));
24 Self {
25 llvm_variant,
26 rust_variant,
27 attrs,
28 }
29 }
30
31 fn with_name(variant: &Variant, mut llvm_variant: Ident) -> Self {
32 let rust_variant = variant.ident.clone();
33 llvm_variant.set_span(rust_variant.span());
34 let mut attrs = variant.attrs.clone();
35 attrs.retain(|attr| !attr.path().is_ident("llvm_variant"));
36 Self {
37 llvm_variant,
38 rust_variant,
39 attrs,
40 }
41 }
42}
43
44#[derive(Default)]
46struct EnumVariants {
47 variants: Vec<EnumVariant>,
48 error: Option<Error>,
49}
50
51impl EnumVariants {
52 #[inline]
53 fn len(&self) -> usize {
54 self.variants.len()
55 }
56
57 #[inline]
58 fn iter(&self) -> core::slice::Iter<'_, EnumVariant> {
59 self.variants.iter()
60 }
61
62 #[inline]
63 fn has_error(&self) -> bool {
64 self.error.is_some()
65 }
66
67 #[inline]
68 fn set_error(&mut self, err: &str, span: Span) {
69 self.error = Some(Error::new(span, err));
70 }
71
72 fn into_error(self) -> Error {
73 self.error.unwrap()
74 }
75}
76
77impl Fold for EnumVariants {
78 fn fold_variant(&mut self, mut variant: Variant) -> Variant {
79 use syn::Meta;
80
81 if self.has_error() {
82 return variant;
83 }
84
85 if let Some(attr) = variant.attrs.iter().find(|attr| attr.path().is_ident("llvm_variant")) {
87 if let Meta::List(meta) = &attr.meta {
89 if let Ok(Meta::Path(name)) = meta.parse_args() {
92 self.variants
93 .push(EnumVariant::with_name(&variant, name.get_ident().unwrap().clone()));
94 variant.attrs.retain(|attr| !attr.path().is_ident("llvm_variant"));
96 return variant;
97 }
98 }
99
100 self.set_error("expected #[llvm_variant(VARIANT_NAME)]", attr.span());
102 return variant;
103 }
104
105 self.variants.push(EnumVariant::new(&variant));
106 variant
107 }
108}
109
110pub struct LLVMEnumType {
112 name: Ident,
113 decl: syn::ItemEnum,
114 variants: EnumVariants,
115}
116
117impl Parse for LLVMEnumType {
118 fn parse(input: ParseStream) -> Result<Self> {
119 let decl = input.parse::<syn::ItemEnum>()?;
121 let name = decl.ident.clone();
122
123 let decl = crate::cfg::VersionFolder::fold_any(Fold::fold_item_enum, decl)?;
125
126 let mut variants = EnumVariants::default();
127 let decl = variants.fold_item_enum(decl);
128 if variants.has_error() {
129 return Err(variants.into_error());
130 }
131
132 Ok(Self { name, decl, variants })
133 }
134}
135
136pub fn llvm_enum(llvm_ty: Path, llvm_enum_type: LLVMEnumType) -> TokenStream {
137 let mut from_arms = Vec::with_capacity(llvm_enum_type.variants.len());
139 for variant in llvm_enum_type.variants.iter() {
140 let src_variant = variant.llvm_variant.clone();
141 let src_attrs: Vec<_> = variant
143 .attrs
144 .iter()
145 .filter(|&attr| !attr.meta.path().is_ident("doc"))
146 .collect();
147 let src_ty = llvm_ty.clone();
148 let dst_variant = variant.rust_variant.clone();
149 let dst_ty = llvm_enum_type.name.clone();
150
151 let pat = PatPath {
152 attrs: Vec::new(),
153 qself: None,
154 path: parse_quote!(#src_ty::#src_variant),
155 };
156
157 let arm: Arm = parse_quote! {
158 #(#src_attrs)*
159 #pat => { #dst_ty::#dst_variant }
160 };
161 from_arms.push(arm);
162 }
163
164 let mut to_arms = Vec::with_capacity(llvm_enum_type.variants.len());
166 for variant in llvm_enum_type.variants.iter() {
167 let src_variant = variant.rust_variant.clone();
168 let src_attrs: Vec<_> = variant
170 .attrs
171 .iter()
172 .filter(|&attr| !attr.meta.path().is_ident("doc"))
173 .collect();
174 let src_ty = llvm_enum_type.name.clone();
175 let dst_variant = variant.llvm_variant.clone();
176 let dst_ty = llvm_ty.clone();
177
178 let pat = PatPath {
179 attrs: Vec::new(),
180 qself: None,
181 path: parse_quote!(#src_ty::#src_variant),
182 };
183
184 let arm: Arm = parse_quote! {
185 #(#src_attrs)*
186 #pat => { #dst_ty::#dst_variant }
187 };
188 to_arms.push(arm);
189 }
190
191 let enum_ty = llvm_enum_type.name.clone();
192 let enum_decl = llvm_enum_type.decl;
193
194 quote! {
195 #enum_decl
196
197 impl #enum_ty {
198 fn new(src: #llvm_ty) -> Self {
199 match src {
200 #(#from_arms)*
201 }
202 }
203 }
204 impl From<#llvm_ty> for #enum_ty {
205 fn from(src: #llvm_ty) -> Self {
206 Self::new(src)
207 }
208 }
209 impl Into<#llvm_ty> for #enum_ty {
210 fn into(self) -> #llvm_ty {
211 match self {
212 #(#to_arms),*
213 }
214 }
215 }
216 }
217}