| 1 | // This file is part of ICU4X. For terms of use, please see the file |
| 2 | // called LICENSE at the top level of the ICU4X source tree |
| 3 | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
| 4 | |
| 5 | use crate::utils::{self, FieldInfo}; |
| 6 | use proc_macro2::Span; |
| 7 | use proc_macro2::TokenStream as TokenStream2; |
| 8 | use quote::{quote, ToTokens}; |
| 9 | use syn::spanned::Spanned; |
| 10 | use syn::{ |
| 11 | parse_quote, Data, DeriveInput, Error, Field, Fields, GenericArgument, Ident, Lifetime, |
| 12 | PathArguments, Type, TypePath, |
| 13 | }; |
| 14 | |
| 15 | pub fn make_varule_impl(ule_name: Ident, mut input: DeriveInput) -> TokenStream2 { |
| 16 | if input.generics.type_params().next().is_some() |
| 17 | || input.generics.const_params().next().is_some() |
| 18 | || input.generics.lifetimes().count() > 1 |
| 19 | { |
| 20 | return Error::new( |
| 21 | input.generics.span(), |
| 22 | "#[make_varule] must be applied to a struct without any type or const parameters and at most one lifetime" , |
| 23 | ) |
| 24 | .to_compile_error(); |
| 25 | } |
| 26 | |
| 27 | let sp = input.span(); |
| 28 | let attrs = match utils::extract_attributes_common(&mut input.attrs, sp, true) { |
| 29 | Ok(val) => val, |
| 30 | Err(e) => return e.to_compile_error(), |
| 31 | }; |
| 32 | |
| 33 | let lt = input.generics.lifetimes().next(); |
| 34 | |
| 35 | if let Some(lt) = lt { |
| 36 | if lt.colon_token.is_some() || !lt.bounds.is_empty() { |
| 37 | return Error::new( |
| 38 | input.generics.span(), |
| 39 | "#[make_varule] must be applied to a struct without lifetime bounds" , |
| 40 | ) |
| 41 | .to_compile_error(); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | let lt = lt.map(|l| &l.lifetime); |
| 46 | |
| 47 | let name = &input.ident; |
| 48 | let input_span = input.span(); |
| 49 | |
| 50 | let fields = match input.data { |
| 51 | Data::Struct(ref mut s) => &mut s.fields, |
| 52 | _ => { |
| 53 | return Error::new(input.span(), "#[make_varule] must be applied to a struct" ) |
| 54 | .to_compile_error(); |
| 55 | } |
| 56 | }; |
| 57 | |
| 58 | if fields.is_empty() { |
| 59 | return Error::new( |
| 60 | input.span(), |
| 61 | "#[make_varule] must be applied to a struct with at least one field" , |
| 62 | ) |
| 63 | .to_compile_error(); |
| 64 | } |
| 65 | |
| 66 | let mut sized_fields = vec![]; |
| 67 | let mut unsized_fields = vec![]; |
| 68 | |
| 69 | let mut custom_varule_idents = vec![]; |
| 70 | |
| 71 | for field in fields.iter_mut() { |
| 72 | match utils::extract_field_attributes(&mut field.attrs) { |
| 73 | Ok(i) => custom_varule_idents.push(i), |
| 74 | Err(e) => return e.to_compile_error(), |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | for (i, field) in fields.iter().enumerate() { |
| 79 | match UnsizedField::new(field, i, custom_varule_idents[i].clone()) { |
| 80 | Ok(o) => unsized_fields.push(o), |
| 81 | Err(_) => sized_fields.push(FieldInfo::new_for_field(field, i)), |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | if unsized_fields.is_empty() { |
| 86 | let last_field_index = fields.len() - 1; |
| 87 | let last_field = fields.iter().next_back().unwrap(); |
| 88 | |
| 89 | let e = UnsizedField::new( |
| 90 | last_field, |
| 91 | last_field_index, |
| 92 | custom_varule_idents[last_field_index].clone(), |
| 93 | ) |
| 94 | .unwrap_err(); |
| 95 | return Error::new(last_field.span(), e).to_compile_error(); |
| 96 | } |
| 97 | |
| 98 | if unsized_fields[0].field.index != fields.len() - unsized_fields.len() |
| 99 | && unsized_fields[0].field.field.ident.is_none() |
| 100 | { |
| 101 | return Error::new( |
| 102 | unsized_fields.first().unwrap().field.field.span(), |
| 103 | "#[make_varule] requires its unsized fields to be at the end for tuple structs" , |
| 104 | ) |
| 105 | .to_compile_error(); |
| 106 | } |
| 107 | |
| 108 | let unsized_field_info = UnsizedFields::new(unsized_fields); |
| 109 | |
| 110 | let mut field_inits = crate::ule::make_ule_fields(&sized_fields); |
| 111 | let last_field_ule = unsized_field_info.varule_ty(); |
| 112 | |
| 113 | let setter = unsized_field_info.varule_setter(); |
| 114 | let vis = &unsized_field_info.varule_vis(); |
| 115 | field_inits.push(quote!(#vis #setter #last_field_ule)); |
| 116 | |
| 117 | let semi = utils::semi_for(fields); |
| 118 | let repr_attr = utils::repr_for(fields); |
| 119 | let field_inits = utils::wrap_field_inits(&field_inits, fields); |
| 120 | let vis = &input.vis; |
| 121 | |
| 122 | let doc = format!( |
| 123 | "[`VarULE`](zerovec::ule::VarULE) type for [` {name}`]. See [` {name}`] for documentation." |
| 124 | ); |
| 125 | let varule_struct: DeriveInput = parse_quote!( |
| 126 | #[repr(#repr_attr)] |
| 127 | #[doc = #doc] |
| 128 | #[allow(missing_docs)] |
| 129 | #vis struct #ule_name #field_inits #semi |
| 130 | ); |
| 131 | |
| 132 | let derived = crate::varule::derive_impl(&varule_struct, unsized_field_info.varule_validator()); |
| 133 | |
| 134 | let maybe_lt_bound = lt.as_ref().map(|lt| quote!(<#lt>)); |
| 135 | |
| 136 | let encode_impl = make_encode_impl( |
| 137 | &sized_fields, |
| 138 | &unsized_field_info, |
| 139 | name, |
| 140 | &ule_name, |
| 141 | &maybe_lt_bound, |
| 142 | ); |
| 143 | |
| 144 | let zf_impl = make_zf_impl( |
| 145 | &sized_fields, |
| 146 | &unsized_field_info, |
| 147 | fields, |
| 148 | name, |
| 149 | &ule_name, |
| 150 | lt, |
| 151 | input_span, |
| 152 | ); |
| 153 | |
| 154 | let eq_impl = quote!( |
| 155 | impl core::cmp::PartialEq for #ule_name { |
| 156 | fn eq(&self, other: &Self) -> bool { |
| 157 | // The VarULE invariants allow us to assume that equality is byte equality |
| 158 | // in non-safety-critical contexts |
| 159 | <Self as zerovec::ule::VarULE>::as_byte_slice(&self) |
| 160 | == <Self as zerovec::ule::VarULE>::as_byte_slice(&other) |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | impl core::cmp::Eq for #ule_name {} |
| 165 | ); |
| 166 | |
| 167 | let zerofrom_fq_path = |
| 168 | quote!(<#name as zerovec::__zerovec_internal_reexport::ZeroFrom<#ule_name>>); |
| 169 | |
| 170 | let maybe_ord_impls = if attrs.skip_ord { |
| 171 | quote!() |
| 172 | } else { |
| 173 | quote!( |
| 174 | impl core::cmp::PartialOrd for #ule_name { |
| 175 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { |
| 176 | Some(self.cmp(other)) |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | impl core::cmp::Ord for #ule_name { |
| 181 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { |
| 182 | let this = #zerofrom_fq_path::zero_from(self); |
| 183 | let other = #zerofrom_fq_path::zero_from(other); |
| 184 | <#name as core::cmp::Ord>::cmp(&this, &other) |
| 185 | } |
| 186 | } |
| 187 | ) |
| 188 | }; |
| 189 | |
| 190 | let maybe_debug = if attrs.debug { |
| 191 | quote!( |
| 192 | impl core::fmt::Debug for #ule_name { |
| 193 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 194 | let this = #zerofrom_fq_path::zero_from(self); |
| 195 | <#name as core::fmt::Debug>::fmt(&this, f) |
| 196 | } |
| 197 | } |
| 198 | ) |
| 199 | } else { |
| 200 | quote!() |
| 201 | }; |
| 202 | |
| 203 | let zmkv = if attrs.skip_kv { |
| 204 | quote!() |
| 205 | } else { |
| 206 | quote!( |
| 207 | impl<'a> zerovec::maps::ZeroMapKV<'a> for #ule_name { |
| 208 | type Container = zerovec::VarZeroVec<'a, #ule_name>; |
| 209 | type Slice = zerovec::VarZeroSlice<#ule_name>; |
| 210 | type GetType = #ule_name; |
| 211 | type OwnedType = zerovec::__zerovec_internal_reexport::boxed::Box<#ule_name>; |
| 212 | } |
| 213 | ) |
| 214 | }; |
| 215 | |
| 216 | let serde_path = quote!(zerovec::__zerovec_internal_reexport::serde); |
| 217 | |
| 218 | let maybe_ser = if attrs.serialize { |
| 219 | quote!( |
| 220 | impl #serde_path::Serialize for #ule_name { |
| 221 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: #serde_path::Serializer { |
| 222 | let this = #zerofrom_fq_path::zero_from(self); |
| 223 | <#name as #serde_path::Serialize>::serialize(&this, serializer) |
| 224 | } |
| 225 | } |
| 226 | ) |
| 227 | } else { |
| 228 | quote!() |
| 229 | }; |
| 230 | |
| 231 | let maybe_de = if attrs.deserialize { |
| 232 | quote!( |
| 233 | impl<'de> #serde_path::Deserialize<'de> for zerovec::__zerovec_internal_reexport::boxed::Box<#ule_name> { |
| 234 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: #serde_path::Deserializer<'de> { |
| 235 | let this = <#name as #serde_path::Deserialize>::deserialize(deserializer)?; |
| 236 | Ok(zerovec::ule::encode_varule_to_box(&this)) |
| 237 | } |
| 238 | } |
| 239 | ) |
| 240 | } else { |
| 241 | quote!() |
| 242 | }; |
| 243 | |
| 244 | let maybe_hash = if attrs.hash { |
| 245 | quote!( |
| 246 | #[allow(clippy::derive_hash_xor_eq)] |
| 247 | impl core::hash::Hash for #ule_name { |
| 248 | fn hash<H>(&self, state: &mut H) where H: core::hash::Hasher { |
| 249 | state.write(<#ule_name as zerovec::ule::VarULE>::as_byte_slice(&self)); |
| 250 | } |
| 251 | } |
| 252 | ) |
| 253 | } else { |
| 254 | quote!() |
| 255 | }; |
| 256 | |
| 257 | let maybe_multi_getters = if let Some(getters) = unsized_field_info.maybe_multi_getters() { |
| 258 | quote! { |
| 259 | impl #ule_name { |
| 260 | #getters |
| 261 | } |
| 262 | } |
| 263 | } else { |
| 264 | quote!() |
| 265 | }; |
| 266 | |
| 267 | quote!( |
| 268 | #input |
| 269 | |
| 270 | #varule_struct |
| 271 | |
| 272 | #maybe_multi_getters |
| 273 | |
| 274 | #encode_impl |
| 275 | |
| 276 | #zf_impl |
| 277 | |
| 278 | #derived |
| 279 | |
| 280 | #maybe_ord_impls |
| 281 | |
| 282 | #eq_impl |
| 283 | |
| 284 | #zmkv |
| 285 | |
| 286 | #maybe_ser |
| 287 | |
| 288 | #maybe_de |
| 289 | |
| 290 | #maybe_debug |
| 291 | |
| 292 | #maybe_hash |
| 293 | ) |
| 294 | } |
| 295 | |
| 296 | fn make_zf_impl( |
| 297 | sized_fields: &[FieldInfo], |
| 298 | unsized_field_info: &UnsizedFields, |
| 299 | fields: &Fields, |
| 300 | name: &Ident, |
| 301 | ule_name: &Ident, |
| 302 | maybe_lt: Option<&Lifetime>, |
| 303 | span: Span, |
| 304 | ) -> TokenStream2 { |
| 305 | if !unsized_field_info.has_zf() { |
| 306 | return quote!(); |
| 307 | } |
| 308 | |
| 309 | let lt = if let Some(ref lt) = maybe_lt { |
| 310 | lt |
| 311 | } else { |
| 312 | return Error::new( |
| 313 | span, |
| 314 | "Can only generate ZeroFrom impls for types with lifetimes" , |
| 315 | ) |
| 316 | .to_compile_error(); |
| 317 | }; |
| 318 | |
| 319 | let mut field_inits = sized_fields |
| 320 | .iter() |
| 321 | .map(|f| { |
| 322 | let ty = &f.field.ty; |
| 323 | let accessor = &f.accessor; |
| 324 | let setter = f.setter(); |
| 325 | quote!(#setter <#ty as zerovec::ule::AsULE>::from_unaligned(other.#accessor)) |
| 326 | }) |
| 327 | .collect::<Vec<_>>(); |
| 328 | |
| 329 | unsized_field_info.push_zf_setters(lt, &mut field_inits); |
| 330 | |
| 331 | let field_inits = utils::wrap_field_inits(&field_inits, fields); |
| 332 | let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom); |
| 333 | quote!( |
| 334 | impl <#lt> #zerofrom_trait <#lt, #ule_name> for #name <#lt> { |
| 335 | fn zero_from(other: &#lt #ule_name) -> Self { |
| 336 | Self #field_inits |
| 337 | } |
| 338 | } |
| 339 | ) |
| 340 | } |
| 341 | |
| 342 | fn make_encode_impl( |
| 343 | sized_fields: &[FieldInfo], |
| 344 | unsized_field_info: &UnsizedFields, |
| 345 | name: &Ident, |
| 346 | ule_name: &Ident, |
| 347 | maybe_lt_bound: &Option<TokenStream2>, |
| 348 | ) -> TokenStream2 { |
| 349 | let mut lengths = vec![]; |
| 350 | |
| 351 | for field in sized_fields { |
| 352 | let ty = &field.field.ty; |
| 353 | lengths.push(quote!(::core::mem::size_of::<<#ty as zerovec::ule::AsULE>::ULE>())); |
| 354 | } |
| 355 | |
| 356 | let (encoders, remaining_offset) = utils::generate_per_field_offsets( |
| 357 | sized_fields, |
| 358 | true, |
| 359 | |field, prev_offset_ident, size_ident| { |
| 360 | let ty = &field.field.ty; |
| 361 | let accessor = &field.accessor; |
| 362 | quote!( |
| 363 | #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid indices |
| 364 | let out = &mut dst[#prev_offset_ident .. #prev_offset_ident + #size_ident]; |
| 365 | let unaligned = zerovec::ule::AsULE::to_unaligned(self.#accessor); |
| 366 | let unaligned_slice = &[unaligned]; |
| 367 | let src = <<#ty as zerovec::ule::AsULE>::ULE as zerovec::ule::ULE>::as_byte_slice(unaligned_slice); |
| 368 | out.copy_from_slice(src); |
| 369 | ) |
| 370 | }, |
| 371 | ); |
| 372 | |
| 373 | let last_encode_len = unsized_field_info.encode_len(); |
| 374 | let last_encode_write = unsized_field_info.encode_write(quote!(out)); |
| 375 | quote!( |
| 376 | unsafe impl #maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for #name #maybe_lt_bound { |
| 377 | // Safety: unimplemented as the other two are implemented |
| 378 | fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { |
| 379 | unreachable!("other two methods implemented" ) |
| 380 | } |
| 381 | |
| 382 | // Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms |
| 383 | fn encode_var_ule_len(&self) -> usize { |
| 384 | #(#lengths +)* #last_encode_len |
| 385 | } |
| 386 | |
| 387 | // Safety: converts each element to ULE form and writes them in sequence |
| 388 | fn encode_var_ule_write(&self, mut dst: &mut [u8]) { |
| 389 | debug_assert_eq!(self.encode_var_ule_len(), dst.len()); |
| 390 | #encoders |
| 391 | |
| 392 | #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid remainder |
| 393 | let out = &mut dst[#remaining_offset..]; |
| 394 | #last_encode_write |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | // This second impl exists to allow for using EncodeAsVarULE without cloning |
| 399 | // |
| 400 | // A blanket impl cannot exist without coherence issues |
| 401 | unsafe impl #maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for &'_ #name #maybe_lt_bound { |
| 402 | // Safety: unimplemented as the other two are implemented |
| 403 | fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { |
| 404 | unreachable!("other two methods implemented" ) |
| 405 | } |
| 406 | |
| 407 | // Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms |
| 408 | fn encode_var_ule_len(&self) -> usize { |
| 409 | (**self).encode_var_ule_len() |
| 410 | } |
| 411 | |
| 412 | // Safety: converts each element to ULE form and writes them in sequence |
| 413 | fn encode_var_ule_write(&self, mut dst: &mut [u8]) { |
| 414 | (**self).encode_var_ule_write(dst) |
| 415 | } |
| 416 | } |
| 417 | ) |
| 418 | } |
| 419 | |
| 420 | /// Represents a VarULE-compatible type that would typically |
| 421 | /// be found behind a `Cow<'a, _>` in the last field, and is represented |
| 422 | /// roughly the same in owned and borrowed versions |
| 423 | #[derive (Copy, Clone, Debug)] |
| 424 | enum OwnULETy<'a> { |
| 425 | /// [T] where T: AsULE<ULE = Self> |
| 426 | Slice(&'a Type), |
| 427 | /// str |
| 428 | Str, |
| 429 | } |
| 430 | |
| 431 | /// Represents the type of the last field of the struct |
| 432 | #[derive (Clone, Debug)] |
| 433 | enum UnsizedFieldKind<'a> { |
| 434 | Cow(OwnULETy<'a>), |
| 435 | ZeroVec(&'a Type), |
| 436 | VarZeroVec(&'a Type), |
| 437 | /// Custom VarULE type, and the identifier corresponding to the VarULE type |
| 438 | Custom(&'a TypePath, Ident), |
| 439 | |
| 440 | // Generally you should be using the above ones for maximum zero-copy, but these will still work |
| 441 | Growable(OwnULETy<'a>), |
| 442 | Boxed(OwnULETy<'a>), |
| 443 | Ref(OwnULETy<'a>), |
| 444 | } |
| 445 | |
| 446 | #[derive (Clone, Debug)] |
| 447 | struct UnsizedField<'a> { |
| 448 | kind: UnsizedFieldKind<'a>, |
| 449 | field: FieldInfo<'a>, |
| 450 | } |
| 451 | |
| 452 | struct UnsizedFields<'a> { |
| 453 | fields: Vec<UnsizedField<'a>>, |
| 454 | } |
| 455 | |
| 456 | impl<'a> UnsizedFields<'a> { |
| 457 | fn new(fields: Vec<UnsizedField<'a>>) -> Self { |
| 458 | assert!(!fields.is_empty(), "Must have at least one unsized field" ); |
| 459 | Self { fields } |
| 460 | } |
| 461 | |
| 462 | // Get the corresponding VarULE type that can store all of these |
| 463 | fn varule_ty(&self) -> TokenStream2 { |
| 464 | if self.fields.len() == 1 { |
| 465 | self.fields[0].kind.varule_ty() |
| 466 | } else { |
| 467 | quote!(zerovec::ule::MultiFieldsULE) |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | // Get the accessor field name in the VarULE type |
| 472 | fn varule_accessor(&self) -> TokenStream2 { |
| 473 | if self.fields.len() == 1 { |
| 474 | self.fields[0].field.accessor.clone() |
| 475 | } else if self.fields[0].field.field.ident.is_some() { |
| 476 | quote!(unsized_fields) |
| 477 | } else { |
| 478 | // first unsized field |
| 479 | self.fields[0].field.accessor.clone() |
| 480 | } |
| 481 | } |
| 482 | |
| 483 | // Get the setter for this type for use in struct definition/creation syntax |
| 484 | fn varule_setter(&self) -> TokenStream2 { |
| 485 | if self.fields.len() == 1 { |
| 486 | self.fields[0].field.setter() |
| 487 | } else if self.fields[0].field.field.ident.is_some() { |
| 488 | quote!(unsized_fields: ) |
| 489 | } else { |
| 490 | quote!() |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | fn varule_vis(&self) -> TokenStream2 { |
| 495 | if self.fields.len() == 1 { |
| 496 | self.fields[0].field.field.vis.to_token_stream() |
| 497 | } else { |
| 498 | // Always private |
| 499 | quote!() |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | // Check if the type has a ZeroFrom impl |
| 504 | fn has_zf(&self) -> bool { |
| 505 | self.fields.iter().all(|f| f.kind.has_zf()) |
| 506 | } |
| 507 | |
| 508 | // Takes all unsized fields on self and encodes them into a byte slice `out` |
| 509 | fn encode_write(&self, out: TokenStream2) -> TokenStream2 { |
| 510 | if self.fields.len() == 1 { |
| 511 | self.fields[0].encode_func(quote!(encode_var_ule_write), quote!(#out)) |
| 512 | } else { |
| 513 | let mut lengths = vec![]; |
| 514 | let mut writers = vec![]; |
| 515 | for (i, field) in self.fields.iter().enumerate() { |
| 516 | lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!())); |
| 517 | let (encodeable_ty, encodeable) = field.encodeable_tokens(); |
| 518 | let varule_ty = field.kind.varule_ty(); |
| 519 | writers |
| 520 | .push(quote!(multi.set_field_at::<#varule_ty, #encodeable_ty>(#i, #encodeable))) |
| 521 | } |
| 522 | |
| 523 | quote!( |
| 524 | let lengths = [#(#lengths),*]; |
| 525 | let mut multi = zerovec::ule::MultiFieldsULE::new_from_lengths_partially_initialized(&lengths, #out); |
| 526 | unsafe { |
| 527 | #(#writers;)* |
| 528 | } |
| 529 | ) |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | // Takes all unsized fields on self and returns the length needed for encoding into a byte slice |
| 534 | fn encode_len(&self) -> TokenStream2 { |
| 535 | if self.fields.len() == 1 { |
| 536 | self.fields[0].encode_func(quote!(encode_var_ule_len), quote!()) |
| 537 | } else { |
| 538 | let mut lengths = vec![]; |
| 539 | for field in self.fields.iter() { |
| 540 | lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!())); |
| 541 | } |
| 542 | quote!(zerovec::ule::MultiFieldsULE::compute_encoded_len_for(&[#(#lengths),*])) |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | /// Constructs ZeroFrom setters for each field of the stack type |
| 547 | fn push_zf_setters(&self, lt: &Lifetime, field_inits: &mut Vec<TokenStream2>) { |
| 548 | let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom); |
| 549 | if self.fields.len() == 1 { |
| 550 | let accessor = self.fields[0].field.accessor.clone(); |
| 551 | let setter = self.fields[0].field.setter(); |
| 552 | let last_field_ty = &self.fields[0].field.field.ty; |
| 553 | let last_field_ule_ty = self.fields[0].kind.varule_ty(); |
| 554 | field_inits.push(quote!(#setter <#last_field_ty as #zerofrom_trait <#lt, #last_field_ule_ty>>::zero_from(&other.#accessor) )); |
| 555 | } else { |
| 556 | for field in self.fields.iter() { |
| 557 | let setter = field.field.setter(); |
| 558 | let getter = field.field.getter(); |
| 559 | let field_ty = &field.field.field.ty; |
| 560 | let field_ule_ty = field.kind.varule_ty(); |
| 561 | |
| 562 | field_inits.push(quote!(#setter |
| 563 | <#field_ty as #zerofrom_trait <#lt, #field_ule_ty>>::zero_from(&other.#getter()) |
| 564 | )); |
| 565 | } |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | fn maybe_multi_getters(&self) -> Option<TokenStream2> { |
| 570 | if self.fields.len() == 1 { |
| 571 | None |
| 572 | } else { |
| 573 | let multi_accessor = self.varule_accessor(); |
| 574 | let field_getters = self.fields.iter().enumerate().map(|(i, field)| { |
| 575 | let getter = field.field.getter(); |
| 576 | |
| 577 | let field_ule_ty = field.kind.varule_ty(); |
| 578 | let doc_name = field.field.getter_doc_name(); |
| 579 | let doc = format!("Access the VarULE type behind {doc_name}" ); |
| 580 | quote!( |
| 581 | #[doc = #doc] |
| 582 | pub fn #getter<'a>(&'a self) -> &'a #field_ule_ty { |
| 583 | unsafe { |
| 584 | self.#multi_accessor.get_field::<#field_ule_ty>(#i) |
| 585 | } |
| 586 | } |
| 587 | ) |
| 588 | }); |
| 589 | |
| 590 | Some(quote!(#(#field_getters)*)) |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | /// In case this needs custom validation code, return it |
| 595 | /// |
| 596 | /// The code will validate a variable known as `last_field_bytes` |
| 597 | fn varule_validator(&self) -> Option<TokenStream2> { |
| 598 | if self.fields.len() == 1 { |
| 599 | None |
| 600 | } else { |
| 601 | let mut validators = vec![]; |
| 602 | for (i, field) in self.fields.iter().enumerate() { |
| 603 | let varule_ty = field.kind.varule_ty(); |
| 604 | validators.push(quote!(multi.validate_field::<#varule_ty>(#i)?;)); |
| 605 | } |
| 606 | |
| 607 | Some(quote!( |
| 608 | let multi = zerovec::ule::MultiFieldsULE::parse_byte_slice(last_field_bytes)?; |
| 609 | unsafe { |
| 610 | #(#validators)* |
| 611 | } |
| 612 | )) |
| 613 | } |
| 614 | } |
| 615 | } |
| 616 | |
| 617 | impl<'a> UnsizedField<'a> { |
| 618 | fn new( |
| 619 | field: &'a Field, |
| 620 | index: usize, |
| 621 | custom_varule_ident: Option<Ident>, |
| 622 | ) -> Result<Self, String> { |
| 623 | Ok(UnsizedField { |
| 624 | kind: UnsizedFieldKind::new(&field.ty, custom_varule_ident)?, |
| 625 | field: FieldInfo::new_for_field(field, index), |
| 626 | }) |
| 627 | } |
| 628 | |
| 629 | /// Call `<Self as EncodeAsVarULE<V>>::#method(self.accessor #additional_args)` after adjusting |
| 630 | /// Self and self.accessor to be the right types |
| 631 | fn encode_func(&self, method: TokenStream2, additional_args: TokenStream2) -> TokenStream2 { |
| 632 | let encodeas_trait = quote!(zerovec::ule::EncodeAsVarULE); |
| 633 | let (encodeable_ty, encodeable) = self.encodeable_tokens(); |
| 634 | let varule_ty = self.kind.varule_ty(); |
| 635 | quote!(<#encodeable_ty as #encodeas_trait<#varule_ty>>::#method(#encodeable, #additional_args)) |
| 636 | } |
| 637 | |
| 638 | /// Returns (encodeable_ty, encodeable) |
| 639 | fn encodeable_tokens(&self) -> (TokenStream2, TokenStream2) { |
| 640 | let accessor = self.field.accessor.clone(); |
| 641 | let value = quote!(self.#accessor); |
| 642 | let encodeable = self.kind.encodeable_value(value); |
| 643 | let encodeable_ty = self.kind.encodeable_ty(); |
| 644 | (encodeable_ty, encodeable) |
| 645 | } |
| 646 | } |
| 647 | |
| 648 | impl<'a> UnsizedFieldKind<'a> { |
| 649 | /// Construct a UnsizedFieldKind for the type of a UnsizedFieldKind if possible |
| 650 | fn new( |
| 651 | ty: &'a Type, |
| 652 | custom_varule_ident: Option<Ident>, |
| 653 | ) -> Result<UnsizedFieldKind<'a>, String> { |
| 654 | static PATH_TYPE_IDENTITY_ERROR: &str = |
| 655 | "Can only automatically detect corresponding VarULE types for path types \ |
| 656 | that are Cow, ZeroVec, VarZeroVec, Box, String, or Vec" ; |
| 657 | static PATH_TYPE_GENERICS_ERROR: &str = |
| 658 | "Can only automatically detect corresponding VarULE types for path \ |
| 659 | types with at most one lifetime and at most one generic parameter. VarZeroVecFormat |
| 660 | types are not currently supported" ; |
| 661 | match *ty { |
| 662 | Type::Reference(ref tyref) => OwnULETy::new(&tyref.elem, "reference" ).map(UnsizedFieldKind::Ref), |
| 663 | Type::Path(ref typath) => { |
| 664 | if let Some(custom_varule_ident) = custom_varule_ident { |
| 665 | return Ok(UnsizedFieldKind::Custom(typath, custom_varule_ident)); |
| 666 | } |
| 667 | if typath.path.segments.len() != 1 { |
| 668 | return Err("Can only automatically detect corresponding VarULE types for \ |
| 669 | path types with a single path segment" .into()); |
| 670 | } |
| 671 | let segment = typath.path.segments.first().unwrap(); |
| 672 | match segment.arguments { |
| 673 | PathArguments::None => { |
| 674 | if segment.ident == "String" { |
| 675 | Ok(UnsizedFieldKind::Growable(OwnULETy::Str)) |
| 676 | } else { |
| 677 | Err(PATH_TYPE_IDENTITY_ERROR.into()) |
| 678 | } |
| 679 | } |
| 680 | PathArguments::AngleBracketed(ref params) => { |
| 681 | // At most one lifetime and exactly one generic parameter |
| 682 | let mut lifetime = None; |
| 683 | let mut generic = None; |
| 684 | for param in ¶ms.args { |
| 685 | match param { |
| 686 | GenericArgument::Lifetime(ref lt) if lifetime.is_none() => { |
| 687 | lifetime = Some(lt) |
| 688 | } |
| 689 | GenericArgument::Type(ref ty) if generic.is_none() => { |
| 690 | generic = Some(ty) |
| 691 | } |
| 692 | _ => return Err(PATH_TYPE_GENERICS_ERROR.into()), |
| 693 | } |
| 694 | } |
| 695 | |
| 696 | // Must be exactly one generic parameter |
| 697 | // (we've handled the zero generics case already) |
| 698 | let generic = if let Some(g) = generic { |
| 699 | g |
| 700 | } else { |
| 701 | return Err(PATH_TYPE_GENERICS_ERROR.into()); |
| 702 | }; |
| 703 | |
| 704 | let ident = segment.ident.to_string(); |
| 705 | |
| 706 | if lifetime.is_some() { |
| 707 | match &*ident { |
| 708 | "ZeroVec" => Ok(UnsizedFieldKind::ZeroVec(generic)), |
| 709 | "VarZeroVec" => Ok(UnsizedFieldKind::VarZeroVec(generic)), |
| 710 | "Cow" => OwnULETy::new(generic, "Cow" ).map(UnsizedFieldKind::Cow), |
| 711 | _ => Err(PATH_TYPE_IDENTITY_ERROR.into()), |
| 712 | } |
| 713 | } else { |
| 714 | match &*ident { |
| 715 | "Vec" => Ok(UnsizedFieldKind::Growable(OwnULETy::Slice(generic))), |
| 716 | "Box" => OwnULETy::new(generic, "Box" ).map(UnsizedFieldKind::Boxed), |
| 717 | _ => Err(PATH_TYPE_IDENTITY_ERROR.into()), |
| 718 | } |
| 719 | } |
| 720 | } |
| 721 | _ => Err("Can only automatically detect corresponding VarULE types for path types \ |
| 722 | with none or angle bracketed generics" .into()), |
| 723 | } |
| 724 | } |
| 725 | _ => Err("Can only automatically detect corresponding VarULE types for path and reference types" .into()), |
| 726 | } |
| 727 | } |
| 728 | /// Get the tokens for the corresponding VarULE type |
| 729 | fn varule_ty(&self) -> TokenStream2 { |
| 730 | match *self { |
| 731 | Self::Ref(ref inner) |
| 732 | | Self::Cow(ref inner) |
| 733 | | Self::Boxed(ref inner) |
| 734 | | Self::Growable(ref inner) => { |
| 735 | let inner_ule = inner.varule_ty(); |
| 736 | quote!(#inner_ule) |
| 737 | } |
| 738 | Self::Custom(_, ref name) => quote!(#name), |
| 739 | Self::ZeroVec(ref inner) => quote!(zerovec::ZeroSlice<#inner>), |
| 740 | Self::VarZeroVec(ref inner) => quote!(zerovec::VarZeroSlice<#inner>), |
| 741 | } |
| 742 | } |
| 743 | |
| 744 | // Takes expr `value` and returns it as a value that can be encoded via EncodeAsVarULE |
| 745 | fn encodeable_value(&self, value: TokenStream2) -> TokenStream2 { |
| 746 | match *self { |
| 747 | Self::Ref(_) | Self::Cow(_) | Self::Growable(_) | Self::Boxed(_) => quote!(&*#value), |
| 748 | |
| 749 | Self::Custom(..) => quote!(&#value), |
| 750 | Self::ZeroVec(_) | Self::VarZeroVec(_) => quote!(&*#value), |
| 751 | } |
| 752 | } |
| 753 | |
| 754 | /// Returns the EncodeAsVarULE type this can be represented as, the same returned by encodeable_value() |
| 755 | fn encodeable_ty(&self) -> TokenStream2 { |
| 756 | match *self { |
| 757 | Self::Ref(ref inner) |
| 758 | | Self::Cow(ref inner) |
| 759 | | Self::Growable(ref inner) |
| 760 | | Self::Boxed(ref inner) => inner.varule_ty(), |
| 761 | |
| 762 | Self::Custom(ref path, _) => quote!(#path), |
| 763 | Self::ZeroVec(ref ty) => quote!(zerovec::ZeroSlice<#ty>), |
| 764 | Self::VarZeroVec(ref ty) => quote!(zerovec::VarZeroSlice<#ty>), |
| 765 | } |
| 766 | } |
| 767 | |
| 768 | fn has_zf(&self) -> bool { |
| 769 | matches!( |
| 770 | *self, |
| 771 | Self::Ref(_) | Self::Cow(_) | Self::ZeroVec(_) | Self::VarZeroVec(_) | Self::Custom(..) |
| 772 | ) |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | impl<'a> OwnULETy<'a> { |
| 777 | fn new(ty: &'a Type, context: &str) -> Result<Self, String> { |
| 778 | match *ty { |
| 779 | Type::Slice(ref slice: &TypeSlice) => Ok(OwnULETy::Slice(&slice.elem)), |
| 780 | Type::Path(ref typath: &TypePath) => { |
| 781 | if typath.path.is_ident("str" ) { |
| 782 | Ok(OwnULETy::Str) |
| 783 | } else { |
| 784 | Err(format!("Cannot automatically detect corresponding VarULE type for non-str path type inside a {context}" )) |
| 785 | } |
| 786 | } |
| 787 | _ => Err(format!("Cannot automatically detect corresponding VarULE type for non-slice/path type inside a {context}" )), |
| 788 | } |
| 789 | } |
| 790 | |
| 791 | /// Get the tokens for the corresponding VarULE type |
| 792 | fn varule_ty(&self) -> TokenStream2 { |
| 793 | match *self { |
| 794 | OwnULETy::Slice(s: &Type) => quote!([#s]), |
| 795 | OwnULETy::Str => quote!(str), |
| 796 | } |
| 797 | } |
| 798 | } |
| 799 | |