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