1use crate::syntax::{derive, Enum, Struct, Trait}; 2use proc_macro2::{Ident, Span, TokenStream}; 3use quote::{quote, quote_spanned, ToTokens}; 4 5pub use crate::syntax::derive::*; 6 7pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream { 8 let mut expanded = TokenStream::new(); 9 let mut traits = Vec::new(); 10 11 for derive in &strct.derives { 12 let span = derive.span; 13 match derive.what { 14 Trait::Copy => expanded.extend(struct_copy(strct, span)), 15 Trait::Clone => expanded.extend(struct_clone(strct, span)), 16 Trait::Debug => expanded.extend(struct_debug(strct, span)), 17 Trait::Default => expanded.extend(struct_default(strct, span)), 18 Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)), 19 Trait::ExternType => unreachable!(), 20 Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)), 21 Trait::Ord => expanded.extend(struct_ord(strct, span)), 22 Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)), 23 Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)), 24 Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)), 25 Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)), 26 } 27 } 28 29 if traits.is_empty() { 30 *actual_derives = None; 31 } else { 32 *actual_derives = Some(quote!(#[derive(#(#traits),*)])); 33 } 34 35 expanded 36} 37 38pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream { 39 let mut expanded = TokenStream::new(); 40 let mut traits = Vec::new(); 41 let mut has_copy = false; 42 let mut has_clone = false; 43 let mut has_eq = false; 44 let mut has_partial_eq = false; 45 46 for derive in &enm.derives { 47 let span = derive.span; 48 match derive.what { 49 Trait::Copy => { 50 expanded.extend(enum_copy(enm, span)); 51 has_copy = true; 52 } 53 Trait::Clone => { 54 expanded.extend(enum_clone(enm, span)); 55 has_clone = true; 56 } 57 Trait::Debug => expanded.extend(enum_debug(enm, span)), 58 Trait::Default => unreachable!(), 59 Trait::Eq => { 60 traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)); 61 has_eq = true; 62 } 63 Trait::ExternType => unreachable!(), 64 Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)), 65 Trait::Ord => expanded.extend(enum_ord(enm, span)), 66 Trait::PartialEq => { 67 traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)); 68 has_partial_eq = true; 69 } 70 Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)), 71 Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)), 72 Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)), 73 } 74 } 75 76 let span = enm.name.rust.span(); 77 if !has_copy { 78 expanded.extend(enum_copy(enm, span)); 79 } 80 if !has_clone { 81 expanded.extend(enum_clone(enm, span)); 82 } 83 if !has_eq { 84 // Required to be derived in order for the enum's "variants" to be 85 // usable in patterns. 86 traits.push(quote!(::cxx::core::cmp::Eq)); 87 } 88 if !has_partial_eq { 89 traits.push(quote!(::cxx::core::cmp::PartialEq)); 90 } 91 92 *actual_derives = Some(quote!(#[derive(#(#traits),*)])); 93 94 expanded 95} 96 97fn struct_copy(strct: &Struct, span: Span) -> TokenStream { 98 let ident = &strct.name.rust; 99 let generics = &strct.generics; 100 101 quote_spanned! {span=> 102 impl #generics ::cxx::core::marker::Copy for #ident #generics {} 103 } 104} 105 106fn struct_clone(strct: &Struct, span: Span) -> TokenStream { 107 let ident = &strct.name.rust; 108 let generics = &strct.generics; 109 110 let body = if derive::contains(&strct.derives, Trait::Copy) { 111 quote!(*self) 112 } else { 113 let fields = strct.fields.iter().map(|field| &field.name.rust); 114 let values = strct.fields.iter().map(|field| { 115 let ident = &field.name.rust; 116 let ty = field.ty.to_token_stream(); 117 let span = ty.into_iter().last().unwrap().span(); 118 quote_spanned!(span=> &self.#ident) 119 }); 120 quote_spanned!(span=> #ident { 121 #(#fields: ::cxx::core::clone::Clone::clone(#values),)* 122 }) 123 }; 124 125 quote_spanned! {span=> 126 #[allow(clippy::expl_impl_clone_on_copy)] 127 impl #generics ::cxx::core::clone::Clone for #ident #generics { 128 fn clone(&self) -> Self { 129 #body 130 } 131 } 132 } 133} 134 135fn struct_debug(strct: &Struct, span: Span) -> TokenStream { 136 let ident = &strct.name.rust; 137 let generics = &strct.generics; 138 let struct_name = ident.to_string(); 139 let fields = strct.fields.iter().map(|field| &field.name.rust); 140 let field_names = fields.clone().map(Ident::to_string); 141 142 quote_spanned! {span=> 143 impl #generics ::cxx::core::fmt::Debug for #ident #generics { 144 fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { 145 formatter.debug_struct(#struct_name) 146 #(.field(#field_names, &self.#fields))* 147 .finish() 148 } 149 } 150 } 151} 152 153fn struct_default(strct: &Struct, span: Span) -> TokenStream { 154 let ident = &strct.name.rust; 155 let generics = &strct.generics; 156 let fields = strct.fields.iter().map(|field| &field.name.rust); 157 158 quote_spanned! {span=> 159 #[allow(clippy::derivable_impls)] // different spans than the derived impl 160 impl #generics ::cxx::core::default::Default for #ident #generics { 161 fn default() -> Self { 162 #ident { 163 #( 164 #fields: ::cxx::core::default::Default::default(), 165 )* 166 } 167 } 168 } 169 } 170} 171 172fn struct_ord(strct: &Struct, span: Span) -> TokenStream { 173 let ident = &strct.name.rust; 174 let generics = &strct.generics; 175 let fields = strct.fields.iter().map(|field| &field.name.rust); 176 177 quote_spanned! {span=> 178 impl #generics ::cxx::core::cmp::Ord for #ident #generics { 179 fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering { 180 #( 181 match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) { 182 ::cxx::core::cmp::Ordering::Equal => {} 183 ordering => return ordering, 184 } 185 )* 186 ::cxx::core::cmp::Ordering::Equal 187 } 188 } 189 } 190} 191 192fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream { 193 let ident = &strct.name.rust; 194 let generics = &strct.generics; 195 196 let body = if derive::contains(&strct.derives, Trait::Ord) { 197 quote! { 198 ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other)) 199 } 200 } else { 201 let fields = strct.fields.iter().map(|field| &field.name.rust); 202 quote! { 203 #( 204 match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) { 205 ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {} 206 ordering => return ordering, 207 } 208 )* 209 ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) 210 } 211 }; 212 213 quote_spanned! {span=> 214 impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics { 215 fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> { 216 #body 217 } 218 } 219 } 220} 221 222fn enum_copy(enm: &Enum, span: Span) -> TokenStream { 223 let ident = &enm.name.rust; 224 225 quote_spanned! {span=> 226 impl ::cxx::core::marker::Copy for #ident {} 227 } 228} 229 230fn enum_clone(enm: &Enum, span: Span) -> TokenStream { 231 let ident = &enm.name.rust; 232 233 quote_spanned! {span=> 234 #[allow(clippy::expl_impl_clone_on_copy)] 235 impl ::cxx::core::clone::Clone for #ident { 236 fn clone(&self) -> Self { 237 *self 238 } 239 } 240 } 241} 242 243fn enum_debug(enm: &Enum, span: Span) -> TokenStream { 244 let ident = &enm.name.rust; 245 let variants = enm.variants.iter().map(|variant| { 246 let variant = &variant.name.rust; 247 let name = variant.to_string(); 248 quote_spanned! {span=> 249 #ident::#variant => formatter.write_str(#name), 250 } 251 }); 252 let fallback = format!("{}({{}})", ident); 253 254 quote_spanned! {span=> 255 impl ::cxx::core::fmt::Debug for #ident { 256 fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result { 257 match *self { 258 #(#variants)* 259 _ => ::cxx::core::write!(formatter, #fallback, self.repr), 260 } 261 } 262 } 263 } 264} 265 266fn enum_ord(enm: &Enum, span: Span) -> TokenStream { 267 let ident = &enm.name.rust; 268 269 quote_spanned! {span=> 270 impl ::cxx::core::cmp::Ord for #ident { 271 fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering { 272 ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr) 273 } 274 } 275 } 276} 277 278fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream { 279 let ident = &enm.name.rust; 280 281 quote_spanned! {span=> 282 impl ::cxx::core::cmp::PartialOrd for #ident { 283 fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> { 284 ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr) 285 } 286 } 287 } 288} 289