• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
make_varule_impl(ule_name: Ident, mut input: DeriveInput) -> TokenStream215 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, attrs.vzv_format);
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_and_from_impl = make_zf_and_from_impl(
145         &sized_fields,
146         &unsized_field_info,
147         fields,
148         name,
149         &ule_name,
150         lt,
151         input_span,
152         attrs.skip_from,
153     );
154 
155     let eq_impl = quote!(
156         impl core::cmp::PartialEq for #ule_name {
157             fn eq(&self, other: &Self) -> bool {
158                 // The VarULE invariants allow us to assume that equality is byte equality
159                 // in non-safety-critical contexts
160                 <Self as zerovec::ule::VarULE>::as_bytes(&self)
161                 == <Self as zerovec::ule::VarULE>::as_bytes(&other)
162             }
163         }
164 
165         impl core::cmp::Eq for #ule_name {}
166     );
167 
168     let zerofrom_fq_path =
169         quote!(<#name as zerovec::__zerovec_internal_reexport::ZeroFrom<#ule_name>>);
170 
171     let maybe_ord_impls = if attrs.skip_ord {
172         quote!()
173     } else {
174         quote!(
175             impl core::cmp::PartialOrd for #ule_name {
176                 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
177                     Some(self.cmp(other))
178                 }
179             }
180 
181             impl core::cmp::Ord for #ule_name {
182                 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
183                     let this = #zerofrom_fq_path::zero_from(self);
184                     let other = #zerofrom_fq_path::zero_from(other);
185                     <#name as core::cmp::Ord>::cmp(&this, &other)
186                 }
187             }
188         )
189     };
190 
191     let maybe_debug = if attrs.debug {
192         quote!(
193             impl core::fmt::Debug for #ule_name {
194                 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
195                     let this = #zerofrom_fq_path::zero_from(self);
196                     <#name as core::fmt::Debug>::fmt(&this, f)
197                 }
198             }
199         )
200     } else {
201         quote!()
202     };
203 
204     let maybe_toowned = if !attrs.skip_toowned {
205         quote!(
206             impl zerovec::__zerovec_internal_reexport::borrow::ToOwned for #ule_name {
207                 type Owned = zerovec::__zerovec_internal_reexport::boxed::Box<Self>;
208                 fn to_owned(&self) -> Self::Owned {
209                     zerovec::ule::encode_varule_to_box(self)
210                 }
211             }
212         )
213     } else {
214         quote!()
215     };
216 
217     let zmkv = if attrs.skip_kv {
218         quote!()
219     } else {
220         quote!(
221             impl<'a> zerovec::maps::ZeroMapKV<'a> for #ule_name {
222                 type Container = zerovec::VarZeroVec<'a, #ule_name>;
223                 type Slice = zerovec::VarZeroSlice<#ule_name>;
224                 type GetType = #ule_name;
225                 type OwnedType = zerovec::__zerovec_internal_reexport::boxed::Box<#ule_name>;
226             }
227         )
228     };
229 
230     let serde_path = quote!(zerovec::__zerovec_internal_reexport::serde);
231 
232     let maybe_ser = if attrs.serialize {
233         quote!(
234             impl #serde_path::Serialize for #ule_name {
235                 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: #serde_path::Serializer {
236                     if serializer.is_human_readable() {
237                         let this = #zerofrom_fq_path::zero_from(self);
238                         <#name as #serde_path::Serialize>::serialize(&this, serializer)
239                     } else {
240                         serializer.serialize_bytes(zerovec::ule::VarULE::as_bytes(self))
241                     }
242                 }
243             }
244         )
245     } else {
246         quote!()
247     };
248 
249     let deserialize_error = format!("&{ule_name} can only deserialize in zero-copy ways");
250 
251     let maybe_de = if attrs.deserialize {
252         quote!(
253             impl<'de> #serde_path::Deserialize<'de> for zerovec::__zerovec_internal_reexport::boxed::Box<#ule_name> {
254                 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: #serde_path::Deserializer<'de> {
255                     if deserializer.is_human_readable() {
256                         let this = <#name as #serde_path::Deserialize>::deserialize(deserializer)?;
257                         Ok(zerovec::ule::encode_varule_to_box(&this))
258                     } else {
259                         // This branch should usually not be hit, since Cow-like use cases will hit the Deserialize impl for &'a ULEType instead.
260                         let deserialized = <& #ule_name>::deserialize(deserializer)?;
261                         Ok(zerovec::ule::VarULE::to_boxed(deserialized))
262                     }
263                 }
264             }
265             impl<'a, 'de: 'a> #serde_path::Deserialize<'de> for &'a #ule_name {
266                 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: #serde_path::Deserializer<'de> {
267                     if !deserializer.is_human_readable() {
268                         let bytes = <&[u8]>::deserialize(deserializer)?;
269                         <#ule_name as zerovec::ule::VarULE>::parse_bytes(bytes).map_err(#serde_path::de::Error::custom)
270                     } else {
271                         Err(#serde_path::de::Error::custom(#deserialize_error))
272                     }
273                 }
274             }
275         )
276     } else {
277         quote!()
278     };
279 
280     let maybe_hash = if attrs.hash {
281         quote!(
282             #[allow(clippy::derive_hash_xor_eq)]
283             impl core::hash::Hash for #ule_name {
284                 fn hash<H>(&self, state: &mut H) where H: core::hash::Hasher {
285                     state.write(<#ule_name as zerovec::ule::VarULE>::as_bytes(&self));
286                 }
287             }
288         )
289     } else {
290         quote!()
291     };
292 
293     let maybe_multi_getters = if let Some(getters) = unsized_field_info.maybe_multi_getters() {
294         quote! {
295             impl #ule_name {
296                 #getters
297             }
298         }
299     } else {
300         quote!()
301     };
302 
303     quote!(
304         #input
305 
306         #varule_struct
307 
308         #maybe_multi_getters
309 
310         #encode_impl
311 
312         #zf_and_from_impl
313 
314         #derived
315 
316         #maybe_ord_impls
317 
318         #eq_impl
319 
320         #zmkv
321 
322         #maybe_ser
323 
324         #maybe_de
325 
326         #maybe_debug
327 
328         #maybe_toowned
329 
330         #maybe_hash
331     )
332 }
333 
334 #[allow(clippy::too_many_arguments)] // Internal function. Could refactor later to use some kind of context type.
make_zf_and_from_impl( sized_fields: &[FieldInfo], unsized_field_info: &UnsizedFields, fields: &Fields, name: &Ident, ule_name: &Ident, maybe_lt: Option<&Lifetime>, span: Span, skip_from: bool, ) -> TokenStream2335 fn make_zf_and_from_impl(
336     sized_fields: &[FieldInfo],
337     unsized_field_info: &UnsizedFields,
338     fields: &Fields,
339     name: &Ident,
340     ule_name: &Ident,
341     maybe_lt: Option<&Lifetime>,
342     span: Span,
343     skip_from: bool,
344 ) -> TokenStream2 {
345     if !unsized_field_info.has_zf() {
346         return quote!();
347     }
348 
349     let lt = if let Some(ref lt) = maybe_lt {
350         lt
351     } else {
352         return Error::new(
353             span,
354             "Can only generate ZeroFrom impls for types with lifetimes",
355         )
356         .to_compile_error();
357     };
358 
359     let mut field_inits = sized_fields
360         .iter()
361         .map(|f| {
362             let ty = &f.field.ty;
363             let accessor = &f.accessor;
364             let setter = f.setter();
365             quote!(#setter <#ty as zerovec::ule::AsULE>::from_unaligned(other.#accessor))
366         })
367         .collect::<Vec<_>>();
368 
369     unsized_field_info.push_zf_setters(lt, &mut field_inits);
370 
371     let field_inits = utils::wrap_field_inits(&field_inits, fields);
372     let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom);
373 
374     let maybe_from = if skip_from {
375         quote!()
376     } else {
377         quote!(
378             impl<#lt> From<&#lt #ule_name> for #name<#lt> {
379                 fn from(other: &#lt #ule_name) -> Self {
380                     <Self as #zerofrom_trait<#lt, #ule_name>>::zero_from(other)
381                 }
382             }
383         )
384     };
385     quote!(
386         impl <#lt> #zerofrom_trait <#lt, #ule_name> for #name <#lt> {
387             fn zero_from(other: &#lt #ule_name) -> Self {
388                 Self #field_inits
389             }
390         }
391 
392         #maybe_from
393     )
394 }
395 
make_encode_impl( sized_fields: &[FieldInfo], unsized_field_info: &UnsizedFields, name: &Ident, ule_name: &Ident, maybe_lt_bound: &Option<TokenStream2>, ) -> TokenStream2396 fn make_encode_impl(
397     sized_fields: &[FieldInfo],
398     unsized_field_info: &UnsizedFields,
399     name: &Ident,
400     ule_name: &Ident,
401     maybe_lt_bound: &Option<TokenStream2>,
402 ) -> TokenStream2 {
403     let mut lengths = vec![];
404 
405     for field in sized_fields {
406         let ty = &field.field.ty;
407         lengths.push(quote!(::core::mem::size_of::<<#ty as zerovec::ule::AsULE>::ULE>()));
408     }
409 
410     let (encoders, remaining_offset) = utils::generate_per_field_offsets(
411         sized_fields,
412         true,
413         |field, prev_offset_ident, size_ident| {
414             let ty = &field.field.ty;
415             let accessor = &field.accessor;
416             quote!(
417                 // generate_per_field_offsets produces valid indices,
418                 // and we don't care about panics in Encode impls
419                 #[allow(clippy::indexing_slicing)]
420                 let out = &mut dst[#prev_offset_ident .. #prev_offset_ident + #size_ident];
421                 let unaligned = zerovec::ule::AsULE::to_unaligned(self.#accessor);
422                 let unaligned_slice = &[unaligned];
423                 let src = <<#ty as zerovec::ule::AsULE>::ULE as zerovec::ule::ULE>::slice_as_bytes(unaligned_slice);
424                 out.copy_from_slice(src);
425             )
426         },
427     );
428 
429     let last_encode_len = unsized_field_info.encode_len();
430     let last_encode_write = unsized_field_info.encode_write(quote!(out));
431     quote!(
432         unsafe impl #maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for #name #maybe_lt_bound {
433             // Safety: unimplemented as the other two are implemented
434             fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R {
435                 unreachable!("other two methods implemented")
436             }
437 
438             // Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms
439             fn encode_var_ule_len(&self) -> usize {
440                 #(#lengths +)* #last_encode_len
441             }
442 
443             // Safety: converts each element to ULE form and writes them in sequence
444             fn encode_var_ule_write(&self, mut dst: &mut [u8]) {
445                 debug_assert_eq!(self.encode_var_ule_len(), dst.len());
446                 #encoders
447 
448 
449                 // generate_per_field_offsets produces valid remainders,
450                 // and we don't care about panics in Encode impls
451                 #[allow(clippy::indexing_slicing)]
452                 let out = &mut dst[#remaining_offset..];
453                 #last_encode_write
454             }
455         }
456 
457         // This second impl exists to allow for using EncodeAsVarULE without cloning
458         //
459         // A blanket impl cannot exist without coherence issues
460         unsafe impl #maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for &'_ #name #maybe_lt_bound {
461             // Safety: unimplemented as the other two are implemented
462             fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R {
463                 unreachable!("other two methods implemented")
464             }
465 
466             // Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms
467             fn encode_var_ule_len(&self) -> usize {
468                 (**self).encode_var_ule_len()
469             }
470 
471             // Safety: converts each element to ULE form and writes them in sequence
472             fn encode_var_ule_write(&self, mut dst: &mut [u8]) {
473                 (**self).encode_var_ule_write(dst)
474             }
475         }
476     )
477 }
478 
479 /// Represents a VarULE-compatible type that would typically
480 /// be found behind a `Cow<'a, _>` in the last field, and is represented
481 /// roughly the same in owned and borrowed versions
482 #[derive(Copy, Clone, Debug)]
483 enum OwnULETy<'a> {
484     /// [T] where T: AsULE<ULE = Self>
485     Slice(&'a Type),
486     /// str
487     Str,
488 }
489 
490 /// Represents the type of the last field of the struct
491 #[derive(Clone, Debug)]
492 enum UnsizedFieldKind<'a> {
493     Cow(OwnULETy<'a>),
494     VarZeroCow(OwnULETy<'a>),
495     ZeroVec(&'a Type),
496     VarZeroVec(&'a Type),
497     /// Custom VarULE type, and the identifier corresponding to the VarULE type
498     Custom(&'a TypePath, Ident),
499 
500     // Generally you should be using the above ones for maximum zero-copy, but these will still work
501     Growable(OwnULETy<'a>),
502     Boxed(OwnULETy<'a>),
503     Ref(OwnULETy<'a>),
504 }
505 
506 #[derive(Clone, Debug)]
507 struct UnsizedField<'a> {
508     kind: UnsizedFieldKind<'a>,
509     field: FieldInfo<'a>,
510 }
511 
512 struct UnsizedFields<'a> {
513     fields: Vec<UnsizedField<'a>>,
514     format_param: TokenStream2,
515 }
516 
517 impl<'a> UnsizedFields<'a> {
518     /// The format_param is an optional tokenstream describing a VZVFormat argument needed by MultiFieldsULE
new(fields: Vec<UnsizedField<'a>>, format_param: Option<TokenStream2>) -> Self519     fn new(fields: Vec<UnsizedField<'a>>, format_param: Option<TokenStream2>) -> Self {
520         assert!(!fields.is_empty(), "Must have at least one unsized field");
521 
522         let format_param = format_param.unwrap_or_else(|| quote!(zerovec::vecs::Index16));
523         Self {
524             fields,
525             format_param,
526         }
527     }
528 
529     // Get the corresponding VarULE type that can store all of these
varule_ty(&self) -> TokenStream2530     fn varule_ty(&self) -> TokenStream2 {
531         let len = self.fields.len();
532         let format_param = &self.format_param;
533         if len == 1 {
534             self.fields[0].kind.varule_ty()
535         } else {
536             quote!(zerovec::ule::MultiFieldsULE::<#len, #format_param>)
537         }
538     }
539 
540     // Get the accessor field name in the VarULE type
varule_accessor(&self) -> TokenStream2541     fn varule_accessor(&self) -> TokenStream2 {
542         if self.fields.len() == 1 {
543             self.fields[0].field.accessor.clone()
544         } else if self.fields[0].field.field.ident.is_some() {
545             quote!(unsized_fields)
546         } else {
547             // first unsized field
548             self.fields[0].field.accessor.clone()
549         }
550     }
551 
552     // Get the setter for this type for use in struct definition/creation syntax
varule_setter(&self) -> TokenStream2553     fn varule_setter(&self) -> TokenStream2 {
554         if self.fields.len() == 1 {
555             self.fields[0].field.setter()
556         } else if self.fields[0].field.field.ident.is_some() {
557             quote!(unsized_fields: )
558         } else {
559             quote!()
560         }
561     }
562 
varule_vis(&self) -> TokenStream2563     fn varule_vis(&self) -> TokenStream2 {
564         if self.fields.len() == 1 {
565             self.fields[0].field.field.vis.to_token_stream()
566         } else {
567             // Always private
568             quote!()
569         }
570     }
571 
572     // Check if the type has a ZeroFrom impl
has_zf(&self) -> bool573     fn has_zf(&self) -> bool {
574         self.fields.iter().all(|f| f.kind.has_zf())
575     }
576 
577     // Takes all unsized fields on self and encodes them into a byte slice `out`
encode_write(&self, out: TokenStream2) -> TokenStream2578     fn encode_write(&self, out: TokenStream2) -> TokenStream2 {
579         let len = self.fields.len();
580         let format_param = &self.format_param;
581         if len == 1 {
582             self.fields[0].encode_func(quote!(encode_var_ule_write), quote!(#out))
583         } else {
584             let mut lengths = vec![];
585             let mut writers = vec![];
586             for (i, field) in self.fields.iter().enumerate() {
587                 lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!()));
588                 let (encodeable_ty, encodeable) = field.encodeable_tokens();
589                 let varule_ty = field.kind.varule_ty();
590                 writers
591                     .push(quote!(multi.set_field_at::<#varule_ty, #encodeable_ty>(#i, #encodeable)))
592             }
593 
594             quote!(
595                 let lengths = [#(#lengths),*];
596                 // Todo: index type should be settable by attribute
597                 let mut multi = zerovec::ule::MultiFieldsULE::<#len, #format_param>::new_from_lengths_partially_initialized(lengths, #out);
598                 unsafe {
599                     #(#writers;)*
600                 }
601             )
602         }
603     }
604 
605     // Takes all unsized fields on self and returns the length needed for encoding into a byte slice
encode_len(&self) -> TokenStream2606     fn encode_len(&self) -> TokenStream2 {
607         let len = self.fields.len();
608         let format_param = &self.format_param;
609         if len == 1 {
610             self.fields[0].encode_func(quote!(encode_var_ule_len), quote!())
611         } else {
612             let mut lengths = vec![];
613             for field in self.fields.iter() {
614                 lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!()));
615             }
616             // Todo: index type should be settable by attribute
617             quote!(zerovec::ule::MultiFieldsULE::<#len, #format_param>::compute_encoded_len_for([#(#lengths),*]))
618         }
619     }
620 
621     /// Constructs ZeroFrom setters for each field of the stack type
push_zf_setters(&self, lt: &Lifetime, field_inits: &mut Vec<TokenStream2>)622     fn push_zf_setters(&self, lt: &Lifetime, field_inits: &mut Vec<TokenStream2>) {
623         let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom);
624         if self.fields.len() == 1 {
625             let accessor = self.fields[0].field.accessor.clone();
626             let setter = self.fields[0].field.setter();
627             let last_field_ty = &self.fields[0].field.field.ty;
628             let last_field_ule_ty = self.fields[0].kind.varule_ty();
629             field_inits.push(quote!(#setter <#last_field_ty as #zerofrom_trait <#lt, #last_field_ule_ty>>::zero_from(&other.#accessor) ));
630         } else {
631             for field in self.fields.iter() {
632                 let setter = field.field.setter();
633                 let getter = field.field.getter();
634                 let field_ty = &field.field.field.ty;
635                 let field_ule_ty = field.kind.varule_ty();
636 
637                 field_inits.push(quote!(#setter
638                     <#field_ty as #zerofrom_trait <#lt, #field_ule_ty>>::zero_from(&other.#getter())
639                 ));
640             }
641         }
642     }
643 
maybe_multi_getters(&self) -> Option<TokenStream2>644     fn maybe_multi_getters(&self) -> Option<TokenStream2> {
645         if self.fields.len() == 1 {
646             None
647         } else {
648             let multi_accessor = self.varule_accessor();
649             let field_getters = self.fields.iter().enumerate().map(|(i, field)| {
650                 let getter = field.field.getter();
651 
652                 let field_ule_ty = field.kind.varule_ty();
653                 let doc_name = field.field.getter_doc_name();
654                 let doc = format!("Access the VarULE type behind {doc_name}");
655                 quote!(
656                     #[doc = #doc]
657                     pub fn #getter<'a>(&'a self) -> &'a #field_ule_ty {
658                         unsafe {
659                             self.#multi_accessor.get_field::<#field_ule_ty>(#i)
660                         }
661                     }
662                 )
663             });
664 
665             Some(quote!(#(#field_getters)*))
666         }
667     }
668 
669     /// In case this needs custom validation code, return it
670     ///
671     /// The code will validate a variable known as `last_field_bytes`
varule_validator(&self) -> Option<TokenStream2>672     fn varule_validator(&self) -> Option<TokenStream2> {
673         let len = self.fields.len();
674         let format_param = &self.format_param;
675         if len == 1 {
676             None
677         } else {
678             let mut validators = vec![];
679             for (i, field) in self.fields.iter().enumerate() {
680                 let varule_ty = field.kind.varule_ty();
681                 validators.push(quote!(multi.validate_field::<#varule_ty>(#i)?;));
682             }
683 
684             Some(quote!(
685                 let multi = zerovec::ule::MultiFieldsULE::<#len, #format_param>::parse_bytes(last_field_bytes)?;
686                 unsafe {
687                     #(#validators)*
688                 }
689             ))
690         }
691     }
692 }
693 
694 impl<'a> UnsizedField<'a> {
new( field: &'a Field, index: usize, custom_varule_ident: Option<Ident>, ) -> Result<Self, String>695     fn new(
696         field: &'a Field,
697         index: usize,
698         custom_varule_ident: Option<Ident>,
699     ) -> Result<Self, String> {
700         Ok(UnsizedField {
701             kind: UnsizedFieldKind::new(&field.ty, custom_varule_ident)?,
702             field: FieldInfo::new_for_field(field, index),
703         })
704     }
705 
706     /// Call `<Self as EncodeAsVarULE<V>>::#method(self.accessor #additional_args)` after adjusting
707     /// Self and self.accessor to be the right types
encode_func(&self, method: TokenStream2, additional_args: TokenStream2) -> TokenStream2708     fn encode_func(&self, method: TokenStream2, additional_args: TokenStream2) -> TokenStream2 {
709         let encodeas_trait = quote!(zerovec::ule::EncodeAsVarULE);
710         let (encodeable_ty, encodeable) = self.encodeable_tokens();
711         let varule_ty = self.kind.varule_ty();
712         quote!(<#encodeable_ty as #encodeas_trait<#varule_ty>>::#method(#encodeable, #additional_args))
713     }
714 
715     /// Returns (encodeable_ty, encodeable)
encodeable_tokens(&self) -> (TokenStream2, TokenStream2)716     fn encodeable_tokens(&self) -> (TokenStream2, TokenStream2) {
717         let accessor = self.field.accessor.clone();
718         let value = quote!(self.#accessor);
719         let encodeable = self.kind.encodeable_value(value);
720         let encodeable_ty = self.kind.encodeable_ty();
721         (encodeable_ty, encodeable)
722     }
723 }
724 
725 impl<'a> UnsizedFieldKind<'a> {
726     /// Construct a UnsizedFieldKind for the type of a UnsizedFieldKind if possible
new( ty: &'a Type, custom_varule_ident: Option<Ident>, ) -> Result<UnsizedFieldKind<'a>, String>727     fn new(
728         ty: &'a Type,
729         custom_varule_ident: Option<Ident>,
730     ) -> Result<UnsizedFieldKind<'a>, String> {
731         static PATH_TYPE_IDENTITY_ERROR: &str =
732             "Can only automatically detect corresponding VarULE types for path types \
733             that are Cow, ZeroVec, VarZeroVec, Box, String, or Vec";
734         static PATH_TYPE_GENERICS_ERROR: &str =
735             "Can only automatically detect corresponding VarULE types for path \
736             types with at most one lifetime and at most one generic parameter. VarZeroVecFormat
737             types are not currently supported";
738         match *ty {
739             Type::Reference(ref tyref) => OwnULETy::new(&tyref.elem, "reference").map(UnsizedFieldKind::Ref),
740             Type::Path(ref typath) => {
741                 if let Some(custom_varule_ident) = custom_varule_ident {
742                     return Ok(UnsizedFieldKind::Custom(typath, custom_varule_ident));
743                 }
744                 if typath.path.segments.len() != 1 {
745                     return Err("Can only automatically detect corresponding VarULE types for \
746                                 path types with a single path segment".into());
747                 }
748                 let segment = typath.path.segments.first().unwrap();
749                 match segment.arguments {
750                     PathArguments::None => {
751                         if segment.ident == "String" {
752                             Ok(UnsizedFieldKind::Growable(OwnULETy::Str))
753                         } else {
754                             Err(PATH_TYPE_IDENTITY_ERROR.into())
755                         }
756                     }
757                     PathArguments::AngleBracketed(ref params) => {
758                         // At most one lifetime and exactly one generic parameter
759                         let mut lifetime = None;
760                         let mut generic = None;
761                         for param in &params.args {
762                             match param {
763                                 GenericArgument::Lifetime(ref lt) if lifetime.is_none() => {
764                                     lifetime = Some(lt)
765                                 }
766                                 GenericArgument::Type(ref ty) if generic.is_none() => {
767                                     generic = Some(ty)
768                                 }
769                                 _ => return Err(PATH_TYPE_GENERICS_ERROR.into()),
770                             }
771                         }
772 
773                         // Must be exactly one generic parameter
774                         // (we've handled the zero generics case already)
775                         let generic = if let Some(g) = generic {
776                             g
777                         } else {
778                             return Err(PATH_TYPE_GENERICS_ERROR.into());
779                         };
780 
781                         let ident = segment.ident.to_string();
782 
783                         if lifetime.is_some() {
784                             match &*ident {
785                                 "ZeroVec" => Ok(UnsizedFieldKind::ZeroVec(generic)),
786                                 "VarZeroVec" => Ok(UnsizedFieldKind::VarZeroVec(generic)),
787                                 "Cow" => OwnULETy::new(generic, "Cow").map(UnsizedFieldKind::Cow),
788                                 "VarZeroCow" => OwnULETy::new(generic, "VarZeroCow").map(UnsizedFieldKind::VarZeroCow),
789                                 _ => Err(PATH_TYPE_IDENTITY_ERROR.into()),
790                             }
791                         } else {
792                             match &*ident {
793                                 "Vec" => Ok(UnsizedFieldKind::Growable(OwnULETy::Slice(generic))),
794                                 "Box" => OwnULETy::new(generic, "Box").map(UnsizedFieldKind::Boxed),
795                                 _ => Err(PATH_TYPE_IDENTITY_ERROR.into()),
796                             }
797                         }
798                     }
799                     _ => Err("Can only automatically detect corresponding VarULE types for path types \
800                               with none or angle bracketed generics".into()),
801                 }
802             }
803             _ => Err("Can only automatically detect corresponding VarULE types for path and reference types".into()),
804         }
805     }
806     /// Get the tokens for the corresponding VarULE type
varule_ty(&self) -> TokenStream2807     fn varule_ty(&self) -> TokenStream2 {
808         match *self {
809             Self::Ref(ref inner)
810             | Self::Cow(ref inner)
811             | Self::VarZeroCow(ref inner)
812             | Self::Boxed(ref inner)
813             | Self::Growable(ref inner) => {
814                 let inner_ule = inner.varule_ty();
815                 quote!(#inner_ule)
816             }
817             Self::Custom(_, ref name) => quote!(#name),
818             Self::ZeroVec(ref inner) => quote!(zerovec::ZeroSlice<#inner>),
819             Self::VarZeroVec(ref inner) => quote!(zerovec::VarZeroSlice<#inner>),
820         }
821     }
822 
823     // Takes expr `value` and returns it as a value that can be encoded via EncodeAsVarULE
encodeable_value(&self, value: TokenStream2) -> TokenStream2824     fn encodeable_value(&self, value: TokenStream2) -> TokenStream2 {
825         match *self {
826             Self::Ref(_)
827             | Self::Cow(_)
828             | Self::VarZeroCow(_)
829             | Self::Growable(_)
830             | Self::Boxed(_) => quote!(&*#value),
831 
832             Self::Custom(..) => quote!(&#value),
833             Self::ZeroVec(_) | Self::VarZeroVec(_) => quote!(&*#value),
834         }
835     }
836 
837     /// Returns the EncodeAsVarULE type this can be represented as, the same returned by encodeable_value()
encodeable_ty(&self) -> TokenStream2838     fn encodeable_ty(&self) -> TokenStream2 {
839         match *self {
840             Self::Ref(ref inner)
841             | Self::Cow(ref inner)
842             | Self::VarZeroCow(ref inner)
843             | Self::Growable(ref inner)
844             | Self::Boxed(ref inner) => inner.varule_ty(),
845 
846             Self::Custom(ref path, _) => quote!(#path),
847             Self::ZeroVec(ref ty) => quote!(zerovec::ZeroSlice<#ty>),
848             Self::VarZeroVec(ref ty) => quote!(zerovec::VarZeroSlice<#ty>),
849         }
850     }
851 
has_zf(&self) -> bool852     fn has_zf(&self) -> bool {
853         matches!(
854             *self,
855             Self::Ref(_)
856                 | Self::Cow(_)
857                 | Self::VarZeroCow(_)
858                 | Self::ZeroVec(_)
859                 | Self::VarZeroVec(_)
860                 | Self::Custom(..)
861         )
862     }
863 }
864 
865 impl<'a> OwnULETy<'a> {
new(ty: &'a Type, context: &str) -> Result<Self, String>866     fn new(ty: &'a Type, context: &str) -> Result<Self, String> {
867         match *ty {
868             Type::Slice(ref slice) => Ok(OwnULETy::Slice(&slice.elem)),
869             Type::Path(ref typath) => {
870                 if typath.path.is_ident("str") {
871                     Ok(OwnULETy::Str)
872                 } else {
873                     Err(format!("Cannot automatically detect corresponding VarULE type for non-str path type inside a {context}"))
874                 }
875             }
876             _ => Err(format!("Cannot automatically detect corresponding VarULE type for non-slice/path type inside a {context}")),
877         }
878     }
879 
880     /// Get the tokens for the corresponding VarULE type
varule_ty(&self) -> TokenStream2881     fn varule_ty(&self) -> TokenStream2 {
882         match *self {
883             OwnULETy::Slice(s) => quote!([#s]),
884             OwnULETy::Str => quote!(str),
885         }
886     }
887 }
888