musli_macros/internals/
attr.rs

1use std::collections::HashMap;
2use std::mem;
3
4use proc_macro2::Span;
5use syn::meta::ParseNestedMeta;
6use syn::parse::Parse;
7use syn::spanned::Spanned;
8use syn::Token;
9
10use crate::expander::NameMethod;
11use crate::expander::UnsizedMethod;
12use crate::internals::name::NameAll;
13use crate::internals::ATTR;
14use crate::internals::{Ctxt, Mode};
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
17pub(crate) enum ModeKind {
18    Binary,
19    Text,
20    Custom(Box<str>),
21}
22
23impl ModeKind {
24    pub(crate) fn default_name_all(&self) -> Option<NameAll> {
25        match self {
26            ModeKind::Binary => Some(NameAll::Index),
27            ModeKind::Text => Some(NameAll::Name),
28            ModeKind::Custom(_) => None,
29        }
30    }
31}
32
33#[derive(Debug, Clone)]
34pub(crate) struct ModeIdent {
35    pub(crate) ident: syn::Ident,
36    pub(crate) kind: ModeKind,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub(crate) enum Only {
41    Encode,
42    Decode,
43}
44
45#[derive(Default)]
46struct OneOf<T> {
47    encode: T,
48    decode: T,
49    any: T,
50}
51
52#[derive(Clone, Copy)]
53pub(crate) enum EnumTagging<'a> {
54    /// Use the default tagging method, as provided by the encoder-specific
55    /// method.
56    Default,
57    /// Only the tag is encoded.
58    Empty,
59    /// The type is internally tagged by the field given by the expression.
60    Internal { tag: &'a syn::Expr },
61    /// An enumerator is adjacently tagged.
62    Adjacent {
63        tag: &'a syn::Expr,
64        content: &'a syn::Expr,
65    },
66}
67
68/// If the type is tagged or not.
69#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
70pub enum Packing {
71    #[default]
72    Tagged,
73    Packed,
74    Transparent,
75}
76
77macro_rules! merge {
78    ($self:expr, $cx:expr, $new:expr, $field:ident, $only:expr) => {{
79        for $field in $new.$field {
80            let out = match $only {
81                None => &mut $self.$field.any,
82                Some(Only::Decode) => &mut $self.$field.decode,
83                Some(Only::Encode) => &mut $self.$field.encode,
84            };
85
86            if out.is_some() {
87                $cx.error_span(
88                    $field.0,
89                    format_args!(
90                        "#[{}] multiple {} attributes specified",
91                        ATTR,
92                        stringify!($field)
93                    ),
94                );
95            } else {
96                *out = Some($field);
97            }
98        }
99    }};
100}
101
102macro_rules! layer {
103    ($attr:ident, $new:ident, $layer:ident {
104        $($(#[$($single_meta:meta)*])* $single:ident: $single_ty:ty,)* $(,)?
105        @multiple
106        $($(#[$($multiple_meta:meta)*])* $multiple:ident: $multiple_ty:ty,)* $(,)?
107    }) => {
108        #[derive(Default)]
109        pub(crate) struct $attr {
110            root: $layer,
111            modes: HashMap<ModeKind, $layer>,
112        }
113
114        impl $attr {
115            fn by_mode<A, O>(&self, mode: Mode<'_>, access: A) -> Option<&O>
116            where
117                A: Copy + Fn(&$layer) -> Option<&O>,
118                O: ?Sized,
119            {
120                if let Some(value) = mode.kind.and_then(|m| self.modes.get(m).and_then(access)) {
121                    Some(value)
122                } else {
123                    access(&self.root)
124                }
125            }
126
127            $(
128                #[allow(unused)]
129                pub(crate) fn $single(&self, mode: Mode<'_>) -> Option<&(Span, $single_ty)> {
130                    self.by_mode(mode, |m| {
131                        match mode.only {
132                            Only::Encode if m.$single.encode.is_some() => m.$single.encode.as_ref(),
133                            Only::Decode if m.$single.decode.is_some() => m.$single.decode.as_ref(),
134                            _ => m.$single.any.as_ref(),
135                        }
136                    })
137                }
138            )*
139
140            $(
141                #[allow(unused)]
142                pub(crate) fn $multiple(&self, mode: Mode<'_>) -> &[(Span, $multiple_ty)] {
143                    self.by_mode(mode, |m| {
144                        match mode.only {
145                            Only::Encode if !m.$multiple.encode.is_empty() => Some(&m.$multiple.encode[..]),
146                            Only::Decode if !m.$multiple.decode.is_empty() => Some(&m.$multiple.decode[..]),
147                            _ if !m.$multiple.any.is_empty() => Some(&m.$multiple.any[..]),
148                            _ => None,
149                        }
150                    }).unwrap_or_default()
151                }
152            )*
153        }
154
155        #[derive(Default)]
156        struct $new {
157            $($(#[$($single_meta)*])* $single: Vec<(Span, $single_ty)>,)*
158            $($(#[$($multiple_meta)*])* $multiple: Vec<(Span, $multiple_ty)>,)*
159        }
160
161        #[derive(Default)]
162        struct $layer {
163            $($(#[$($single_meta)*])* $single: OneOf<Option<(Span, $single_ty)>>,)*
164            $($(#[$($multiple_meta)*])* $multiple: OneOf<Vec<(Span, $multiple_ty)>>,)*
165        }
166
167        impl $layer {
168            /// Merge attributes.
169            fn merge_with(&mut self, cx: &Ctxt, new: $new, only: Option<Only>) {
170                $(
171                    merge!(self, cx, new, $single, only);
172                )*
173
174                $(
175                    let list = match only {
176                        None => {
177                            &mut self.$multiple.any
178                        }
179                        Some(Only::Encode) => {
180                            &mut self.$multiple.encode
181                        }
182                        Some(Only::Decode) => {
183                            &mut self.$multiple.decode
184                        }
185                    };
186
187                    list.extend(new.$multiple);
188                )*
189            }
190        }
191    }
192}
193
194layer! {
195    TypeAttr, TypeLayerNew, TypeLayer {
196        /// `#[musli(crate = <path>)]`.
197        krate: syn::Path,
198        /// `#[musli(name_type)]`.
199        name_type: syn::Type,
200        /// `#[musli(name_all = "..")]`.
201        name_all: NameAll,
202        /// `#[musli(name_method = "..")]`.
203        name_method: NameMethod,
204        /// `#[musli(name_format_with)]`.
205        name_format_with: syn::Path,
206        /// If `#[musli(tag = <expr>)]` is specified.
207        tag: syn::Expr,
208        /// If `#[musli(content = <expr>)]` is specified.
209        content: syn::Expr,
210        /// `#[musli(packed)]` or `#[musli(transparent)]`.
211        packing: Packing,
212        @multiple
213        /// Bounds in a where predicate.
214        bounds: syn::WherePredicate,
215        /// Bounds to require for a `Decode` implementation.
216        decode_bounds: syn::WherePredicate,
217    }
218}
219
220impl TypeAttr {
221    pub(crate) fn is_name_type_ambiguous(&self, mode: Mode<'_>) -> bool {
222        self.name_type(mode).is_none()
223            && self.name_all(mode).is_none()
224            && self.name_method(mode).is_none()
225    }
226
227    pub(crate) fn enum_tagging_span(&self, mode: Mode<'_>) -> Option<Span> {
228        let tag = self.tag(mode);
229        let content = self.content(mode);
230        Some(tag.or(content)?.0)
231    }
232
233    /// Indicates the state of enum tagging.
234    pub(crate) fn enum_tagging(&self, mode: Mode<'_>) -> Option<EnumTagging<'_>> {
235        let (_, tag) = self.tag(mode)?;
236
237        Some(match self.content(mode) {
238            Some((_, content)) => EnumTagging::Adjacent { tag, content },
239            _ => EnumTagging::Internal { tag },
240        })
241    }
242
243    /// Get the configured crate, or fallback to default.
244    pub(crate) fn crate_or_default(&self, default: &str) -> syn::Path {
245        if let Some((_, krate)) = self.root.krate.any.as_ref() {
246            return krate.clone();
247        }
248
249        let mut path = syn::Path::from(syn::Ident::new(default, Span::call_site()));
250        path.leading_colon = Some(<Token![::]>::default());
251        path
252    }
253}
254
255pub(crate) fn type_attrs(cx: &Ctxt, attrs: &[syn::Attribute]) -> TypeAttr {
256    let mut attr = TypeAttr::default();
257
258    for a in attrs {
259        if !a.path().is_ident(ATTR) {
260            continue;
261        }
262
263        let mut new = TypeLayerNew::default();
264        let mut mode = None;
265        let mut only = None;
266
267        let result = a.parse_nested_meta(|meta| {
268            // #[musli(mode = <path>)]
269            if meta.path.is_ident("mode") {
270                meta.input.parse::<Token![=]>()?;
271                mode = Some(parse_mode(&meta)?);
272                return Ok(());
273            }
274
275            if meta.path.is_ident("encode_only") {
276                only = Some(Only::Encode);
277                return Ok(());
278            }
279
280            if meta.path.is_ident("decode_only") {
281                only = Some(Only::Decode);
282                return Ok(());
283            }
284
285            // #[musli(tag = <expr>)]
286            if meta.path.is_ident("tag") {
287                meta.input.parse::<Token![=]>()?;
288                new.tag.push((meta.path.span(), meta.input.parse()?));
289                return Ok(());
290            }
291
292            // #[musli(content = <expr>)]
293            if meta.path.is_ident("content") {
294                meta.input.parse::<Token![=]>()?;
295                new.content.push((meta.path.span(), meta.input.parse()?));
296                return Ok(());
297            }
298
299            // #[musli(crate = <path>)]
300            if meta.path.is_ident("crate") {
301                let path = if meta.input.parse::<Option<Token![=]>>()?.is_some() {
302                    meta.input.parse()?
303                } else {
304                    syn::parse_quote!(crate)
305                };
306
307                new.krate.push((meta.path.span(), path));
308                return Ok(());
309            }
310
311            // #[musli(name_type = <type>)]
312            if meta.path.is_ident("name_type") {
313                meta.input.parse::<Token![=]>()?;
314                new.name_type.push((meta.path.span(), meta.input.parse()?));
315                return Ok(());
316            }
317
318            // #[musli(name_format_with = <path>)]
319            if meta.path.is_ident("name_format_with") {
320                meta.input.parse::<Token![=]>()?;
321                new.name_format_with
322                    .push((meta.path.span(), meta.input.parse()?));
323                return Ok(());
324            }
325
326            // #[musli(bound = {..})]
327            if meta.path.is_ident("bound") {
328                meta.input.parse::<Token![=]>()?;
329                parse_bounds(&meta, &mut new.bounds)?;
330                return Ok(());
331            }
332
333            // #[musli(decode_bound = {..})]
334            if meta.path.is_ident("decode_bound") {
335                meta.input.parse::<Token![=]>()?;
336                parse_bounds(&meta, &mut new.decode_bounds)?;
337                return Ok(());
338            }
339
340            // #[musli(packed)]
341            if meta.path.is_ident("packed") {
342                new.packing.push((meta.path.span(), Packing::Packed));
343                return Ok(());
344            }
345
346            // #[musli(transparent)]
347            if meta.path.is_ident("transparent") {
348                new.packing.push((meta.path.span(), Packing::Transparent));
349                return Ok(());
350            }
351
352            // #[musli(name_all = "..")]
353            if meta.path.is_ident("name_all") {
354                new.name_all
355                    .push((meta.path.span(), parse_name_all(&meta)?));
356                return Ok(());
357            }
358
359            // #[musli(name_method = "..")]
360            if meta.path.is_ident("name_method") {
361                new.name_method
362                    .push((meta.path.span(), parse_name_method(&meta)?));
363                return Ok(());
364            }
365
366            Err(syn::Error::new_spanned(
367                meta.path,
368                format_args!("#[{ATTR}] Unsupported type attribute"),
369            ))
370        });
371
372        if let Err(error) = result {
373            cx.syn_error(error);
374        }
375
376        let attr = match mode {
377            Some(mode) => {
378                let modes = attr.modes.entry(mode.kind.clone()).or_default();
379                cx.register_mode(mode);
380                modes
381            }
382            None => &mut attr.root,
383        };
384
385        attr.merge_with(cx, new, only);
386    }
387
388    attr
389}
390
391fn parse_name_method(meta: &syn::meta::ParseNestedMeta<'_>) -> Result<NameMethod, syn::Error> {
392    meta.input.parse::<Token![=]>()?;
393
394    let string: syn::LitStr = meta.input.parse()?;
395    let s = string.value();
396
397    match s.as_str() {
398        "value" => Ok(NameMethod::Value),
399        "unsized" => Ok(NameMethod::Unsized(UnsizedMethod::Default)),
400        "unsized_bytes" => Ok(NameMethod::Unsized(UnsizedMethod::Bytes)),
401        _ => Err(syn::Error::new_spanned(
402            string,
403            "#[musli(name_method = ..)]: Bad value, expected one of \"value\", \"unsized\", \"unsized_bytes\"",
404        )),
405    }
406}
407
408fn parse_name_all(meta: &syn::meta::ParseNestedMeta<'_>) -> Result<NameAll, syn::Error> {
409    meta.input.parse::<Token![=]>()?;
410
411    let string: syn::LitStr = meta.input.parse()?;
412    let s = string.value();
413
414    let Some(name_all) = NameAll::parse(s.as_str()) else {
415        let mut options = Vec::new();
416
417        for option in NameAll::ALL {
418            options.push(format!(r#""{option}""#));
419        }
420
421        let options = options.join(", ");
422
423        return Err(syn::Error::new_spanned(
424            string,
425            format_args!("#[{ATTR}(name_all = {s:?})]: Bad value, expected one of {options}"),
426        ));
427    };
428
429    Ok(name_all)
430}
431
432fn parse_bounds(
433    meta: &syn::meta::ParseNestedMeta,
434    out: &mut Vec<(Span, syn::WherePredicate)>,
435) -> syn::Result<()> {
436    let content;
437    syn::braced!(content in meta.input);
438    let where_clauses = content.parse_terminated(syn::WherePredicate::parse, Token![,])?;
439
440    for where_clause in where_clauses {
441        out.push((meta.path.span(), where_clause));
442    }
443
444    Ok(())
445}
446
447layer! {
448    VariantAttr, VariantLayerNew, VariantLayer {
449        /// `#[musli(name_type)]`.
450        name_type: syn::Type,
451        /// `#[musli(name_format_with)]`.
452        name_format_with: syn::Path,
453        /// Name a variant with the given expression.
454        name: syn::Expr,
455        /// Pattern used to match the given field when decoding.
456        pattern: syn::Pat,
457        /// `#[musli(name_all = "..")]`.
458        name_all: NameAll,
459        /// `#[musli(name_method = "..")]`.
460        name_method: NameMethod,
461        /// `#[musli(packed)]` or `#[musli(transparent)]`.
462        packing: Packing,
463        /// `#[musli(default)]`.
464        default_variant: (),
465        @multiple
466    }
467}
468
469impl VariantAttr {
470    pub(crate) fn is_name_type_ambiguous(&self, mode: Mode<'_>) -> bool {
471        self.name_type(mode).is_none()
472            && self.name_all(mode).is_none()
473            && self.name_method(mode).is_none()
474    }
475}
476
477/// Parse variant attributes.
478pub(crate) fn variant_attrs(cx: &Ctxt, attrs: &[syn::Attribute]) -> VariantAttr {
479    let mut attr = VariantAttr::default();
480
481    for a in attrs {
482        if !a.path().is_ident(ATTR) {
483            continue;
484        }
485
486        let mut new = VariantLayerNew::default();
487        let mut mode = None;
488        let mut only = None;
489
490        let result = a.parse_nested_meta(|meta| {
491            // #[musli(mode = <path>)]
492            if meta.path.is_ident("mode") {
493                meta.input.parse::<Token![=]>()?;
494                mode = Some(parse_mode(&meta)?);
495                return Ok(());
496            }
497
498            if meta.path.is_ident("encode_only") {
499                only = Some(Only::Encode);
500                return Ok(());
501            }
502
503            if meta.path.is_ident("decode_only") {
504                only = Some(Only::Decode);
505                return Ok(());
506            }
507
508            // #[musli(name_type = <type>)]
509            if meta.path.is_ident("name_type") {
510                meta.input.parse::<Token![=]>()?;
511                new.name_type.push((meta.path.span(), meta.input.parse()?));
512                return Ok(());
513            }
514
515            // #[musli(name_format_with = <path>)]
516            if meta.path.is_ident("name_format_with") {
517                meta.input.parse::<Token![=]>()?;
518                new.name_format_with
519                    .push((meta.path.span(), meta.input.parse()?));
520                return Ok(());
521            }
522
523            if meta.path.is_ident("rename") {
524                return Err(syn::Error::new_spanned(
525                    meta.path,
526                    "#[musli(rename = ..)] has been changed to #[musli(name = ..)]",
527                ));
528            }
529
530            // #[musli(name = <expr>)]
531            if meta.path.is_ident("name") {
532                meta.input.parse::<Token![=]>()?;
533                new.name.push((meta.path.span(), meta.input.parse()?));
534                return Ok(());
535            }
536
537            // #[musli(pattern = <expr>)]
538            if meta.path.is_ident("pattern") {
539                meta.input.parse::<Token![=]>()?;
540                new.pattern
541                    .push((meta.path.span(), meta.input.call(syn::Pat::parse_single)?));
542                return Ok(());
543            }
544
545            // #[musli(default)]
546            if meta.path.is_ident("default") {
547                new.default_variant.push((meta.path.span(), ()));
548                return Ok(());
549            }
550
551            // #[musli(packed)]
552            if meta.path.is_ident("packed") {
553                new.packing.push((meta.path.span(), Packing::Packed));
554                return Ok(());
555            }
556
557            // #[musli(transparent)]
558            if meta.path.is_ident("transparent") {
559                new.packing.push((meta.path.span(), Packing::Transparent));
560                return Ok(());
561            }
562
563            // #[musli(name_all = "..")]
564            if meta.path.is_ident("name_all") {
565                new.name_all
566                    .push((meta.path.span(), parse_name_all(&meta)?));
567                return Ok(());
568            }
569
570            // #[musli(name_method = "..")]
571            if meta.path.is_ident("name_method") {
572                new.name_method
573                    .push((meta.path.span(), parse_name_method(&meta)?));
574                return Ok(());
575            }
576
577            Err(syn::Error::new_spanned(
578                meta.path,
579                format_args!("#[{ATTR}] Unsupported type attribute"),
580            ))
581        });
582
583        if let Err(error) = result {
584            cx.syn_error(error);
585        }
586
587        let attr = match mode {
588            Some(mode) => {
589                let out = attr.modes.entry(mode.kind.clone()).or_default();
590                cx.register_mode(mode);
591                out
592            }
593            None => &mut attr.root,
594        };
595
596        attr.merge_with(cx, new, only);
597    }
598
599    attr
600}
601
602#[derive(Default, Clone, Copy)]
603pub(crate) enum FieldEncoding {
604    Packed,
605    Bytes,
606    Trace,
607    #[default]
608    Default,
609}
610
611layer! {
612    Field, FieldNew, FieldLayer {
613        /// Module to use when decoding.
614        encode_path: syn::Path,
615        /// Path to use when decoding.
616        decode_path: syn::Path,
617        /// Method to check if we want to skip encoding.
618        skip_encoding_if: syn::Path,
619        /// Rename a field to the given literal.
620        name: syn::Expr,
621        /// Pattern used to match the given field when decoding.
622        pattern: syn::Pat,
623        /// Use a default value for the field if it's not available.
624        is_default: Option<syn::Path>,
625        /// Use a default value for the field if it's not available.
626        skip: (),
627        /// Field encoding to use.
628        encoding: FieldEncoding,
629        @multiple
630    }
631}
632
633impl Field {
634    /// Expand encode of the given field.
635    pub(crate) fn encode_path_expanded(&self, mode: Mode<'_>, span: Span) -> (Span, syn::Path) {
636        let encode_path = self.encode_path(mode);
637
638        if let Some((span, encode_path)) = encode_path {
639            (*span, encode_path.clone())
640        } else {
641            let field_encoding = self.encoding(mode).map(|&(_, e)| e).unwrap_or_default();
642            let encode_path = mode.encode_t_encode(field_encoding);
643            (span, encode_path)
644        }
645    }
646
647    /// Expand decode of the given field.
648    pub(crate) fn decode_path_expanded(&self, mode: Mode<'_>, span: Span) -> (Span, syn::Path) {
649        let decode_path = self.decode_path(mode);
650
651        if let Some((span, decode_path)) = decode_path {
652            (*span, decode_path.clone())
653        } else {
654            let field_encoding = self.encoding(mode).map(|&(_, e)| e).unwrap_or_default();
655            let decode_path = mode.decode_t_decode(field_encoding);
656            (span, decode_path)
657        }
658    }
659}
660
661/// Parse field attributes.
662pub(crate) fn field_attrs(cx: &Ctxt, attrs: &[syn::Attribute]) -> Field {
663    let mut attr = Field::default();
664
665    for a in attrs {
666        if !a.path().is_ident(ATTR) {
667            continue;
668        }
669
670        let mut new = FieldNew::default();
671        let mut mode = None;
672        let mut only = None;
673
674        let result = a.parse_nested_meta(|meta| {
675            // #[musli(mode = <path>)]
676            if meta.path.is_ident("mode") {
677                meta.input.parse::<Token![=]>()?;
678                mode = Some(parse_mode(&meta)?);
679                return Ok(());
680            }
681
682            if meta.path.is_ident("encode_only") {
683                only = Some(Only::Encode);
684                return Ok(());
685            }
686
687            if meta.path.is_ident("decode_only") {
688                only = Some(Only::Decode);
689                return Ok(());
690            }
691
692            // parse #[musli(with = <path>)]
693            if meta.path.is_ident("with") {
694                meta.input.parse::<Token![=]>()?;
695                let mut path = meta.input.parse::<syn::Path>()?;
696
697                let (span, arguments) = match path.segments.last_mut() {
698                    Some(s) => (
699                        s.span(),
700                        mem::replace(&mut s.arguments, syn::PathArguments::None),
701                    ),
702                    None => (path.span(), syn::PathArguments::None),
703                };
704
705                let mut encode_path = path.clone();
706
707                encode_path.segments.push({
708                    let mut segment = syn::PathSegment::from(syn::Ident::new("encode", span));
709                    segment.arguments = arguments.clone();
710                    segment
711                });
712
713                let mut decode_path = path.clone();
714
715                decode_path.segments.push({
716                    let mut segment = syn::PathSegment::from(syn::Ident::new("decode", span));
717                    segment.arguments = arguments;
718                    segment
719                });
720
721                new.encode_path.push((path.span(), encode_path));
722                new.decode_path.push((path.span(), decode_path));
723                return Ok(());
724            }
725
726            // #[musli(skip_encoding_if = <path>)]
727            if meta.path.is_ident("skip_encoding_if") {
728                meta.input.parse::<Token![=]>()?;
729                new.skip_encoding_if
730                    .push((meta.path.span(), meta.input.parse()?));
731                return Ok(());
732            }
733
734            if meta.path.is_ident("rename") {
735                return Err(syn::Error::new_spanned(
736                    meta.path,
737                    "#[musli(rename = ..)] has been changed to #[musli(name = ..)]",
738                ));
739            }
740
741            // #[musli(name = <expr>)]
742            if meta.path.is_ident("name") {
743                meta.input.parse::<Token![=]>()?;
744                new.name.push((meta.path.span(), meta.input.parse()?));
745                return Ok(());
746            }
747
748            // #[musli(pattern = <expr>)]
749            if meta.path.is_ident("pattern") {
750                meta.input.parse::<Token![=]>()?;
751                new.pattern
752                    .push((meta.path.span(), meta.input.call(syn::Pat::parse_single)?));
753                return Ok(());
754            }
755
756            // #[musli(default)]
757            if meta.path.is_ident("default") {
758                if meta.input.parse::<Option<Token![=]>>()?.is_some() {
759                    new.is_default
760                        .push((meta.path.span(), Some(meta.input.parse()?)));
761                } else {
762                    new.is_default.push((meta.path.span(), None));
763                }
764
765                return Ok(());
766            }
767
768            // #[musli(skip)]
769            if meta.path.is_ident("skip") {
770                new.skip.push((meta.path.span(), ()));
771                return Ok(());
772            }
773
774            // #[musli(trace)]
775            if meta.path.is_ident("trace") {
776                new.encoding.push((meta.path.span(), FieldEncoding::Trace));
777                return Ok(());
778            }
779
780            // #[musli(bytes)]
781            if meta.path.is_ident("bytes") {
782                new.encoding.push((meta.path.span(), FieldEncoding::Bytes));
783                return Ok(());
784            }
785
786            // #[musli(packed)]
787            if meta.path.is_ident("packed") {
788                new.encoding.push((meta.path.span(), FieldEncoding::Packed));
789                return Ok(());
790            }
791
792            Err(syn::Error::new_spanned(
793                meta.path,
794                format_args!("#[{ATTR}] Unsupported field attribute"),
795            ))
796        });
797
798        if let Err(error) = result {
799            cx.syn_error(error);
800        }
801
802        let attr = match mode {
803            Some(mode) => {
804                let out = attr.modes.entry(mode.kind.clone()).or_default();
805                cx.register_mode(mode);
806                out
807            }
808            None => &mut attr.root,
809        };
810
811        attr.merge_with(cx, new, only);
812    }
813
814    attr
815}
816
817fn parse_mode(meta: &ParseNestedMeta<'_>) -> syn::Result<ModeIdent> {
818    let ident: syn::Ident = meta.input.parse()?;
819    let s = ident.to_string();
820
821    let kind = match s.as_str() {
822        "Binary" => ModeKind::Binary,
823        "Text" => ModeKind::Text,
824        other => ModeKind::Custom(other.into()),
825    };
826
827    Ok(ModeIdent { ident, kind })
828}