minicbor_derive/lib.rs
1//! Procedural macros to derive minicbor's `Encode`, `Decode`, and `CborLen`
2//! traits.
3//!
4//! Deriving is supported for `struct`s and `enum`s. The encoding is optimised
5//! for forward and backward compatibility and the overall approach is
6//! influenced by [Google's Protocol Buffers][1].
7//!
8//! The goal is that ideally a change to a type still allows older software,
9//! which is unaware of the changes, to decode values of the changed type
10//! (forward compatibility) and newer software, to decode values of types
11//! encoded by older software, which do not include the changes made to the
12//! type (backward compatibility).
13//!
14//! In order to reach this goal, the encoding has the following characteristics:
15//!
16//! 1. The encoding does not contain any names, i.e. no field names, type names
17//! or variant names. Instead, every field and every constructor needs to be
18//! annotated with an index number, e.g. `#[n(1)]`.
19//!
20//! 2. Unknown fields are ignored during decoding.[^1]
21//!
22//! 3. Optional types default to `None` if their value is not present during
23//! decoding.
24//!
25//! 4. Optional enums default to `None` if an unknown variant is encountered
26//! during decoding.
27//!
28//! Item **1** ensures that names can be changed freely without compatibility
29//! concerns. Item **2** ensures that new fields do not affect older software.
30//! Item **3** ensures that newer software can stop producing optional values.
31//! Item **4** ensures that enums can get new variants that older software is
32//! not aware of. By "fields" we mean the elements of structs and tuple structs
33//! as well as enum structs and enum tuples. In addition, it is a compatible
34//! change to turn a unit variant into a struct or tuple variant if all fields
35//! are optional.
36//!
37//! From the above it should be obvious that *non-optional fields need to be
38//! present forever*, so they should only be part of a type after careful
39//! consideration.
40//!
41//! It should be emphasised that an `enum` itself can not be changed in a
42//! compatible way. An unknown variant causes an error. It is only when they
43//! are declared as an optional field type that unknown variants of an enum
44//! are mapped to `None`. In other words, *only structs can be used as
45//! top-level types in a forward and backward compatible way, enums can not.*
46//!
47//! # Example
48//!
49//! ```
50//! use minicbor::{Encode, Decode};
51//!
52//! #[derive(Encode, Decode)]
53//! struct Point {
54//! #[n(0)] x: f64,
55//! #[n(1)] y: f64
56//! }
57//!
58//! #[derive(Encode, Decode)]
59//! struct ConvexHull {
60//! #[n(0)] left: Point,
61//! #[n(1)] right: Point,
62//! #[n(2)] points: Vec<Point>,
63//! #[n(3)] state: Option<State>
64//! }
65//!
66//! #[derive(Encode, Decode)]
67//! enum State {
68//! #[n(0)] Start,
69//! #[n(1)] Search { #[n(0)] info: u64 }
70//! }
71//! ```
72//!
73//! In this example the following changes would be compatible in both
74//! directions:
75//!
76//! - Renaming every identifier.
77//!
78//! - Adding optional fields to `Point`, `ConvexHull`, `State::Start` or
79//! `State::Search`.
80//!
81//! - Adding more variants to `State` *iff* `State` is only decoded as part of
82//! `ConvexHull`. Direct decoding of `State` would produce an `UnknownVariant`
83//! error for those new variants.
84//!
85//! [1]: https://developers.google.com/protocol-buffers/
86//!
87//! # Supported attributes
88//!
89//! - [`#[n(...)]` and `#[cbor(n(...))]`](#n-and-b-or-cborn-and-cborb)
90//! - [`#[b(...)]` and `#[cbor(b(...))]`](#n-and-b-or-cborn-and-cborb)
91//! - [`#[cbor(borrow)]`](#cborborrow)
92//! - [`#[cbor(array)]`](#cborarray)
93//! - [`#[cbor(map)]`](#cbormap)
94//! - [`#[cbor(index_only)]`](#cborindex_only)
95//! - [`#[cbor(transparent)]`](#cbortransparent)
96//! - [`#[cbor(skip)]`](#cborskip)
97//! - [`#[cbor(default)]`](#cbordefault)
98//! - [`#[cbor(tag(...))]`](#cbortag)
99//! - [`#[cbor(decode_with)]`](#cbordecode_with--path)
100//! - [`#[cbor(encode_with)]`](#cborencode_with--path)
101//! - [`#[cbor(with)]`](#cborwith--path)
102//! - [`#[cbor(nil)]`](#cbornil--path)
103//! - [`#[cbor(has_nil)]`](#cborhas_nil)
104//! - [`#[cbor(is_nil)]`](#cboris_nil--path)
105//! - [`#[cbor(decode_bound)]`](#cbordecode_bound--)
106//! - [`#[cbor(encode_bound)]`](#cborencode_bound--)
107//! - [`#[cbor(bound)]`](#cborbound)
108//! - [`#[cbor(context_bound)]`](#cborcontext_bound--)
109//! - [`#[cbor(cbor_len)]`](#cborcbor_len--path)
110//!
111//! ## `#[n(...)]` and `#[b(...)]` (or `#[cbor(n(...))]` and `#[cbor(b(...))]`)
112//!
113//! Each field and variant needs to be annotated with an index number, which is
114//! used instead of the name. `b` is a syntactic shorthand for writing
115//! `#[cbor(n(...), borrow)]` (see [`#[cbor(borrow)]`](#cborborrow) for details).
116//!
117//! ## `#[cbor(borrow)]`
118//!
119//! When attached to a field this attribute indicates that the value borrows from
120//! the decoding input. This means that if a field is annotated with `#[borrow(...)]`,
121//! all of its lifetimes will be constrained to the input lifetime (`'bytes`).
122//!
123//! Further, if the type is a `Cow<'_, str>`, `Cow<'_, minicbor::bytes::ByteSlice>`
124//! or `Cow<'_, [u8]>`, the generated code will decode the `str`, `ByteSlice` or
125//! `[u8]` and construct a `Cow::Borrowed` variant, contrary to the regular `Cow`
126//! impls of `Decode` and `DecodeBytes` which produce owned values.
127//!
128//! Note that some values implicitly borrow (see section
129//! [Implicit borrowing](#implicit-borrowing) below).
130//!
131//! `borrow` can also specify, which lifetimes should be constrained, e.g.
132//! `#[cbor(borrow = "'a + 'b")]`.
133//!
134//! ## `#[cbor(array)]`
135//!
136//! Uses a CBOR array to encode the annotated struct, enum or enum variant.
137//! When used with an enum it applies to all its variants but can be overriden
138//! per variant. See section [CBOR encoding](#cbor-encoding) for details.
139//!
140//! If neither `#[cbor(array)]` nor `#[cbor(map)]` are specified, `#[cbor(array)]`
141//! is used by default.
142//!
143//! ## `#[cbor(map)]`
144//!
145//! Use a CBOR map to encode the annotated struct, enum or enum variant.
146//! When used with an enum it applies to all its variants but can be overriden
147//! per variant. See section [CBOR encoding](#cbor-encoding) for details.
148//!
149//! If neither `#[cbor(array)]` nor `#[cbor(map)]` are specified, `#[cbor(array)]`
150//! is used by default.
151//!
152//! ## `#[cbor(index_only)]`
153//!
154//! Enumerations which do not contain fields may have this attribute attached to
155//! them. This changes the encoding to encode only the variant index (cf. section
156//! [CBOR encoding](#cbor-encoding) for details).
157//!
158//! ## `#[cbor(flat)]`
159//!
160//! This attribute can be attached to enums. It provides a "shallow" encoding,
161//! in such a way that each variant is a encoded as a variable-sized array
162//! containing as its first element the index of the variant, and further elements
163//! correspond to the variant fields in order.
164//!
165//! ## `#[cbor(transparent)]`
166//!
167//! This attribute can be attached to structs with exactly one field (aka newtypes).
168//! If present, the generated `Encode` and `Decode` impls will just forward the
169//! respective `encode` and `decode` calls to the inner type, i.e. the resulting
170//! CBOR representation will be identical to the one of the inner type.
171//!
172//! ## `#[cbor(skip)]`
173//!
174//! This attribute can be attached to fields in structs and enums and prevents
175//! those fields from being encoded. Field types must implement [`Default`] and
176//! when decoding the fields are initialised with `Default::default()`.
177//!
178//! ## `#[cbor(default)]`
179//!
180//! This attribute can be attached to fields in structs and enums. When decoding,
181//! missing values do not cause an error, but the [`Default`] value of the field's
182//! type is used to initialise the field.
183//!
184//! ## `#[cbor(tag(...))]`
185//!
186//! This attribute can be attached to structs, enums and their fields. Its argument
187//! is a base-10 unsigned integer which is encoded as the CBOR tag of the value.
188//! Decoding will also attempt to read the tag and fails otherwise.
189//!
190//! ## `#[cbor(decode_with = "<path>")]`
191//!
192//! When applied to a field of type `T`, the function denoted by `<path>` will be
193//! used to decode `T`. The function needs to be equivalent to the following type:
194//!
195//! ```no_run
196//! use minicbor::decode::{Decoder, Error};
197//!
198//! fn decode<'b, Ctx, T: 'b>(d: &mut Decoder<'b>, ctx: &mut Ctx) -> Result<T, Error> {
199//! todo!()
200//! }
201//! ```
202//!
203//! Please note that if the decode function is generic in its context parameter that the
204//! derive macro uses the type variable name `Ctx`.
205//!
206//! ## `#[cbor(encode_with = "<path>")]`
207//!
208//! When applied to a field of type `T`, the function denoted by `<path>` will be
209//! used to encode `T`. The function needs to be equivalent to the following type:
210//!
211//! ```no_run
212//! use minicbor::encode::{Encoder, Error, Write};
213//!
214//! fn encode<Ctx, T, W: Write>(v: &T, e: &mut Encoder<W>, ctx: &mut Ctx) -> Result<(), Error<W::Error>> {
215//! todo!()
216//! }
217//! ```
218//!
219//! Please note that if the encode function is generic in its context parameter that the
220//! derive macro uses the type variable name `Ctx`.
221//!
222//! ## `#[cbor(with = "<path>")]`
223//!
224//! Combines [`#[cbor(decode_with = "...")]`](#cbordecode_with--path) and
225//! [`#[cbor(encode_with = "...")]`](#cborencode_with--path). Here, `<path>` denotes
226//! a module that contains functions named `encode` and `decode` that satisfy the
227//! respective type signatures mentioned in `encode_with` and `decode_with`.
228//! If `CborLen` is also derived, the module is assumed to contain a function named
229//! `cbor_len` with a signature matching the one described in
230//! [`#[cbor(cbor_len = "...")]`](#cborcbor_len--path) below.
231//!
232//! ## `#[cbor(nil = "<path>")]`
233//!
234//! Only valid in conjuction with [`#[cbor(decode_with = "...")]`](#cbordecode_with--path).
235//! If present, `<path>` denotes a function to create a nil-like value of type `T`.
236//! See `minicbor::Decode::nil` for details. The function needs to be equivalent to the
237//! following type:
238//!
239//! ```no_run
240//! fn nil<T>() -> Option<T> {
241//! todo!()
242//! }
243//! ```
244//!
245//! ## `#[cbor(has_nil)]`
246//!
247//! Only valid in conjuction with [`#[cbor(with = "...")]`](#cborwith--path). If present,
248//! the attribute signals that the module denoted by `with` also contains functions `nil`
249//! and `is_nil` to create nil values and to check if a value is a nil value.
250//!
251//! ## `#[cbor(is_nil = "<path>")]`
252//!
253//! Only valid in conjuction with [`#[cbor(encode_with = "...")]`](#cborencode_with--path).
254//! If present, `<path>` denotes a function to check if a value of type `T` is a
255//! nil-like value. See `minicbor::Encode::is_nil` for details. The function needs to
256//! be equivalent to the following type:
257//!
258//! ```no_run
259//! fn is_nil<T>(v: &T) -> bool {
260//! todo!()
261//! }
262//! ```
263//!
264//! ## `#[cbor(cbor_len = "<path>")]`
265//!
266//! Only applicable when deriving `CborLen`. When applied to a field of type `T`, the
267//! function denoted by `<path>` will be used to calculate the CBOR length in bytes.
268//! The function needs to be equivalent to the following type:
269//!
270//! ```no_run
271//! fn cbor_len<Ctx, T>(val: &T, ctx: &mut Ctx) -> usize {
272//! todo!()
273//! }
274//! ```
275//!
276//! Please note that if the cbor_len function is generic in its context parameter that the
277//! derive macro uses the type variable name `Ctx`.
278//!
279//! ## `#[cbor(decode_bound = "...")]`
280//!
281//! When applied to a generic field, this attribute overrides any implicit type
282//! parameter bounds generated by `minicbor-derive` for the derived `Decode` impl.
283//!
284//! ## `#[cbor(encode_bound = "...")]`
285//!
286//! When applied to a generic field, this attribute overrides any implicit type
287//! parameter bounds generated by `minicbor-derive` for the derived `Encode` impl.
288//!
289//! ## `#[cbor(bound)]`
290//!
291//! Combines [`#[cbor(encode_bound = "...")]`](#cborencode_bound--) and
292//! [`#[cbor(decode_bound = "...")]`](#cbordecode_bound--), i.e. the bound applies
293//! to the derived `Encode` and `Decode` impl.
294//!
295//! ## `#[cbor(context_bound = "...")]`
296//!
297//! When deriving `Encode` or `Decode` for a type which has parts that constrain the
298//! generic context type parameter, this attribute can be used to add the required
299//! trait bounds to the context type parameter. The attribute can either be repeated
300//! or the bounds can be listed as '+'-separated value, e.g. "A + B + C".
301//!
302//! ### Example
303//! <details>
304//! <summary>A combined context.</summary>
305//!
306//! ```no_run
307//! use minicbor::{Encode, Decode};
308//! use minicbor::decode::{self, Decoder};
309//!
310//! // Some decodable type that uses a custom context.
311//! struct A(u8);
312//!
313//! // `A`'s context type.
314//! struct AC { a: u8 }
315//!
316//! impl AsMut<AC> for AC {
317//! fn as_mut(&mut self) -> &mut AC { self }
318//! }
319//!
320//! impl<'b, C: AsMut<AC>> Decode<'b, C> for A {
321//! fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, decode::Error> {
322//! Ok(A(ctx.as_mut().a))
323//! }
324//! }
325//!
326//! // Another decodable type that uses a different context.
327//! struct B(u8);
328//!
329//! // `B`'s context type.
330//! struct BC { b: u8 }
331//!
332//! impl AsMut<BC> for BC {
333//! fn as_mut(&mut self) -> &mut BC { self }
334//! }
335//!
336//! impl<'b, C: AsMut<BC>> Decode<'b, C> for B {
337//! fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, decode::Error> {
338//! Ok(B(ctx.as_mut().b))
339//! }
340//! }
341//!
342//! // Finally, a type that combines `A` and `B` and therefore also needs to provide
343//! // a context that can be used by both of them.
344//! #[derive(Decode)]
345//! #[cbor(context_bound = "AsMut<AC> + AsMut<BC>")]
346//! struct C {
347//! #[n(0)] a: A,
348//! #[n(1)] b: B
349//! }
350//!
351//! // The combined context type.
352//! struct CC(AC, BC);
353//!
354//! impl AsMut<AC> for CC {
355//! fn as_mut(&mut self) -> &mut AC {
356//! &mut self.0
357//! }
358//! }
359//!
360//! impl AsMut<BC> for CC {
361//! fn as_mut(&mut self) -> &mut BC {
362//! &mut self.1
363//! }
364//! }
365//!
366//! ```
367//! </details>
368//!
369//! # Implicit borrowing
370//!
371//! Apart from the explicit borrowing with [`#[b(...)]`](#n-and-b-or-cborn-and-cborb),
372//! the following types implicitly borrow from the decoding input, which means
373//! their lifetimes are constrained by the input lifetime:
374//!
375//! - `&'_ str`
376//! - `&'_ minicbor::bytes::ByteSlice`
377//! - `Option<&'_ str>`
378//! - `Option<&'_ minicbor::bytes::ByteSlice>`
379//!
380//! ## What about `&[u8]`?
381//!
382//! `&[u8]` is a special case of `&[T]`. The lack of trait impl specialisation
383//! in Rust makes it difficult to provide optimised support for byte slices.
384//! The generic `[T]` impl of `Encode` produces an array of `T`s. To specifically
385//! encode to and decode from CBOR bytes, the types `ByteSlice`, `ByteArray` and
386//! `ByteVec` are provided by `minicbor`. In addition, the attributes
387//! `encode_with`, `decode_with` and `with` can be used with `&[u8]` when deriving,
388//! e.g.
389//!
390//! ```
391//! use minicbor::{Encode, Decode};
392//!
393//! #[derive(Encode, Decode)]
394//! struct Foo<'a> {
395//! #[cbor(n(0), with = "minicbor::bytes")]
396//! field0: &'a [u8],
397//!
398//! #[n(1)]
399//! #[cbor(encode_with = "minicbor::bytes::encode")]
400//! #[cbor(decode_with = "minicbor::bytes::decode")]
401//! field1: &'a [u8],
402//!
403//! #[cbor(n(2), with = "minicbor::bytes")]
404//! field2: Option<&'a [u8]>,
405//!
406//! #[cbor(n(3), with = "minicbor::bytes")]
407//! field3: Vec<u8>,
408//!
409//! #[cbor(n(4), with = "minicbor::bytes")]
410//! field4: [u8; 16]
411//! }
412//! ```
413//!
414//! # CBOR encoding
415//!
416//! The CBOR values produced by a derived `Encode` implementation are of the
417//! following formats.
418//!
419//! ## Structs
420//!
421//! ### Array encoding
422//!
423//! By default or if a struct has the [`#[cbor(array)]`](#cborarray) attribute,
424//! it will be represented as a CBOR array. Its index numbers are represened by
425//! the position of the field value in this array. Any gaps between index numbers
426//! are filled with CBOR NULL values and `Option`s which are `None` likewise
427//! end up as NULLs in this array.
428//!
429//! ```text
430//! <<struct-as-array encoding>> =
431//! `array(n)`
432//! item_0
433//! item_1
434//! ...
435//! item_n-1
436//! ```
437//!
438//! ### Map encoding
439//!
440//! If a struct has the [`#[cbor(map)]`](#cbormap) attribute attached, then it
441//! will be represented as a CBOR map with keys corresponding to the numeric
442//! index value:
443//!
444//! ```text
445//! <<struct-as-map encoding>> =
446//! `map(n)`
447//! `0` item_0
448//! `1` item_1
449//! ...
450//! `n-1` item_n-1
451//! ```
452//!
453//! Optional fields whose value is `None` are not encoded.
454//!
455//! ## Enums
456//!
457//! Unless [`#[cbor(index_only)]`](#cborindex_only) or [`#[cbor(flat)]`](#cborflat)
458//! are used, each enum variant is encoded as a two-element array. The first element
459//! is the variant index and the second the actual variant value.
460//!
461//! If enums do not have fields and the `index_only` attribute is present, only the
462//! variant index is encoded.
463//!
464//! If `flat` is used, an enum variant is encoded as an array with the variant index
465//! as its first element, followed directly by all variant fields (if any).
466//!
467//!
468//! ```text
469//! <<enum encoding>> =
470//! | `array(2)` n <<struct-as-array encoding>> ; if #[cbor(array)]
471//! | `array(2)` n <<struct-as-map encoding>> ; if #[cbor(map)]
472//! | `array(k)` n <<field encoding>>* ; if #[cbor(flat)]
473//! | n ; if #[cbor(index_only)]
474//! ```
475//!
476//! Above, `k` is the number of variant fields plus one.
477//!
478//! ## Which encoding to use?
479//!
480//! The map encoding needs to represent the indexes explicitly in the encoding
481//! which costs at least one extra byte per field value, whereas the array
482//! encoding does not need to encode the indexes. On the other hand, absent
483//! values, i.e. `None`s and gaps between indexes are not encoded with maps but
484//! need to be encoded explicitly with arrays as NULLs which need one byte each.
485//! Which encoding to choose depends therefore on the nature of the type that
486//! should be encoded:
487//!
488//! - *Dense types* are types which contain only few `Option`s or their `Option`s
489//! are assumed to be `Some`s usually. They are best encoded as arrays.
490//!
491//! - *Sparse types* are types with many `Option`s and their `Option`s are usually
492//! `None`s. They are best encoded as maps.
493//!
494//! When selecting the encoding, future changes to the type should be considered
495//! as they may turn a dense type into a sparse one over time. This also applies
496//! to [`#[cbor(index_only)]`](#cborindex_only) which should be used only with
497//! enums which are not expected to ever have fields in their variants.
498//!
499//! [^1]: CBOR items are ignored using `Decoder::skip`. This method requires
500//! feature "alloc" to work for all possible CBOR items. Without "alloc",
501//! indefinite maps or arrays inside of regular maps or arrays can not be skipped
502//! over. If such a combination occurs and `Decoder::skip` was compiled without
503//! feature "alloc", a decoding error is returned.
504
505extern crate proc_macro;
506
507mod decode;
508mod encode;
509mod cbor_len;
510
511pub(crate) mod attrs;
512pub(crate) mod fields;
513pub(crate) mod lifetimes;
514pub(crate) mod variants;
515
516use std::collections::HashSet;
517
518/// Derive the `minicbor::Decode` trait for a struct or enum.
519///
520/// See the [crate] documentation for details.
521#[proc_macro_derive(Decode, attributes(n, b, cbor))]
522pub fn derive_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
523 decode::derive_from(input)
524}
525
526/// Derive the `minicbor::Encode` trait for a struct or enum.
527///
528/// See the [crate] documentation for details.
529#[proc_macro_derive(Encode, attributes(n, b, cbor))]
530pub fn derive_encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
531 encode::derive_from(input)
532}
533
534#[derive(Debug, Copy, Clone, PartialEq, Eq)]
535enum Mode {
536 Encode,
537 Decode
538}
539
540/// Derive the `minicbor::CborLen` trait for a struct or enum.
541///
542/// See the [crate] documentation for details.
543#[proc_macro_derive(CborLen, attributes(n, b, cbor))]
544pub fn derive_cbor_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
545 cbor_len::derive_from(input)
546}
547
548// Helpers ////////////////////////////////////////////////////////////////////
549
550/// Check if the given type is an `Option` whose inner type matches the predicate.
551fn is_option(ty: &syn::Type, pred: impl FnOnce(&syn::Type) -> bool) -> bool {
552 if let syn::Type::Path(t) = ty {
553 if let Some(s) = t.path.segments.last() {
554 if s.ident == "Option" {
555 if let syn::PathArguments::AngleBracketed(b) = &s.arguments {
556 if b.args.len() == 1 {
557 if let syn::GenericArgument::Type(ty) = &b.args[0] {
558 return pred(ty)
559 }
560 }
561 }
562 }
563 }
564 }
565 false
566}
567
568/// Check if the given type is a `Cow` whose inner type matches the predicate.
569fn is_cow(ty: &syn::Type, pred: impl FnOnce(&syn::Type) -> bool) -> bool {
570 if let syn::Type::Path(t) = ty {
571 if let Some(s) = t.path.segments.last() {
572 if s.ident == "Cow" {
573 if let syn::PathArguments::AngleBracketed(b) = &s.arguments {
574 if b.args.len() == 2 {
575 if let syn::GenericArgument::Lifetime(_) = &b.args[0] {
576 if let syn::GenericArgument::Type(ty) = &b.args[1] {
577 return pred(ty)
578 }
579 }
580 }
581 }
582 }
583 }
584 }
585 false
586}
587
588/// Check if the given type is a `&str`.
589fn is_str(ty: &syn::Type) -> bool {
590 if let syn::Type::Path(t) = ty {
591 t.qself.is_none() && t.path.segments.len() == 1 && t.path.segments[0].ident == "str"
592 } else {
593 false
594 }
595}
596
597/// Check if the given type is a `&[u8]`.
598fn is_byte_slice(ty: &syn::Type) -> bool {
599 if let syn::Type::Path(t) = ty {
600 return t.qself.is_none() &&
601 ((t.path.segments.len() == 1 && t.path.segments[0].ident == "ByteSlice")
602 || (t.path.segments.len() == 2
603 && t.path.segments[0].ident == "bytes"
604 && t.path.segments[1].ident == "ByteSlice")
605 || (t.path.segments.len() == 3
606 && t.path.segments[0].ident == "minicbor"
607 && t.path.segments[1].ident == "bytes"
608 && t.path.segments[2].ident == "ByteSlice"))
609 }
610 if let syn::Type::Slice(t) = ty {
611 if let syn::Type::Path(t) = &*t.elem {
612 t.qself.is_none() && t.path.segments.len() == 1 && t.path.segments[0].ident == "u8"
613 } else {
614 false
615 }
616 } else {
617 false
618 }
619}
620
621/// Traverse all field types and collect all type parameters along the way.
622fn collect_type_params<'a, I>(all: &syn::Generics, fields: I) -> HashSet<syn::TypeParam>
623where
624 I: Iterator<Item = &'a fields::Field>
625{
626 use syn::visit::Visit;
627
628 struct Collector {
629 all: Vec<syn::Ident>,
630 found: HashSet<syn::TypeParam>
631 }
632
633 impl<'a> Visit<'a> for Collector {
634 fn visit_field(&mut self, f: &'a syn::Field) {
635 if let syn::Type::Path(ty) = &f.ty {
636 if let Some(t) = ty.path.segments.first() {
637 if self.all.contains(&t.ident) {
638 self.found.insert(syn::TypeParam::from(t.ident.clone()));
639 }
640 }
641 }
642 self.visit_type(&f.ty)
643 }
644
645 fn visit_path(&mut self, p: &'a syn::Path) {
646 if p.leading_colon.is_none() && p.segments.len() == 1 {
647 let id = &p.segments[0].ident;
648 if self.all.contains(id) {
649 self.found.insert(syn::TypeParam::from(id.clone()));
650 }
651 }
652 syn::visit::visit_path(self, p)
653 }
654 }
655
656 let mut c = Collector {
657 all: all.type_params().map(|tp| tp.ident.clone()).collect(),
658 found: HashSet::new()
659 };
660
661 for f in fields {
662 c.visit_field(&f.orig)
663 }
664
665 c.found
666}
667
668fn add_bound_to_type_params<'a, I, A>
669 ( bound: syn::TypeParamBound
670 , params: I
671 , blacklist: &HashSet<syn::TypeParam>
672 , attrs: A
673 , mode: Mode
674 )
675where
676 I: IntoIterator<Item = &'a mut syn::TypeParam>,
677 A: IntoIterator<Item = &'a attrs::Attributes> + Clone
678{
679 let find_type_param = |t: &syn::TypeParam| attrs.clone().into_iter()
680 .find_map(|a| {
681 a.type_params().and_then(|p| match mode {
682 Mode::Encode => p.get_encode(&t.ident),
683 Mode::Decode => p.get_decode(&t.ident)
684 })
685 });
686
687 for p in params {
688 if let Some(t) = find_type_param(p) {
689 p.bounds.extend(t.bounds.iter().cloned())
690 } else if !blacklist.contains(p) {
691 p.bounds.push(bound.clone())
692 }
693 }
694}
695
696fn add_bound_to_matching_type_params<'a, I>
697 ( bound: syn::TypeParamBound
698 , params: I
699 , whitelist: &HashSet<syn::Ident>
700 )
701where
702 I: IntoIterator<Item = &'a mut syn::TypeParam>,
703{
704 for p in params {
705 if whitelist.contains(&p.ident) {
706 p.bounds.push(bound.clone())
707 }
708 }
709}
710
711fn add_typeparam<'a, I>(g: &syn::Generics, mut t: syn::TypeParam, b: Option<I>) -> syn::Generics
712where
713 I: Iterator<Item = &'a syn::TraitBound>
714{
715 let mut g2 = g.clone();
716 if let Some(bounds) = b {
717 t.bounds.extend(bounds.cloned().map(syn::TypeParamBound::Trait))
718 }
719 g2.params = Some(t.into()).into_iter().chain(g2.params).collect();
720 g2
721}
722
723fn gen_ctx_param() -> syn::Result<syn::TypeParam> {
724 syn::parse_str("Ctx")
725}