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 | let this = #zerofrom_fq_path::zero_from(self); |
177 | let other = #zerofrom_fq_path::zero_from(other); |
178 | <#name as core::cmp::PartialOrd>::partial_cmp(&this, &other) |
179 | } |
180 | } |
181 | |
182 | impl core::cmp::Ord for #ule_name { |
183 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { |
184 | let this = #zerofrom_fq_path::zero_from(self); |
185 | let other = #zerofrom_fq_path::zero_from(other); |
186 | <#name as core::cmp::Ord>::cmp(&this, &other) |
187 | } |
188 | } |
189 | ) |
190 | }; |
191 | |
192 | let maybe_debug = if attrs.debug { |
193 | quote!( |
194 | impl core::fmt::Debug for #ule_name { |
195 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
196 | let this = #zerofrom_fq_path::zero_from(self); |
197 | <#name as core::fmt::Debug>::fmt(&this, f) |
198 | } |
199 | } |
200 | ) |
201 | } else { |
202 | quote!() |
203 | }; |
204 | |
205 | let zmkv = if attrs.skip_kv { |
206 | quote!() |
207 | } else { |
208 | quote!( |
209 | impl<'a> zerovec::maps::ZeroMapKV<'a> for #ule_name { |
210 | type Container = zerovec::VarZeroVec<'a, #ule_name>; |
211 | type Slice = zerovec::VarZeroSlice<#ule_name>; |
212 | type GetType = #ule_name; |
213 | type OwnedType = zerovec::__zerovec_internal_reexport::boxed::Box<#ule_name>; |
214 | } |
215 | ) |
216 | }; |
217 | |
218 | let serde_path = quote!(zerovec::__zerovec_internal_reexport::serde); |
219 | |
220 | let maybe_ser = if attrs.serialize { |
221 | quote!( |
222 | impl #serde_path::Serialize for #ule_name { |
223 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: #serde_path::Serializer { |
224 | let this = #zerofrom_fq_path::zero_from(self); |
225 | <#name as #serde_path::Serialize>::serialize(&this, serializer) |
226 | } |
227 | } |
228 | ) |
229 | } else { |
230 | quote!() |
231 | }; |
232 | |
233 | let maybe_de = if attrs.deserialize { |
234 | quote!( |
235 | impl<'de> #serde_path::Deserialize<'de> for zerovec::__zerovec_internal_reexport::boxed::Box<#ule_name> { |
236 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: #serde_path::Deserializer<'de> { |
237 | let this = <#name as #serde_path::Deserialize>::deserialize(deserializer)?; |
238 | Ok(zerovec::ule::encode_varule_to_box(&this)) |
239 | } |
240 | } |
241 | ) |
242 | } else { |
243 | quote!() |
244 | }; |
245 | |
246 | let maybe_hash = if attrs.hash { |
247 | quote!( |
248 | #[allow(clippy::derive_hash_xor_eq)] |
249 | impl core::hash::Hash for #ule_name { |
250 | fn hash<H>(&self, state: &mut H) where H: core::hash::Hasher { |
251 | state.write(<#ule_name as zerovec::ule::VarULE>::as_byte_slice(&self)); |
252 | } |
253 | } |
254 | ) |
255 | } else { |
256 | quote!() |
257 | }; |
258 | |
259 | quote!( |
260 | #input |
261 | |
262 | #varule_struct |
263 | |
264 | #encode_impl |
265 | |
266 | #zf_impl |
267 | |
268 | #derived |
269 | |
270 | #maybe_ord_impls |
271 | |
272 | #eq_impl |
273 | |
274 | #zmkv |
275 | |
276 | #maybe_ser |
277 | |
278 | #maybe_de |
279 | |
280 | #maybe_debug |
281 | |
282 | #maybe_hash |
283 | ) |
284 | } |
285 | |
286 | fn make_zf_impl( |
287 | sized_fields: &[FieldInfo], |
288 | unsized_field_info: &UnsizedFields, |
289 | fields: &Fields, |
290 | name: &Ident, |
291 | ule_name: &Ident, |
292 | maybe_lt: Option<&Lifetime>, |
293 | span: Span, |
294 | ) -> TokenStream2 { |
295 | if !unsized_field_info.has_zf() { |
296 | return quote!(); |
297 | } |
298 | |
299 | let lt = if let Some(ref lt) = maybe_lt { |
300 | lt |
301 | } else { |
302 | return Error::new( |
303 | span, |
304 | "Can only generate ZeroFrom impls for types with lifetimes" , |
305 | ) |
306 | .to_compile_error(); |
307 | }; |
308 | |
309 | let mut field_inits = sized_fields |
310 | .iter() |
311 | .map(|f| { |
312 | let ty = &f.field.ty; |
313 | let accessor = &f.accessor; |
314 | let setter = f.setter(); |
315 | quote!(#setter <#ty as zerovec::ule::AsULE>::from_unaligned(other.#accessor)) |
316 | }) |
317 | .collect::<Vec<_>>(); |
318 | |
319 | unsized_field_info.push_zf_setters(lt, &mut field_inits); |
320 | |
321 | let field_inits = utils::wrap_field_inits(&field_inits, fields); |
322 | let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom); |
323 | quote!( |
324 | impl <#lt> #zerofrom_trait <#lt, #ule_name> for #name <#lt> { |
325 | fn zero_from(other: &#lt #ule_name) -> Self { |
326 | Self #field_inits |
327 | } |
328 | } |
329 | ) |
330 | } |
331 | |
332 | fn make_encode_impl( |
333 | sized_fields: &[FieldInfo], |
334 | unsized_field_info: &UnsizedFields, |
335 | name: &Ident, |
336 | ule_name: &Ident, |
337 | maybe_lt_bound: &Option<TokenStream2>, |
338 | ) -> TokenStream2 { |
339 | let mut lengths = vec![]; |
340 | |
341 | for field in sized_fields { |
342 | let ty = &field.field.ty; |
343 | lengths.push(quote!(::core::mem::size_of::<<#ty as zerovec::ule::AsULE>::ULE>())); |
344 | } |
345 | |
346 | let (encoders, remaining_offset) = utils::generate_per_field_offsets( |
347 | sized_fields, |
348 | true, |
349 | |field, prev_offset_ident, size_ident| { |
350 | let ty = &field.field.ty; |
351 | let accessor = &field.accessor; |
352 | quote!( |
353 | #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid indices |
354 | let out = &mut dst[#prev_offset_ident .. #prev_offset_ident + #size_ident]; |
355 | let unaligned = zerovec::ule::AsULE::to_unaligned(self.#accessor); |
356 | let unaligned_slice = &[unaligned]; |
357 | let src = <<#ty as zerovec::ule::AsULE>::ULE as zerovec::ule::ULE>::as_byte_slice(unaligned_slice); |
358 | out.copy_from_slice(src); |
359 | ) |
360 | }, |
361 | ); |
362 | |
363 | let last_encode_len = unsized_field_info.encode_len(); |
364 | let last_encode_write = unsized_field_info.encode_write(quote!(out)); |
365 | quote!( |
366 | unsafe impl #maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for #name #maybe_lt_bound { |
367 | // Safety: unimplemented as the other two are implemented |
368 | fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { |
369 | unreachable!("other two methods implemented" ) |
370 | } |
371 | |
372 | // Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms |
373 | fn encode_var_ule_len(&self) -> usize { |
374 | #(#lengths +)* #last_encode_len |
375 | } |
376 | |
377 | // Safety: converts each element to ULE form and writes them in sequence |
378 | fn encode_var_ule_write(&self, mut dst: &mut [u8]) { |
379 | debug_assert_eq!(self.encode_var_ule_len(), dst.len()); |
380 | #encoders |
381 | |
382 | #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid remainder |
383 | let out = &mut dst[#remaining_offset..]; |
384 | #last_encode_write |
385 | } |
386 | } |
387 | |
388 | // This second impl exists to allow for using EncodeAsVarULE without cloning |
389 | // |
390 | // A blanket impl cannot exist without coherence issues |
391 | unsafe impl #maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for &'_ #name #maybe_lt_bound { |
392 | // Safety: unimplemented as the other two are implemented |
393 | fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { |
394 | unreachable!("other two methods implemented" ) |
395 | } |
396 | |
397 | // Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms |
398 | fn encode_var_ule_len(&self) -> usize { |
399 | (**self).encode_var_ule_len() |
400 | } |
401 | |
402 | // Safety: converts each element to ULE form and writes them in sequence |
403 | fn encode_var_ule_write(&self, mut dst: &mut [u8]) { |
404 | (**self).encode_var_ule_write(dst) |
405 | } |
406 | } |
407 | ) |
408 | } |
409 | |
410 | /// Represents a VarULE-compatible type that would typically |
411 | /// be found behind a `Cow<'a, _>` in the last field, and is represented |
412 | /// roughly the same in owned and borrowed versions |
413 | #[derive (Copy, Clone, Debug)] |
414 | enum OwnULETy<'a> { |
415 | /// [T] where T: AsULE<ULE = Self> |
416 | Slice(&'a Type), |
417 | /// str |
418 | Str, |
419 | } |
420 | |
421 | /// Represents the type of the last field of the struct |
422 | #[derive (Clone, Debug)] |
423 | enum UnsizedFieldKind<'a> { |
424 | Cow(OwnULETy<'a>), |
425 | ZeroVec(&'a Type), |
426 | VarZeroVec(&'a Type), |
427 | /// Custom VarULE type, and the identifier corresponding to the VarULE type |
428 | Custom(&'a TypePath, Ident), |
429 | |
430 | // Generally you should be using the above ones for maximum zero-copy, but these will still work |
431 | Growable(OwnULETy<'a>), |
432 | Boxed(OwnULETy<'a>), |
433 | Ref(OwnULETy<'a>), |
434 | } |
435 | |
436 | #[derive (Clone, Debug)] |
437 | struct UnsizedField<'a> { |
438 | kind: UnsizedFieldKind<'a>, |
439 | field: FieldInfo<'a>, |
440 | } |
441 | |
442 | struct UnsizedFields<'a> { |
443 | fields: Vec<UnsizedField<'a>>, |
444 | } |
445 | |
446 | impl<'a> UnsizedFields<'a> { |
447 | fn new(fields: Vec<UnsizedField<'a>>) -> Self { |
448 | assert!(!fields.is_empty(), "Must have at least one unsized field" ); |
449 | Self { fields } |
450 | } |
451 | |
452 | // Get the corresponding VarULE type that can store all of these |
453 | fn varule_ty(&self) -> TokenStream2 { |
454 | if self.fields.len() == 1 { |
455 | self.fields[0].kind.varule_ty() |
456 | } else { |
457 | quote!(zerovec::ule::MultiFieldsULE) |
458 | } |
459 | } |
460 | |
461 | // Get the accessor field name in the VarULE type |
462 | fn varule_accessor(&self) -> TokenStream2 { |
463 | if self.fields.len() == 1 { |
464 | self.fields[0].field.accessor.clone() |
465 | } else if self.fields[0].field.field.ident.is_some() { |
466 | quote!(unsized_fields) |
467 | } else { |
468 | // first unsized field |
469 | self.fields[0].field.accessor.clone() |
470 | } |
471 | } |
472 | |
473 | // Get the setter for this type for use in struct definition/creation syntax |
474 | fn varule_setter(&self) -> TokenStream2 { |
475 | if self.fields.len() == 1 { |
476 | self.fields[0].field.setter() |
477 | } else if self.fields[0].field.field.ident.is_some() { |
478 | quote!(unsized_fields: ) |
479 | } else { |
480 | quote!() |
481 | } |
482 | } |
483 | |
484 | fn varule_vis(&self) -> TokenStream2 { |
485 | if self.fields.len() == 1 { |
486 | self.fields[0].field.field.vis.to_token_stream() |
487 | } else { |
488 | // Always private |
489 | quote!() |
490 | } |
491 | } |
492 | |
493 | // Check if the type has a ZeroFrom impl |
494 | fn has_zf(&self) -> bool { |
495 | self.fields.iter().all(|f| f.kind.has_zf()) |
496 | } |
497 | |
498 | // Takes all unsized fields on self and encodes them into a byte slice `out` |
499 | fn encode_write(&self, out: TokenStream2) -> TokenStream2 { |
500 | if self.fields.len() == 1 { |
501 | self.fields[0].encode_func(quote!(encode_var_ule_write), quote!(#out)) |
502 | } else { |
503 | let mut lengths = vec![]; |
504 | let mut writers = vec![]; |
505 | for (i, field) in self.fields.iter().enumerate() { |
506 | lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!())); |
507 | let (encodeable_ty, encodeable) = field.encodeable_tokens(); |
508 | let varule_ty = field.kind.varule_ty(); |
509 | writers |
510 | .push(quote!(multi.set_field_at::<#varule_ty, #encodeable_ty>(#i, #encodeable))) |
511 | } |
512 | |
513 | quote!( |
514 | let lengths = [#(#lengths),*]; |
515 | let mut multi = zerovec::ule::MultiFieldsULE::new_from_lengths_partially_initialized(&lengths, #out); |
516 | unsafe { |
517 | #(#writers;)* |
518 | } |
519 | ) |
520 | } |
521 | } |
522 | |
523 | // Takes all unsized fields on self and returns the length needed for encoding into a byte slice |
524 | fn encode_len(&self) -> TokenStream2 { |
525 | if self.fields.len() == 1 { |
526 | self.fields[0].encode_func(quote!(encode_var_ule_len), quote!()) |
527 | } else { |
528 | let mut lengths = vec![]; |
529 | for field in self.fields.iter() { |
530 | lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!())); |
531 | } |
532 | quote!(zerovec::ule::MultiFieldsULE::compute_encoded_len_for(&[#(#lengths),*])) |
533 | } |
534 | } |
535 | |
536 | /// Constructs ZeroFrom setters for each field of the stack type |
537 | fn push_zf_setters(&self, lt: &Lifetime, field_inits: &mut Vec<TokenStream2>) { |
538 | let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom); |
539 | if self.fields.len() == 1 { |
540 | let accessor = self.fields[0].field.accessor.clone(); |
541 | let setter = self.fields[0].field.setter(); |
542 | let last_field_ty = &self.fields[0].field.field.ty; |
543 | let last_field_ule_ty = self.fields[0].kind.varule_ty(); |
544 | field_inits.push(quote!(#setter <#last_field_ty as #zerofrom_trait <#lt, #last_field_ule_ty>>::zero_from(&other.#accessor) )); |
545 | } else { |
546 | let multi_accessor = self.varule_accessor(); |
547 | for (i, field) in self.fields.iter().enumerate() { |
548 | let setter = field.field.setter(); |
549 | let field_ty = &field.field.field.ty; |
550 | let field_ule_ty = field.kind.varule_ty(); |
551 | |
552 | field_inits.push(quote!(#setter unsafe { |
553 | <#field_ty as #zerofrom_trait <#lt, #field_ule_ty>>::zero_from(&other.#multi_accessor.get_field::<#field_ule_ty>(#i)) |
554 | })); |
555 | } |
556 | } |
557 | } |
558 | |
559 | /// In case this needs custom validation code, return it |
560 | /// |
561 | /// The code will validate a variable known as `last_field_bytes` |
562 | fn varule_validator(&self) -> Option<TokenStream2> { |
563 | if self.fields.len() == 1 { |
564 | None |
565 | } else { |
566 | let mut validators = vec![]; |
567 | for (i, field) in self.fields.iter().enumerate() { |
568 | let varule_ty = field.kind.varule_ty(); |
569 | validators.push(quote!(multi.validate_field::<#varule_ty>(#i)?;)); |
570 | } |
571 | |
572 | Some(quote!( |
573 | let multi = zerovec::ule::MultiFieldsULE::parse_byte_slice(last_field_bytes)?; |
574 | unsafe { |
575 | #(#validators)* |
576 | } |
577 | )) |
578 | } |
579 | } |
580 | } |
581 | |
582 | impl<'a> UnsizedField<'a> { |
583 | fn new( |
584 | field: &'a Field, |
585 | index: usize, |
586 | custom_varule_ident: Option<Ident>, |
587 | ) -> Result<Self, String> { |
588 | Ok(UnsizedField { |
589 | kind: UnsizedFieldKind::new(&field.ty, custom_varule_ident)?, |
590 | field: FieldInfo::new_for_field(field, index), |
591 | }) |
592 | } |
593 | |
594 | /// Call `<Self as EncodeAsVarULE<V>>::#method(self.accessor #additional_args)` after adjusting |
595 | /// Self and self.accessor to be the right types |
596 | fn encode_func(&self, method: TokenStream2, additional_args: TokenStream2) -> TokenStream2 { |
597 | let encodeas_trait = quote!(zerovec::ule::EncodeAsVarULE); |
598 | let (encodeable_ty, encodeable) = self.encodeable_tokens(); |
599 | let varule_ty = self.kind.varule_ty(); |
600 | quote!(<#encodeable_ty as #encodeas_trait<#varule_ty>>::#method(#encodeable, #additional_args)) |
601 | } |
602 | |
603 | /// Returns (encodeable_ty, encodeable) |
604 | fn encodeable_tokens(&self) -> (TokenStream2, TokenStream2) { |
605 | let accessor = self.field.accessor.clone(); |
606 | let value = quote!(self.#accessor); |
607 | let encodeable = self.kind.encodeable_value(value); |
608 | let encodeable_ty = self.kind.encodeable_ty(); |
609 | (encodeable_ty, encodeable) |
610 | } |
611 | } |
612 | |
613 | impl<'a> UnsizedFieldKind<'a> { |
614 | /// Construct a UnsizedFieldKind for the type of a UnsizedFieldKind if possible |
615 | fn new( |
616 | ty: &'a Type, |
617 | custom_varule_ident: Option<Ident>, |
618 | ) -> Result<UnsizedFieldKind<'a>, String> { |
619 | static PATH_TYPE_IDENTITY_ERROR: &str = |
620 | "Can only automatically detect corresponding VarULE types for path types \ |
621 | that are Cow, ZeroVec, VarZeroVec, Box, String, or Vec" ; |
622 | static PATH_TYPE_GENERICS_ERROR: &str = |
623 | "Can only automatically detect corresponding VarULE types for path \ |
624 | types with at most one lifetime and at most one generic parameter. VarZeroVecFormat |
625 | types are not currently supported" ; |
626 | match *ty { |
627 | Type::Reference(ref tyref) => OwnULETy::new(&tyref.elem, "reference" ).map(UnsizedFieldKind::Ref), |
628 | Type::Path(ref typath) => { |
629 | if let Some(custom_varule_ident) = custom_varule_ident { |
630 | return Ok(UnsizedFieldKind::Custom(typath, custom_varule_ident)); |
631 | } |
632 | if typath.path.segments.len() != 1 { |
633 | return Err("Can only automatically detect corresponding VarULE types for \ |
634 | path types with a single path segment" .into()); |
635 | } |
636 | let segment = typath.path.segments.first().unwrap(); |
637 | match segment.arguments { |
638 | PathArguments::None => { |
639 | if segment.ident == "String" { |
640 | Ok(UnsizedFieldKind::Growable(OwnULETy::Str)) |
641 | } else { |
642 | Err(PATH_TYPE_IDENTITY_ERROR.into()) |
643 | } |
644 | } |
645 | PathArguments::AngleBracketed(ref params) => { |
646 | // At most one lifetime and exactly one generic parameter |
647 | let mut lifetime = None; |
648 | let mut generic = None; |
649 | for param in ¶ms.args { |
650 | match param { |
651 | GenericArgument::Lifetime(ref lt) if lifetime.is_none() => { |
652 | lifetime = Some(lt) |
653 | } |
654 | GenericArgument::Type(ref ty) if generic.is_none() => { |
655 | generic = Some(ty) |
656 | } |
657 | _ => return Err(PATH_TYPE_GENERICS_ERROR.into()), |
658 | } |
659 | } |
660 | |
661 | // Must be exactly one generic parameter |
662 | // (we've handled the zero generics case already) |
663 | let generic = if let Some(g) = generic { |
664 | g |
665 | } else { |
666 | return Err(PATH_TYPE_GENERICS_ERROR.into()); |
667 | }; |
668 | |
669 | let ident = segment.ident.to_string(); |
670 | |
671 | if lifetime.is_some() { |
672 | match &*ident { |
673 | "ZeroVec" => Ok(UnsizedFieldKind::ZeroVec(generic)), |
674 | "VarZeroVec" => Ok(UnsizedFieldKind::VarZeroVec(generic)), |
675 | "Cow" => OwnULETy::new(generic, "Cow" ).map(UnsizedFieldKind::Cow), |
676 | _ => Err(PATH_TYPE_IDENTITY_ERROR.into()), |
677 | } |
678 | } else { |
679 | match &*ident { |
680 | "Vec" => Ok(UnsizedFieldKind::Growable(OwnULETy::Slice(generic))), |
681 | "Box" => OwnULETy::new(generic, "Box" ).map(UnsizedFieldKind::Boxed), |
682 | _ => Err(PATH_TYPE_IDENTITY_ERROR.into()), |
683 | } |
684 | } |
685 | } |
686 | _ => Err("Can only automatically detect corresponding VarULE types for path types \ |
687 | with none or angle bracketed generics" .into()), |
688 | } |
689 | } |
690 | _ => Err("Can only automatically detect corresponding VarULE types for path and reference types" .into()), |
691 | } |
692 | } |
693 | /// Get the tokens for the corresponding VarULE type |
694 | fn varule_ty(&self) -> TokenStream2 { |
695 | match *self { |
696 | Self::Ref(ref inner) |
697 | | Self::Cow(ref inner) |
698 | | Self::Boxed(ref inner) |
699 | | Self::Growable(ref inner) => { |
700 | let inner_ule = inner.varule_ty(); |
701 | quote!(#inner_ule) |
702 | } |
703 | Self::Custom(_, ref name) => quote!(#name), |
704 | Self::ZeroVec(ref inner) => quote!(zerovec::ZeroSlice<#inner>), |
705 | Self::VarZeroVec(ref inner) => quote!(zerovec::VarZeroSlice<#inner>), |
706 | } |
707 | } |
708 | |
709 | // Takes expr `value` and returns it as a value that can be encoded via EncodeAsVarULE |
710 | fn encodeable_value(&self, value: TokenStream2) -> TokenStream2 { |
711 | match *self { |
712 | Self::Ref(_) | Self::Cow(_) | Self::Growable(_) | Self::Boxed(_) => quote!(&*#value), |
713 | |
714 | Self::Custom(..) => quote!(&#value), |
715 | Self::ZeroVec(_) | Self::VarZeroVec(_) => quote!(&*#value), |
716 | } |
717 | } |
718 | |
719 | /// Returns the EncodeAsVarULE type this can be represented as, the same returned by encodeable_value() |
720 | fn encodeable_ty(&self) -> TokenStream2 { |
721 | match *self { |
722 | Self::Ref(ref inner) |
723 | | Self::Cow(ref inner) |
724 | | Self::Growable(ref inner) |
725 | | Self::Boxed(ref inner) => inner.varule_ty(), |
726 | |
727 | Self::Custom(ref path, _) => quote!(#path), |
728 | Self::ZeroVec(ref ty) => quote!(zerovec::ZeroSlice<#ty>), |
729 | Self::VarZeroVec(ref ty) => quote!(zerovec::VarZeroSlice<#ty>), |
730 | } |
731 | } |
732 | |
733 | fn has_zf(&self) -> bool { |
734 | matches!( |
735 | *self, |
736 | Self::Ref(_) | Self::Cow(_) | Self::ZeroVec(_) | Self::VarZeroVec(_) | Self::Custom(..) |
737 | ) |
738 | } |
739 | } |
740 | |
741 | impl<'a> OwnULETy<'a> { |
742 | fn new(ty: &'a Type, context: &str) -> Result<Self, String> { |
743 | match *ty { |
744 | Type::Slice(ref slice: &TypeSlice) => Ok(OwnULETy::Slice(&slice.elem)), |
745 | Type::Path(ref typath: &TypePath) => { |
746 | if typath.path.is_ident("str" ) { |
747 | Ok(OwnULETy::Str) |
748 | } else { |
749 | Err(format!("Cannot automatically detect corresponding VarULE type for non-str path type inside a {context}" )) |
750 | } |
751 | } |
752 | _ => Err(format!("Cannot automatically detect corresponding VarULE type for non-slice/path type inside a {context}" )), |
753 | } |
754 | } |
755 | |
756 | /// Get the tokens for the corresponding VarULE type |
757 | fn varule_ty(&self) -> TokenStream2 { |
758 | match *self { |
759 | OwnULETy::Slice(s: &Type) => quote!([#s]), |
760 | OwnULETy::Str => quote!(str), |
761 | } |
762 | } |
763 | } |
764 | |