|
| 1 | +// Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | + |
| 3 | +use std::collections::HashSet; |
| 4 | + |
| 5 | +use proc_macro2::TokenStream; |
| 6 | +use quote::ToTokens; |
| 7 | +use syn::spanned::Spanned; |
| 8 | + |
| 9 | +mod boxed; |
| 10 | +#[allow(dead_code)] |
| 11 | +mod boxed_inline { |
| 12 | + #[derive(deluxe::ParseMetaItem)] |
| 13 | + pub struct BoxedInline { |
| 14 | + copy: Option<syn::Expr>, |
| 15 | + free: Option<syn::Expr>, |
| 16 | + init: Option<syn::Expr>, |
| 17 | + copy_into: Option<syn::Expr>, |
| 18 | + clear: Option<syn::Expr>, |
| 19 | + r#type: Option<syn::Expr>, |
| 20 | + #[deluxe(flatten)] |
| 21 | + args: super::WrapperArgs, |
| 22 | + } |
| 23 | +} |
| 24 | +#[allow(dead_code)] |
| 25 | +mod shared { |
| 26 | + #[derive(deluxe::ParseMetaItem)] |
| 27 | + pub struct Shared { |
| 28 | + r#ref: syn::Expr, |
| 29 | + unref: syn::Expr, |
| 30 | + r#type: Option<syn::Expr>, |
| 31 | + #[deluxe(flatten)] |
| 32 | + args: super::WrapperArgs, |
| 33 | + } |
| 34 | +} |
| 35 | +#[allow(dead_code)] |
| 36 | +mod object { |
| 37 | + #[derive(deluxe::ParseMetaItem)] |
| 38 | + pub struct Object { |
| 39 | + #[deluxe(append)] |
| 40 | + extends: Vec<syn::Path>, |
| 41 | + #[deluxe(append)] |
| 42 | + implements: Vec<syn::Path>, |
| 43 | + r#type: syn::Expr, |
| 44 | + #[deluxe(flatten)] |
| 45 | + args: super::WrapperArgs, |
| 46 | + } |
| 47 | +} |
| 48 | +#[allow(dead_code)] |
| 49 | +mod object_subclass { |
| 50 | + #[derive(deluxe::ParseMetaItem)] |
| 51 | + pub struct ObjectSubclass { |
| 52 | + #[deluxe(append)] |
| 53 | + extends: Vec<syn::Path>, |
| 54 | + #[deluxe(append)] |
| 55 | + implements: Vec<syn::Path>, |
| 56 | + #[deluxe(flatten)] |
| 57 | + args: super::WrapperArgs, |
| 58 | + } |
| 59 | +} |
| 60 | +#[allow(dead_code)] |
| 61 | +mod interface { |
| 62 | + #[derive(deluxe::ParseMetaItem)] |
| 63 | + pub struct Interface { |
| 64 | + #[deluxe(append)] |
| 65 | + requires: Vec<syn::Path>, |
| 66 | + r#type: syn::Expr, |
| 67 | + #[deluxe(flatten)] |
| 68 | + args: super::WrapperArgs, |
| 69 | + } |
| 70 | +} |
| 71 | +#[allow(dead_code)] |
| 72 | +mod object_interface { |
| 73 | + #[derive(deluxe::ParseMetaItem)] |
| 74 | + pub struct ObjectInterface { |
| 75 | + #[deluxe(append)] |
| 76 | + requires: Vec<syn::Path>, |
| 77 | + #[deluxe(flatten)] |
| 78 | + args: super::WrapperArgs, |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +#[derive(deluxe::ParseMetaItem)] |
| 83 | +struct WrapperArgs { |
| 84 | + #[deluxe( |
| 85 | + append, |
| 86 | + rename = skip_trait, |
| 87 | + map = |s: HashSet<syn::Ident>| s.into_iter().map(|s| s.to_string()).collect(), |
| 88 | + )] |
| 89 | + skipped_traits: HashSet<String>, |
| 90 | +} |
| 91 | + |
| 92 | +enum Member<'a> { |
| 93 | + Named(&'a syn::Ident), |
| 94 | + Unnamed(syn::Index), |
| 95 | +} |
| 96 | + |
| 97 | +impl<'a> Member<'a> { |
| 98 | + fn from_fields(fields: &'a syn::Fields) -> impl Iterator<Item = Member<'a>> { |
| 99 | + fields |
| 100 | + .iter() |
| 101 | + .enumerate() |
| 102 | + .map(|(index, field)| match &field.ident { |
| 103 | + Some(ident) => Member::Named(ident), |
| 104 | + None => Member::Unnamed(syn::Index { |
| 105 | + index: index as u32, |
| 106 | + span: field.ty.span(), |
| 107 | + }), |
| 108 | + }) |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +impl<'a> ToTokens for Member<'a> { |
| 113 | + #[inline] |
| 114 | + fn to_tokens(&self, tokens: &mut TokenStream) { |
| 115 | + match self { |
| 116 | + Self::Named(ident) => ident.to_tokens(tokens), |
| 117 | + Self::Unnamed(index) => index.to_tokens(tokens), |
| 118 | + } |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +fn construct_stmt( |
| 123 | + fields: &syn::Fields, |
| 124 | + first_func: impl FnOnce(Member<'_>) -> TokenStream, |
| 125 | + mut rest_func: impl FnMut(Member<'_>) -> TokenStream, |
| 126 | +) -> TokenStream { |
| 127 | + let mut first_func = Some(first_func); |
| 128 | + let members = fields.iter().enumerate().map(move |(index, f)| { |
| 129 | + let member = match &f.ident { |
| 130 | + Some(ident) => Member::Named(ident), |
| 131 | + None => Member::Unnamed(syn::Index { |
| 132 | + index: index as u32, |
| 133 | + span: f.ty.span(), |
| 134 | + }), |
| 135 | + }; |
| 136 | + let expr: TokenStream = if index == 0 { |
| 137 | + (first_func.take().unwrap())(member) |
| 138 | + } else { |
| 139 | + rest_func(member) |
| 140 | + }; |
| 141 | + match &f.ident { |
| 142 | + Some(ident) => quote::quote_spanned! { f.ty.span() => #ident: #expr }, |
| 143 | + None => expr, |
| 144 | + } |
| 145 | + }); |
| 146 | + match fields { |
| 147 | + syn::Fields::Named(_) => quote::quote! { Self { #(#members),* } }, |
| 148 | + syn::Fields::Unnamed(_) => quote::quote! { Self(#(#members),*) }, |
| 149 | + _ => unreachable!(), |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +#[inline] |
| 154 | +fn unique_lifetime(name: &str, generics: &syn::Generics) -> syn::Lifetime { |
| 155 | + let mut ident = String::from(name); |
| 156 | + while generics.lifetimes().any(|l| l.lifetime.ident == ident) { |
| 157 | + ident.push('_'); |
| 158 | + } |
| 159 | + ident.insert(0, '\''); |
| 160 | + syn::Lifetime::new(&ident, proc_macro2::Span::mixed_site()) |
| 161 | +} |
| 162 | + |
| 163 | +#[inline] |
| 164 | +fn insert_lifetime(name: &str, generics: &mut syn::Generics) -> syn::Lifetime { |
| 165 | + let lt = unique_lifetime(name, generics); |
| 166 | + generics.params.insert(0, syn::parse_quote! { #lt }); |
| 167 | + lt |
| 168 | +} |
| 169 | + |
| 170 | +fn get_first_field_type_param(fields: &syn::Fields, type_index: usize) -> syn::Result<&syn::Type> { |
| 171 | + fields |
| 172 | + .iter() |
| 173 | + .next() |
| 174 | + .ok_or_else(|| fields.span()) |
| 175 | + .and_then(|f| match &f.ty { |
| 176 | + syn::Type::Path(tp) => tp |
| 177 | + .path |
| 178 | + .segments |
| 179 | + .last() |
| 180 | + .ok_or_else(|| tp.span()) |
| 181 | + .and_then(|s| match &s.arguments { |
| 182 | + syn::PathArguments::AngleBracketed(ga) => ga |
| 183 | + .args |
| 184 | + .iter() |
| 185 | + .nth(type_index) |
| 186 | + .ok_or_else(|| ga.span()) |
| 187 | + .and_then(|a| match a { |
| 188 | + syn::GenericArgument::Type(t) => Ok(t), |
| 189 | + t => Err(t.span()), |
| 190 | + }), |
| 191 | + a => Err(a.span()), |
| 192 | + }), |
| 193 | + ty => Err(ty.span()), |
| 194 | + }) |
| 195 | + .map_err(|span| { |
| 196 | + syn::Error::new( |
| 197 | + span, |
| 198 | + format_args!("First field missing type argument {type_index}"), |
| 199 | + ) |
| 200 | + }) |
| 201 | +} |
| 202 | + |
| 203 | +#[inline] |
| 204 | +fn get_type_name(ty: &syn::Type) -> Option<&syn::Ident> { |
| 205 | + match ty { |
| 206 | + syn::Type::Path(tp) => tp.path.segments.last().map(|s| &s.ident), |
| 207 | + _ => None, |
| 208 | + } |
| 209 | +} |
| 210 | + |
| 211 | +fn add_repr_transparent(item: &mut syn::ItemStruct) { |
| 212 | + if item.fields.iter().skip(1).all(|f| { |
| 213 | + get_type_name(&f.ty) |
| 214 | + .map(|i| i == "PhantomData") |
| 215 | + .unwrap_or(false) |
| 216 | + }) { |
| 217 | + item.attrs.extend( |
| 218 | + syn::parse::Parser::parse_str(syn::Attribute::parse_outer, "#[repr(transparent)]") |
| 219 | + .unwrap(), |
| 220 | + ); |
| 221 | + } |
| 222 | +} |
| 223 | + |
| 224 | +pub fn impl_wrapper(attr: TokenStream, mut item: syn::ItemStruct) -> syn::Result<TokenStream> { |
| 225 | + let first_field = match item.fields.iter().next() { |
| 226 | + Some(f) => f, |
| 227 | + None => { |
| 228 | + return Err(syn::Error::new_spanned( |
| 229 | + item, |
| 230 | + "Wrapper struct must have at least one field", |
| 231 | + )); |
| 232 | + } |
| 233 | + }; |
| 234 | + let type_name = match get_type_name(&first_field.ty) { |
| 235 | + Some(n) => n, |
| 236 | + None => { |
| 237 | + return Err(syn::Error::new_spanned( |
| 238 | + item, |
| 239 | + "First field must be a type path", |
| 240 | + )) |
| 241 | + } |
| 242 | + }; |
| 243 | + |
| 244 | + let mut tokens = match type_name.to_string().as_str() { |
| 245 | + "Boxed" => deluxe::parse2::<boxed::Boxed>(attr)?.into_token_stream(&mut item)?, |
| 246 | + _ => return Err(syn::Error::new_spanned(type_name, "Unknown wrapper type")), |
| 247 | + }; |
| 248 | + item.to_tokens(&mut tokens); |
| 249 | + Ok(tokens) |
| 250 | +} |
0 commit comments