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