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}