micropb_gen/
config.rs

1//! Configuration options for Protobuf types and fields.
2
3use std::borrow::Cow;
4
5use proc_macro2::{Span, TokenStream};
6use syn::Ident;
7
8use crate::generator::sanitized_ident;
9
10#[derive(Debug, Clone, Copy)]
11#[cfg_attr(test, derive(PartialEq, Eq))]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13/// Sizes of integer types
14pub enum IntSize {
15    /// 8-bit int
16    S8,
17    /// 16-bit int
18    S16,
19    /// 32-bit int
20    S32,
21    /// 64-bit int
22    S64,
23}
24
25impl IntSize {
26    pub(crate) fn type_name(self, signed: bool) -> Ident {
27        let t = match self {
28            IntSize::S8 if signed => "i8",
29            IntSize::S8 => "u8",
30            IntSize::S16 if signed => "i16",
31            IntSize::S16 => "u16",
32            IntSize::S32 if signed => "i32",
33            IntSize::S32 => "u32",
34            IntSize::S64 if signed => "i64",
35            IntSize::S64 => "u64",
36        };
37        Ident::new(t, Span::call_site())
38    }
39
40    pub(crate) fn max_value(self) -> u64 {
41        match self {
42            IntSize::S8 => u8::MAX as u64,
43            IntSize::S16 => u16::MAX as u64,
44            IntSize::S32 => u32::MAX as u64,
45            IntSize::S64 => u64::MAX,
46        }
47    }
48
49    pub(crate) fn min_value(self) -> i64 {
50        match self {
51            IntSize::S8 => i8::MIN as i64,
52            IntSize::S16 => i16::MIN as i64,
53            IntSize::S32 => i32::MIN as i64,
54            IntSize::S64 => i64::MIN,
55        }
56    }
57}
58
59#[derive(Debug, Clone)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61/// Customize encoding and decoding behaviour for a generated field
62pub enum CustomField {
63    /// Fully-qualified type name that replaces the generated type of the field.
64    ///
65    /// This type must implement `FieldEncode` and `FieldDecode`.
66    Type(String),
67    /// Name of the other field that this field will delegate to.
68    ///
69    /// The delegated field must have [`CustomField::Type`] configured. It will handle the decoding
70    /// and encoding of this field's wire value.
71    Delegate(String),
72}
73
74impl CustomField {
75    /// Constructs a [`CustomField::Type`]
76    pub fn from_type(s: &str) -> Self {
77        Self::Type(s.to_owned())
78    }
79
80    /// Constructs a [`CustomField::Delegate`]
81    pub fn from_delegate(s: &str) -> Self {
82        Self::Delegate(s.to_owned())
83    }
84}
85
86#[derive(Debug, Clone, Copy)]
87#[cfg_attr(test, derive(PartialEq, Eq))]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89/// Representation of optional fields in the generated code
90pub enum OptionalRepr {
91    /// Presence of optional field is tracked in a separate bitfield called a hazzer.
92    ///
93    /// Default for non-boxed fields.
94    Hazzer,
95
96    /// Optional field is wrapped in `Option`.
97    ///
98    /// Default for boxed fields.
99    Option,
100
101    /// Represented as a non-optional field.
102    ///
103    /// The field is represented the same way as with [`Hazzer`](Self::Hazzer), but without any
104    /// presence tracking. Note that the presence of the field will always be on for the purpose of
105    /// encoding and decoding, making it different from the implicit presence used by Proto3
106    /// non-optional fields. As such, accessors will always return `Some`, and the `take_*` and
107    /// `clear_*` accessors won't be generated.
108    None,
109}
110
111macro_rules! config_decl {
112    ($($(#[$doc:meta])* $([$placeholder:ident])? $field:ident : $([$placeholder2:ident])? Option<$type:ty>,)+) => {
113        #[non_exhaustive]
114        #[derive(Debug, Clone, Default)]
115        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116        /// Configuration that changes how the code generator handles Protobuf types and fields.
117        /// See [`configure`](crate::Generator::configure) for how configurations are applied.
118        ///
119        /// Configuration fields are set by chaining builder methods:
120        /// ```no_run
121        /// # use micropb_gen::Config;
122        /// Config::new().boxed(true).max_len(12).vec_type("MyVec");
123        /// ```
124        pub struct Config {
125            $(pub(crate) $field: Option<$type>,)+
126        }
127
128        impl Config {
129            /// Create new config
130            pub fn new() -> Self {
131                Self::default()
132            }
133
134            pub(crate) fn merge(&mut self, other: &Self) {
135                $(config_decl!(@merge $([$placeholder])? $field, self, other);)+
136            }
137
138            $(config_decl!(@setter $(#[$doc])* $field: $([$placeholder2])? $type);)+
139        }
140    };
141
142    (@merge $field:ident, $self:ident, $other:ident) => {
143        if let Some(v) = &$other.$field {
144            $self.$field = Some(v.clone());
145        }
146    };
147
148    (@merge [no_inherit] $field:ident, $self:ident, $other:ident) => {
149        $self.$field = $other.$field.clone();
150    };
151
152    (@setter $(#[$doc:meta])* $field:ident: [deref] $type:ty) => {
153        $(#[$doc])*
154        pub fn $field(mut self, s: &str) -> Self {
155            self.$field = Some(s.to_owned());
156            self
157        }
158    };
159
160    (@setter $(#[$doc:meta])* $field:ident: $type:ty) => {
161        $(#[$doc])*
162        pub fn $field(mut self, val: $type) -> Self {
163            self.$field = Some(val);
164            self
165        }
166    };
167}
168
169config_decl! {
170    // Field configs
171
172    /// Max number of elements for fixed-capacity repeated and `map` fields.
173    ///
174    /// This should only be set if [`vec_type`](Config::vec_type) or [`map_type`](Config::map_type)
175    /// is a fix-capacity container, because `max_len` will be used as the 2nd type parameter of
176    /// the container in the generated code.
177    ///
178    /// For example, if `vec_type` is `ArrayVec` and `max_len` is 5, then the generated container
179    /// type will be `ArrayVec<_, 5>`.
180    max_len: Option<u32>,
181
182    /// Max number of bytes for fixed-capacity `string` and `bytes` fields.
183    ///
184    /// Like with [`max_len`](Config::max_len), this should only be set if
185    /// [`string_type`](Config::string_type) or [`vec_type`](Config::vec_type) is a fix-capacity
186    /// container, because `max_bytes` will be used as the 2nd type parameter of the container in
187    /// the generated code.
188    max_bytes: Option<u32>,
189
190    /// Override the integer type of integer fields such as `int32` or `fixed64`.
191    ///
192    /// Change the integer fields to be 8, 16, 32, or 64 bytes. If the integer type is smaller than
193    /// the value on the wire, the value will be truncated to fit.
194    ///
195    /// # Example
196    /// ```no_run
197    /// # use micropb_gen::{Generator, Config, config::IntSize};
198    /// # let mut gen = micropb_gen::Generator::new();
199    /// // Set type of int32 to `i8`
200    /// gen.configure(".Message.int32_field", Config::new().int_size(IntSize::S8));
201    /// // Set type of uint32 to `u64`
202    /// gen.configure(".Message.uint32_field", Config::new().int_size(IntSize::S64));
203    /// ```
204    ///
205    /// # Avoiding 64-bit operations
206    /// Setting a 64-bit int field such as `int64` or `sint64` to >=32 bits makes the code
207    /// generator use 32-bit operations on that field instead of 64-bit operations. This can have
208    /// performance benefits on some 32-bit platforms. Setting all int fields to >=32 bits allows
209    /// `micropb`'s `enable-64bits` feature flag to be turned off, disabling 64-bit operations
210    /// altogether.
211    int_size: Option<IntSize>,
212
213    /// Set attributes for message fields.
214    ///
215    /// The attribute string will be placed before matched fields. The string must be in the syntax
216    /// of 0 or more Rust attributes.
217    ///
218    /// # Example
219    /// ```no_run
220    /// # use micropb_gen::{Generator, Config};
221    /// # let mut gen = micropb_gen::Generator::new();
222    /// // Set field attribute
223    /// gen.configure(".Message.foo", Config::new().field_attributes("#[serde(skip)]"));
224    /// // Unset field attribute
225    /// gen.configure(".Message.foo", Config::new().field_attributes(""));
226    /// ```
227    ///
228    /// # Special cases
229    /// - If applied to an oneof field, the attributes are applied to the oneof field of the
230    /// message struct.
231    /// - If applied to an oneof variant, the attributes are applied to the oneof enum variant in
232    /// the oneof enum definition.
233    /// - If applied to the `._has` suffix, the attributes are applied to the hazzer field of the
234    /// message struct.
235    /// - If applied to the `._unknown` suffix, the attributes are applied to the unknown handler
236    /// of the message struct.
237    field_attributes: [deref] Option<String>,
238
239    /// Wrap the field in a `Box`.
240    ///
241    /// If the field is already wrapped in `Option`, then the field will be of type
242    /// `Option<Box<_>>`.
243    ///
244    /// This config not apply to elements of repeated and `map` fields.
245    boxed: Option<bool>,
246
247    /// Container type that's generated for repeated fields.
248    ///
249    /// For decoding, the provided type must implement `PbVec<T>`. For encoding, the type must
250    /// dereference into `[T]`, where `T` is the type of the element. Moreover, the type must
251    /// implement `Default` in order to generate default values.
252    ///
253    /// If the provided type contains the sequence `$N`, it will be substituted for the value of
254    /// [`max_bytes`](Config::max_bytes) if it's set for this field. Similarly, the sequence `$T`
255    /// will be substituted for the type of the repeated element.
256    ///
257    /// # Example
258    /// ```no_run
259    /// # use micropb_gen::{Generator, Config, config::IntSize};
260    /// # let mut gen = micropb_gen::Generator::new();
261    /// // assuming that .pkg.Message.list is a repeated field of booleans:
262    ///
263    /// // repeated field configured to `Vec<bool>` (dynamic-capacity)
264    /// gen.configure(".pkg.Message.list", Config::new().vec_type("Vec<$T>"));
265    /// // repeated field configured to `arrayvec::ArrayVec<bool, 5>` (fixed-capacity)
266    /// gen.configure(".pkg.Message.list", Config::new().vec_type("arrayvec::ArrayVec<$T, $N>").max_len(5));
267    /// ```
268    vec_type: [deref] Option<String>,
269
270    /// Container type that's generated for `string` fields.
271    ///
272    /// For decoding, the provided type must implement `PbString`. For encoding, the type must
273    /// dereference to `str`. Moreover, the type must implement `Default + TryFrom<&str>` in order
274    /// to generate default values.
275    ///
276    /// If the provided type contains the sequence `$N`, it will be substituted for the value of
277    /// [`max_bytes`](Config::max_bytes) if it's set for this field.
278    ///
279    /// # Example
280    /// ```no_run
281    /// # use micropb_gen::{Generator, Config};
282    /// # let mut gen = micropb_gen::Generator::new();
283    /// // `string` field configured to `String` (dynamic-capacity)
284    /// gen.configure(".pkg.Message.string_field", Config::new().string_type("String"));
285    /// // `string` field configured to `ArrayString<4>` (fixed-capacity)
286    /// gen.configure(".pkg.Message.string_field", Config::new().string_type("ArrayString<$N>").max_bytes(4));
287    /// ```
288    string_type: [deref] Option<String>,
289
290    /// Container type that's generated for `bytes` fields.
291    ///
292    /// For decoding, the provided type must implement `PbBytes`. For encoding, the type must
293    /// dereference to `[u8]`. Moreover, the type must implement `Default + TryFrom<&[u8]>` in
294    /// order to generate default values.
295    ///
296    /// If the provided type contains the sequence `$N`, it will be substituted for the value of
297    /// [`max_bytes`](Config::max_bytes) if it's set for this field.
298    ///
299    /// # Example
300    /// ```no_run
301    /// # use micropb_gen::{Generator, Config};
302    /// # let mut gen = micropb_gen::Generator::new();
303    /// // `bytes` field configured to `Vec<u8>` (dynamic-capacity)
304    /// gen.configure(".pkg.Message.string_field", Config::new().string_type("Vec<u8>"));
305    /// // `bytes` field configured to `Vec<u8, 4>` (fixed-capacity)
306    /// gen.configure(".pkg.Message.string_field", Config::new().string_type("Vec<u8, $N>").max_bytes(4));
307    /// ```
308    bytes_type: [deref] Option<String>,
309
310    /// Container type that's generated for `map` fields.
311    ///
312    /// For decoding, the provided type must implement `PbMap`. For encoding, the type must
313    /// implement `IntoIterator<Item = (&K, &V)>` for `&T`. Moreover, the type must implement
314    /// `Default` in order to generate default values.
315    ///
316    /// If the provided type contains the sequence `$N`, it will be substituted for the value of
317    /// [`max_bytes`](Config::max_bytes) if it's set for this field. Similarly, the sequences `$K`
318    /// and `$V` will be substituted for the types of the map key and value respectively.
319    ///
320    /// # Example
321    /// ```no_run
322    /// # use micropb_gen::{Generator, Config, config::IntSize};
323    /// # let mut gen = micropb_gen::Generator::new();
324    /// // assume that .pkg.Message.map_field is a `map<int32, float>`:
325    ///
326    /// // `map` field configured to `BTreeMap<i32, f32>` (dynamic-capacity)
327    /// gen.configure(".pkg.Message.map_field", Config::new().map_type("BTreeMap<$K, $V>"));
328    /// // `map` field configured to `FnvIndexMap<i32, f32, 4>` (fixed-capacity)
329    /// gen.configure(".pkg.Message.map_field", Config::new().map_type("FnvIndexMap<$K, $V, $N>").max_len(4));
330    /// ```
331    map_type: [deref] Option<String>,
332
333    /// Determine how optional fields are represented.
334    ///
335    /// Presence of optional fields is tracked by either a bitfield in the message struct called a
336    /// hazzer, or by the `Option` type. By default, non-boxed fields use hazzers and boxed fields
337    /// use `Option`. This behaviour can be customized by setting this option.
338    ///
339    /// # Example
340    /// ```no_run
341    /// # use micropb_gen::{Generator, Config, config::OptionalRepr};
342    /// # let mut gen = micropb_gen::Generator::new();
343    /// // `optional1: T` with bitfield entry (default unboxed behaviour)
344    /// gen.configure(".Message.optional1", Config::new().optional_repr(OptionalRepr::Hazzer));
345    /// // `optional2: Option<T>`
346    /// gen.configure(".Message.optional2", Config::new().optional_repr(OptionalRepr::Option));
347    /// // `optional3: Box<T>` with bitfield entry
348    /// gen.configure(".Message.optional3", Config::new().boxed(true)
349    ///                                         .optional_repr(OptionalRepr::Hazzer));
350    /// // `optional4: Option<Box<T>>` (default boxed behaviour)
351    /// gen.configure(".Message.optional4", Config::new().boxed(true)
352    ///                                         .optional_repr(OptionalRepr::Option));
353    /// ```
354    optional_repr: Option<OptionalRepr>,
355
356    /// Replace generated field with an user-provided type. See [`CustomField`] for more info.
357    ///
358    /// Substitute a user-provided type as the type of the field. The encoding and decoding
359    /// behaviour will also be user-provided, so the custom type must implement `FieldEncode` and
360    /// `FieldDecode` and correctly handle the field's wire representation.
361    ///
362    /// Alternatively, a field can be set to "delegate" to another custom field for encoding and
363    /// decoding. In that case, the field won't be generated at all, and its wire value will be
364    /// handled by the delegated field.
365    ///
366    /// This configuration applies to normal field and oneof fields, but won't be applied to
367    /// `oneof` variants.
368    ///
369    /// # Interaction with other configs
370    /// Setting this config option overrides every other config option that affects the field's
371    /// generated type, including `optional_repr`, `int_size`, and `boxed` (but not
372    /// `field_attributes`). If the field is optional, then the custom type is responsible for
373    /// tracking field presence, since custom fields aren't tracked by the hazzer.
374    ///
375    /// # Example
376    /// ```no_run
377    /// # use micropb_gen::{Generator, Config, config::CustomField};
378    /// # let mut gen = micropb_gen::Generator::new();
379    /// // Make the generator generate `foo: crate::CustomHandler` for field `foo`
380    /// gen.configure(
381    ///     ".Message.foo",
382    ///     Config::new().custom_field(CustomField::from_type("crate::CustomHandler"))
383    /// );
384    /// // Decoding and encoding of `bar` will also be handled by the `CustomHandler` assigned to `foo`
385    /// gen.configure(
386    ///     ".Message.bar",
387    ///     Config::new().custom_field(CustomField::from_delegate("foo"))
388    /// );
389    /// ```
390    custom_field: Option<CustomField>,
391
392    /// Rename a field in the generated Rust struct.
393    ///
394    /// Instead of the protobuf field name, use a different name for the generated field and its
395    /// accessors. Applies to normal fields as well as oneofs and oneof variants.
396    ///
397    /// # Example
398    /// ```no_run
399    /// # use micropb_gen::{Generator, Config};
400    /// # let mut gen = micropb_gen::Generator::new();
401    /// // `super` can't be a field identifier, so we need to rename it
402    /// gen.configure(".Message.super", Config::new().rename_field("super_"));
403    /// // The oneof field will be renamed to `oneof`, and the oneof type will be `Oneof`
404    /// gen.configure(".Message.my_oneof", Config::new().rename_field("oneof"));
405    /// ```
406    ///
407    /// # Note
408    /// This configuration is only applied to the path passed to
409    /// [`configure`](crate::Generator::configure). It is not propagated to "children" paths.
410    [no_inherit] rename_field: [deref] Option<String>,
411
412    /// Override the max size of the field on the wire.
413    ///
414    /// Instead of calculating the max size of the field, the generator will use this value instead
415    /// when determining the max size of the entire message. This is useful for fields with
416    /// "unbounded" size, such as `Vec` fields and recursive fields. Applies to normal fields,
417    /// oneof fields, and oneof variants.
418    encoded_max_size: Option<usize>,
419
420    /// Specify lifetime parameter of a message field.
421    ///
422    /// If message type `Inner` has fields with a lifetime, its message struct will be generated
423    /// with that lifetime parameter. However, if another message type `Outer` has `Inner` as its
424    /// field, then that field must specify `field_lifetime` so that the lifetime is included in
425    /// the field declaration.
426    ///
427    /// # Example
428    /// ```no_run
429    /// # use micropb_gen::{Generator, Config};
430    /// # let mut gen = micropb_gen::Generator::new();
431    /// // `Inner` now has a lifetime param
432    /// gen.configure(".Inner.field", Config::new().string_type("MyString<'a>"));
433    /// // Make sure inner is declared as `inner: Inner<'a>`
434    /// // Will also automatically add the lifetime param to declaration of `Outer`
435    /// gen.configure(".Outer.inner", Config::new().field_lifetime("'a"));
436    /// ```
437    field_lifetime: [deref] Option<String>,
438
439    /// Disable field accessors.
440    ///
441    /// Do not generate accessors for this field other than the getter method on optional fields,
442    /// which is required by the encoding logic. This won't reduce the compiled code size, but it
443    /// will significantly reduce the size of the output source file.
444    no_accessors: Option<bool>,
445
446    // Type configs
447
448    /// Override the integer size of Protobuf enums.
449    ///
450    /// Change the integer fields to be `i8`, `i16`, `i32`, or `i64`. If the integer type is
451    /// smaller than the value on the wire, the value will be truncated to fit.
452    enum_int_size: Option<IntSize>,
453
454    /// Use unsigned integer to represent enums.
455    ///
456    /// Enum will be `u32` instead of `i32`. Negative values on the wire will be truncated, so do
457    /// not use this if any of the enum variants are negative. Setting this option will reduce the
458    /// maximum encoded size of the enum, since signed integers always have a max size of 10 bytes.
459    enum_unsigned: Option<bool>,
460
461    /// Set attributes for generated types, such as messages and enums.
462    ///
463    /// The attribute string will be placed before type definitions. The string must be in the
464    /// syntax of 0 or more Rust attributes.
465    ///
466    /// # Example
467    /// ```no_run
468    /// # use micropb_gen::{Generator, Config};
469    /// # let mut gen = micropb_gen::Generator::new();
470    /// // Set 2 type attributes for Message
471    /// gen.configure(".Message", Config::new().type_attributes("#[derive(Eq)] #[MyDerive]"));
472    /// // Unset type attributes for Message
473    /// gen.configure(".Message", Config::new().type_attributes(""));
474    /// ```
475    ///
476    /// # Special cases
477    /// - If applied to an oneof field, the attributes are applied to the oneof enum type
478    /// definition inside the message.
479    /// - If applied to the `._has` suffix, the attributes are applied to the hazzer type
480    /// definition inside the message.
481    type_attributes: [deref] Option<String>,
482
483    /// Disable generating `Debug` trait derives for message types.
484    no_debug_impl: Option<bool>,
485
486    /// Disable generating `Default` trait impl for message types.
487    ///
488    /// This can cause compile errors if decoding logic is being generated, because decoding
489    /// repeated and `map` fields requires the elements to implement `Default`.
490    no_default_impl: Option<bool>,
491
492    /// Disable generating `PartialEq` trait impl for message types.
493    no_partial_eq_impl: Option<bool>,
494
495    /// Disable generating `Clone` trait derives for message types.
496    no_clone_impl: Option<bool>,
497
498    /// Add a custom handler on a message struct for handling unknown fields.
499    ///
500    /// When decoding a message, unknown fields are skipped by default. If a message has
501    /// `unknown_handler` configured to a type name, a field of that type named `_unknown` will be
502    /// added to the message struct. This field will handle decoding of all unknown fields and will
503    /// also be encoded, so the handler type must implement `FieldEncode` and `FieldDecode`,
504    /// like with [`custom_field`](Config::custom_field).
505    ///
506    /// # Note
507    /// This configuration is only applied to the path passed to
508    /// [`configure`](crate::Generator::configure). It is not propagated to "children" paths.
509    [no_inherit] unknown_handler: [deref] Option<String>,
510
511    // General configs
512
513    /// Skip generating a type or field
514    ///
515    /// If applied to message or enum, the whole type definition will be skipped. If applied to a
516    /// field, it won't be included in the message struct.
517    skip: Option<bool>,
518}
519
520impl Config {
521    /// Ensure proper handling of a recursive field by boxing it and hardcoding its max size to 0
522    ///
523    /// Combination of [`Self::boxed`] and [`Self::encoded_max_size`].
524    pub fn recursive_field(self) -> Self {
525        self.boxed(true).encoded_max_size(0)
526    }
527}
528
529struct Attributes(Vec<syn::Attribute>);
530
531impl syn::parse::Parse for Attributes {
532    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
533        Ok(Self(input.call(syn::Attribute::parse_outer)?))
534    }
535}
536
537pub(crate) fn parse_attributes(s: &str) -> syn::Result<Vec<syn::Attribute>> {
538    let attrs: Attributes = syn::parse_str(s)?;
539    Ok(attrs.0)
540}
541
542impl Config {
543    pub(crate) fn field_attr_parsed(&self) -> Result<Vec<syn::Attribute>, String> {
544        let s = self.field_attributes.as_deref().unwrap_or("");
545        parse_attributes(s).map_err(|e| {
546            format!("Failed to parse field_attributes \"{s}\" as Rust attributes: {e}")
547        })
548    }
549
550    pub(crate) fn type_attr_parsed(&self) -> Result<Vec<syn::Attribute>, String> {
551        let s = self.type_attributes.as_deref().unwrap_or("");
552        parse_attributes(s)
553            .map_err(|e| format!("Failed to parse type_attributes \"{s}\" as Rust attributes: {e}"))
554    }
555
556    pub(crate) fn rust_field_name(&self, name: &str) -> Result<(String, Ident), String> {
557        if let Some(s) = &self.rename_field {
558            // expect user-supplied names to not require sanitization
559            Ok((
560                s.to_owned(),
561                syn::parse_str(s).map_err(|e| {
562                    format!("Failed to parse rename_field \"{s}\" as identifier: {e}")
563                })?,
564            ))
565        } else {
566            Ok((name.to_owned(), sanitized_ident(name)))
567        }
568    }
569
570    pub(crate) fn string_type_parsed(&self, n: Option<u32>) -> Result<Option<syn::Type>, String> {
571        self.string_type
572            .as_ref()
573            .map(|t| {
574                check_missing_len(t, n, "max_bytes")?;
575                let typestr = substitute_param(t.into(), "$N", n);
576                syn::parse_str(&typestr).map_err(|e| {
577                    format!("Failed to parse string_type \"{typestr}\" as type path: {e}")
578                })
579            })
580            .transpose()
581    }
582
583    pub(crate) fn bytes_type_parsed(&self, n: Option<u32>) -> Result<Option<syn::Type>, String> {
584        self.bytes_type
585            .as_ref()
586            .map(|t| {
587                check_missing_len(t, n, "max_bytes")?;
588                let typestr = substitute_param(t.into(), "$N", n);
589                syn::parse_str(&typestr).map_err(|e| {
590                    format!("Failed to parse bytes_type \"{typestr}\" as type path: {e}")
591                })
592            })
593            .transpose()
594    }
595
596    pub(crate) fn vec_type_parsed(
597        &self,
598        t: TokenStream,
599        n: Option<u32>,
600    ) -> Result<Option<syn::Type>, String> {
601        self.vec_type
602            .as_ref()
603            .map(|typestr| {
604                let typestr = substitute_param(typestr.into(), "$T", Some(t));
605                let typestr = substitute_param(typestr, "$N", n);
606                check_missing_len(&typestr, n, "max_len")?;
607                syn::parse_str(&typestr).map_err(|e| {
608                    format!("Failed to parse vec_type \"{typestr}\" as type path: {e}")
609                })
610            })
611            .transpose()
612    }
613
614    pub(crate) fn map_type_parsed(
615        &self,
616        k: TokenStream,
617        v: TokenStream,
618        n: Option<u32>,
619    ) -> Result<Option<syn::Type>, String> {
620        self.map_type
621            .as_ref()
622            .map(|t| {
623                let typestr = substitute_param(t.into(), "$K", Some(k));
624                let typestr = substitute_param(typestr, "$V", Some(v));
625                let typestr = substitute_param(typestr, "$N", n);
626                check_missing_len(&typestr, n, "max_len")?;
627                syn::parse_str(&typestr).map_err(|e| {
628                    format!("Failed to parse map_type \"{typestr}\" as type path: {e}")
629                })
630            })
631            .transpose()
632    }
633
634    pub(crate) fn unknown_handler_parsed(&self) -> Result<Option<syn::Type>, String> {
635        self.unknown_handler
636            .as_ref()
637            .map(|t| {
638                syn::parse_str(t).map_err(|e| {
639                    format!("Failed to parse unknown_handler \"{t}\" as Rust type: {e}")
640                })
641            })
642            .transpose()
643    }
644
645    pub(crate) fn custom_field_parsed(
646        &self,
647    ) -> Result<Option<crate::generator::field::CustomField>, String> {
648        let res = match &self.custom_field {
649            Some(CustomField::Type(s)) => Some(crate::generator::field::CustomField::Type(
650                syn::parse_str(s).map_err(|e| {
651                    format!("Failed to parse custom field \"{s}\" as Rust type: {e}")
652                })?,
653            )),
654            Some(CustomField::Delegate(s)) => Some(crate::generator::field::CustomField::Delegate(
655                syn::parse_str(s).map_err(|e| {
656                    format!("Failed to parse custom delegate \"{s}\" as identifier: {e}")
657                })?,
658            )),
659            None => None,
660        };
661        Ok(res)
662    }
663
664    pub(crate) fn field_lifetime_parsed(&self) -> Result<Option<syn::Lifetime>, String> {
665        self.field_lifetime
666            .as_ref()
667            .map(|l| {
668                syn::parse_str(l)
669                    .map_err(|e| format!("Failed to parse \"{l}\" as Rust lifetime: {e}"))
670            })
671            .transpose()
672    }
673}
674
675fn substitute_param<'a>(
676    typestr: Cow<'a, str>,
677    pat: &str,
678    t: Option<impl ToString>,
679) -> Cow<'a, str> {
680    if let Some(t) = t {
681        if typestr.find(pat).is_some() {
682            let t = t.to_string();
683            return typestr.replace(pat, &t).into();
684        }
685    }
686    typestr
687}
688
689fn check_missing_len(typestr: &str, n: Option<u32>, len_param: &str) -> Result<(), String> {
690    if n.is_none() && typestr.contains("$N") {
691        Err(format!("Missing {len_param} for type path \"{typestr}\""))
692    } else {
693        Ok(())
694    }
695}
696
697#[cfg(test)]
698mod tests {
699    use quote::{format_ident, quote, ToTokens};
700
701    use super::*;
702
703    #[test]
704    fn merge() {
705        let mut mergee = Config::new()
706            .rename_field("rename")
707            .skip(true)
708            .vec_type("vec")
709            .string_type("str");
710        let merger = Config::new().skip(false).vec_type("array");
711        mergee.merge(&merger);
712
713        assert!(!mergee.skip.unwrap());
714        assert_eq!(mergee.vec_type.unwrap(), "array");
715        assert_eq!(mergee.string_type.unwrap(), "str");
716        // max_len was never set
717        assert!(mergee.max_len.is_none());
718        // rename_field gets overwritten unconditionally when merging
719        assert!(mergee.rename_field.is_none());
720    }
721
722    #[test]
723    fn parse() {
724        let mut config = Config::new()
725            .vec_type("heapless::Vec<$T, $N>")
726            .string_type("heapless::String<$N>")
727            .map_type("Map<$K, $V, $N>")
728            .bytes_type("Bytes")
729            .type_attributes("#[derive(Hash)]");
730
731        assert_eq!(
732            config
733                .vec_type_parsed(quote! {u8}, Some(5))
734                .unwrap()
735                .to_token_stream()
736                .to_string(),
737            quote! { heapless::Vec<u8, 5> }.to_string()
738        );
739        assert_eq!(
740            config
741                .string_type_parsed(Some(12))
742                .unwrap()
743                .to_token_stream()
744                .to_string(),
745            quote! { heapless::String<12> }.to_string()
746        );
747        assert_eq!(
748            config
749                .map_type_parsed(quote! {u32}, quote! {bool}, Some(14))
750                .unwrap()
751                .to_token_stream()
752                .to_string(),
753            quote! { Map<u32, bool, 14> }.to_string()
754        );
755        assert_eq!(
756            config
757                .bytes_type_parsed(None)
758                .unwrap()
759                .to_token_stream()
760                .to_string(),
761            "Bytes"
762        );
763
764        let attrs = config.type_attr_parsed().unwrap();
765        assert_eq!(
766            quote! { #(#attrs)* }.to_string(),
767            quote! { #[derive(Hash)] }.to_string()
768        );
769
770        let attrs = config.field_attr_parsed().unwrap();
771        assert_eq!(quote! { #(#attrs)* }.to_string(), "");
772        config.field_attributes = Some("#[default] #[delete]".to_owned());
773        let attrs = config.field_attr_parsed().unwrap();
774        assert_eq!(
775            quote! { #(#attrs)* }.to_string(),
776            quote! { #[default] #[delete] }.to_string()
777        );
778
779        assert_eq!(
780            config.rust_field_name("name").unwrap(),
781            ("name".to_owned(), format_ident!("r#name"))
782        );
783        config.rename_field = Some("rename".to_string());
784        assert_eq!(
785            config.rust_field_name("name").unwrap(),
786            ("rename".to_owned(), format_ident!("rename"))
787        );
788
789        config.custom_field = Some(CustomField::Type("Vec<u16, 4>".to_owned()));
790        let crate::generator::field::CustomField::Type(typ) =
791            config.custom_field_parsed().unwrap().unwrap()
792        else {
793            unreachable!()
794        };
795        assert_eq!(
796            typ.to_token_stream().to_string(),
797            quote! { Vec<u16, 4> }.to_string()
798        );
799
800        config.custom_field = Some(CustomField::Delegate("name".to_owned()));
801        let crate::generator::field::CustomField::Delegate(del) =
802            config.custom_field_parsed().unwrap().unwrap()
803        else {
804            unreachable!()
805        };
806        assert_eq!(del, format_ident!("name"));
807    }
808}