Skip to main content

xdr_repr/
ty.rs

1//! Traits for describing XDR type representations.
2//!
3//! This module provides traits that allow Rust types to describe their XDR
4//! representation, enabling automatic generation of XDR specifications.
5
6#![allow(dead_code)]
7
8use crate::ident;
9use crate::xdr::*;
10use alloc::string::*;
11
12/// Types that can produce a complete XDR specification.
13pub trait HasSpecification {
14    /// Returns the complete XDR specification for this type.
15    fn spec() -> Specification;
16}
17
18/// Types that can produce an XDR definition.
19///
20/// This trait is automatically implemented for types that implement [`HasTypeDef`].
21pub trait XDR {
22    /// Returns the XDR definition for this type.
23    fn def() -> Definition;
24}
25
26impl<T: HasTypeDef> XDR for T {
27    fn def() -> Definition {
28        Definition::Type(T::type_def())
29    }
30}
31
32/// Types that can describe their XDR type definition.
33///
34/// This trait allows types to specify their XDR type definition, including
35/// structs, enums, unions, and typedefs.
36pub trait HasTypeDef {
37    /// Returns the XDR type definition for this type.
38    fn type_def() -> TypeDef;
39
40    /// Returns the XDR identifier for this named type.
41    fn type_ident() -> Ident {
42        type_def_ident(Self::type_def())
43    }
44
45    /// Creates a declaration that references this named type.
46    fn named_declaration(name: Ident) -> Declaration {
47        named_declaration_from_type_def(name, Self::type_def())
48    }
49}
50
51/// Types that can describe their XDR type specification.
52///
53/// This trait allows types to specify their XDR type specification, which is
54/// used in declarations and type references.
55pub trait HasTypeSpec {
56    /// Returns the XDR type specification for this type.
57    fn type_spec() -> TypeSpec;
58}
59
60impl<T: HasTypeDef> HasTypeSpec for T {
61    fn type_spec() -> TypeSpec {
62        TypeSpec::Ident(T::type_ident())
63    }
64}
65
66pub(crate) fn type_def_ident(type_def: TypeDef) -> Ident {
67    match type_def {
68        TypeDef::Def(decl) => decl_ident(decl),
69        TypeDef::Enum(ident, _) => ident,
70        TypeDef::Struct(ident, _) => ident,
71        TypeDef::Union(ident, _) => ident,
72    }
73}
74
75fn decl_ident(decl: Declaration) -> Ident {
76    match decl {
77        Declaration::Named { name: ident, .. } => ident,
78        Declaration::FixedOpaque { name: ident, .. } => ident,
79        Declaration::VariableOpaque { name: ident, .. } => ident,
80        Declaration::String { name: ident, .. } => ident,
81        Declaration::FixedArray { name: ident, .. } => ident,
82        Declaration::VariableArray { name: ident, .. } => ident,
83        Declaration::Optional { name: ident, .. } => ident,
84        Declaration::Void => Ident("void".to_string()),
85    }
86}
87
88fn named_declaration_from_type_def(name: Ident, type_def: TypeDef) -> Declaration {
89    let declare_named = |ty_ident| Declaration::Named {
90        name,
91        ty: TypeSpec::Ident(ty_ident),
92    };
93
94    match type_def {
95        TypeDef::Def(decl) => match decl.name() {
96            Some(ty_ident) => declare_named(ty_ident.clone()),
97            None => decl,
98        },
99        TypeDef::Struct(ty_ident, _) => declare_named(ty_ident),
100        TypeDef::Enum(ty_ident, _) => declare_named(ty_ident),
101        TypeDef::Union(ty_ident, _) => declare_named(ty_ident),
102    }
103}
104
105macro_rules! impl_has_type_spec {
106    { $( $ty:ty => $spec:expr ),* $(,)? } =>
107    {$(
108        impl HasTypeSpec for $ty {
109            fn type_spec() -> TypeSpec {
110                $spec
111            }
112        }
113    )*};
114}
115
116impl_has_type_spec! {
117    i32 => TypeSpec::Int,
118    u32 => TypeSpec::Unsigned,
119    i64 => TypeSpec::Hyper,
120    u64 => TypeSpec::UnsignedHyper,
121    bool => TypeSpec::Bool,
122    f32 => TypeSpec::Float,
123    f64 => TypeSpec::Double,
124}
125
126// 128-bit integer types using the alternate XDR encoding.
127// When the `int128` feature is enabled, `i128` and `u128` are represented
128// as named types referring to double-u64 structures defined outside this package.
129#[cfg(feature = "int128")]
130#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
131// These refer to double-u64 structures defined outside this package
132impl_has_type_spec! {
133    i128 => <(i64, u64)>::type_spec(),
134    u128 => <(u64, u64)>::type_spec()
135}
136
137/// Types that can create an XDR declaration with a given name.
138///
139/// This trait allows types to create a declaration that can be used
140/// in struct fields, procedure parameters, etc.
141pub trait Declare {
142    /// Creates an XDR declaration with the given name.
143    fn declare(name: Ident) -> Declaration;
144}
145
146macro_rules! impl_declare_named {
147    { $( $ty:ty => $spec:expr ),* $(,)? } =>
148    {$(
149        impl Declare for $ty {
150            fn declare(name: Ident) -> Declaration {
151                Declaration::Named {
152                    name,
153                    ty: $spec
154                }
155            }
156        }
157    )*};
158}
159
160impl_declare_named! {
161    i32 => TypeSpec::Int,
162    u32 => TypeSpec::Unsigned,
163    i64 => TypeSpec::Hyper,
164    u64 => TypeSpec::UnsignedHyper,
165    bool => TypeSpec::Bool,
166    f32 => TypeSpec::Float,
167    f64 => TypeSpec::Double,
168}
169
170#[cfg(feature = "int128")]
171#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
172// These refer to double-u64 structures defined outside this package
173impl_declare_named! {
174    i128 => <(i64, u64)>::type_spec(),
175    u128 => <(u64, u64)>::type_spec(),
176}
177
178macro_rules! impl_refs {
179    ($($ty:ty),* $(,)?) => {$(
180        impl HasTypeSpec for &$ty {
181            fn type_spec() -> TypeSpec {
182                <$ty>::type_spec()
183            }
184        }
185        impl HasTypeSpec for &mut $ty {
186            fn type_spec() -> TypeSpec {
187                <$ty>::type_spec()
188            }
189        }
190        impl Declare for &$ty {
191            fn declare(name: Ident) -> Declaration {
192                <$ty>::declare(name)
193            }
194        }
195        impl Declare for &mut $ty {
196            fn declare(name: Ident) -> Declaration {
197                <$ty>::declare(name)
198            }
199        }
200    )*};
201}
202
203impl_refs! {
204    i32, u32, i64, u64, bool, f32, f64,
205}
206
207#[cfg(feature = "int128")]
208#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
209impl_refs! {
210    i128, u128,
211}
212
213impl<T: HasTypeSpec> HasTypeSpec for Option<T> {
214    fn type_spec() -> TypeSpec {
215        // Options aren't types in XDR, so we represent them as embedded structs
216        // with a single optional field named "unnamed_0"
217        TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::Optional {
218            name: ident("unnamed_0"),
219            opt_ty: T::type_spec(),
220        }]))
221    }
222}
223
224impl<T: HasTypeSpec> Declare for Option<T> {
225    fn declare(name: Ident) -> Declaration {
226        Declaration::Optional {
227            name,
228            opt_ty: T::type_spec(),
229        }
230    }
231}
232
233const _: () = {
234    use alloc::boxed::Box;
235
236    impl<T: HasTypeDef> HasTypeDef for Box<T> {
237        fn type_def() -> TypeDef {
238            T::type_def()
239        }
240
241        fn type_ident() -> Ident {
242            T::type_ident()
243        }
244
245        fn named_declaration(name: Ident) -> Declaration {
246            T::named_declaration(name)
247        }
248    }
249};
250
251impl<const N: usize> Declare for [u8; N] {
252    fn declare(name: Ident) -> Declaration {
253        const { assert!(N <= u32::MAX as usize, "N exceeds u32::MAX") }
254        Declaration::FixedOpaque {
255            name,
256            size: Value::Const((N as u32).into()),
257        }
258    }
259}
260
261// HasTypeSpec for [u8; N] - returns embedded struct with fixed opaque field
262// This allows nested arrays like [[u8; 3]; 5] to work
263impl<const N: usize> HasTypeSpec for [u8; N] {
264    fn type_spec() -> TypeSpec {
265        const { assert!(N <= u32::MAX as usize, "N exceeds u32::MAX") }
266        // Arrays aren't types in XDR, so we represent them as embedded structs
267        // with a single field named "unnamed_0"
268        TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::FixedOpaque {
269            name: ident("unnamed_0"),
270            size: Value::Const((N as u32).into()),
271        }]))
272    }
273}
274
275impl<const N: usize> Declare for &[u8; N] {
276    fn declare(name: Ident) -> Declaration {
277        <[u8; N]>::declare(name)
278    }
279}
280
281impl<const N: usize> HasTypeSpec for &[u8; N] {
282    fn type_spec() -> TypeSpec {
283        <[u8; N]>::type_spec()
284    }
285}
286
287impl<const N: usize> Declare for &mut [u8; N] {
288    fn declare(name: Ident) -> Declaration {
289        <[u8; N]>::declare(name)
290    }
291}
292
293impl<const N: usize> HasTypeSpec for &mut [u8; N] {
294    fn type_spec() -> TypeSpec {
295        <[u8; N]>::type_spec()
296    }
297}
298
299impl<const N: usize, T: HasTypeSpec> Declare for [T; N] {
300    fn declare(name: Ident) -> Declaration {
301        const { assert!(N <= u32::MAX as usize, "N exceeds u32::MAX") }
302        Declaration::FixedArray {
303            name,
304            elt_ty: T::type_spec(),
305            len: Value::Const((N as u32).into()),
306        }
307    }
308}
309
310// HasTypeSpec for [T; N] - returns embedded struct with fixed array field
311// This allows nested arrays like [[u32; 3]; 5] to work
312impl<const N: usize, T: HasTypeSpec> HasTypeSpec for [T; N] {
313    fn type_spec() -> TypeSpec {
314        const { assert!(N <= u32::MAX as usize, "N exceeds u32::MAX") }
315        // Arrays aren't types in XDR, so we represent them as embedded structs
316        // with a single field named "unnamed_0"
317        TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::FixedArray {
318            name: ident("unnamed_0"),
319            elt_ty: T::type_spec(),
320            len: Value::Const((N as u32).into()),
321        }]))
322    }
323}
324
325impl<const N: usize, T: HasTypeSpec> HasTypeSpec for &[T; N] {
326    fn type_spec() -> TypeSpec {
327        <[T; N]>::type_spec()
328    }
329}
330
331impl<const N: usize, T: HasTypeSpec> Declare for &[T; N] {
332    fn declare(name: Ident) -> Declaration {
333        <[T; N]>::declare(name)
334    }
335}
336
337impl<const N: usize, T: HasTypeSpec> HasTypeSpec for &mut [T; N] {
338    fn type_spec() -> TypeSpec {
339        <[T; N]>::type_spec()
340    }
341}
342
343impl<const N: usize, T: HasTypeSpec> Declare for &mut [T; N] {
344    fn declare(name: Ident) -> Declaration {
345        <[T; N]>::declare(name)
346    }
347}
348
349impl HasTypeSpec for &[u8] {
350    fn type_spec() -> TypeSpec {
351        TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableOpaque {
352            name: ident("unnamed_0"),
353            max: Value::Const(Constant::Unsigned(u32::MAX)),
354        }]))
355    }
356}
357
358impl Declare for &[u8] {
359    fn declare(name: Ident) -> Declaration {
360        Declaration::VariableOpaque {
361            name,
362            max: Value::Const(Constant::Unsigned(u32::MAX)),
363        }
364    }
365}
366
367impl HasTypeSpec for &mut [u8] {
368    fn type_spec() -> TypeSpec {
369        <&[u8]>::type_spec()
370    }
371}
372
373impl Declare for &mut [u8] {
374    fn declare(name: Ident) -> Declaration {
375        <&[u8]>::declare(name)
376    }
377}
378
379impl HasTypeSpec for &str {
380    fn type_spec() -> TypeSpec {
381        TypeSpec::Struct(StructBody::new(alloc::vec![<&str as Declare>::declare(
382            ident("unnamed_0")
383        ),]))
384    }
385}
386
387impl Declare for &str {
388    fn declare(name: Ident) -> Declaration {
389        Declaration::String {
390            name,
391            max: Value::Const(Constant::Unsigned(u32::MAX)),
392        }
393    }
394}
395
396impl HasTypeSpec for &mut str {
397    fn type_spec() -> TypeSpec {
398        <&str>::type_spec()
399    }
400}
401
402impl Declare for &mut str {
403    fn declare(name: Ident) -> Declaration {
404        <&str>::declare(name)
405    }
406}
407
408impl<T: HasTypeSpec> HasTypeSpec for &[T] {
409    fn type_spec() -> TypeSpec {
410        TypeSpec::Struct(StructBody::new(alloc::vec![<&[T] as Declare>::declare(
411            ident("unnamed_0"),
412        )]))
413    }
414}
415
416impl<T: HasTypeSpec> Declare for &[T] {
417    fn declare(name: Ident) -> Declaration {
418        Declaration::VariableArray {
419            name,
420            elt_ty: T::type_spec(),
421            max: Value::Const(Constant::Unsigned(u32::MAX)),
422        }
423    }
424}
425
426impl<T: HasTypeSpec> HasTypeSpec for &mut [T] {
427    fn type_spec() -> TypeSpec {
428        <&[T]>::type_spec()
429    }
430}
431
432impl<T: HasTypeSpec> Declare for &mut [T] {
433    fn declare(name: Ident) -> Declaration {
434        <&[T]>::declare(name)
435    }
436}
437
438const _: () = {
439    use xdr_common::{BytesM, StaticIfNoAlloc, StringM, VecM};
440
441    impl<T, const MAX: u32> HasTypeSpec for VecM<T, MAX>
442    where
443        T: HasTypeSpec + StaticIfNoAlloc,
444    {
445        fn type_spec() -> TypeSpec {
446            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableArray {
447                name: ident("unnamed_0"),
448                elt_ty: T::type_spec(),
449                max: Value::Const(Constant::Unsigned(MAX)),
450            }]))
451        }
452    }
453
454    impl<T, const MAX: u32> Declare for VecM<T, MAX>
455    where
456        T: HasTypeSpec + StaticIfNoAlloc,
457    {
458        fn declare(name: Ident) -> Declaration {
459            Declaration::VariableArray {
460                name,
461                elt_ty: T::type_spec(),
462                max: Value::Const(Constant::Unsigned(MAX)),
463            }
464        }
465    }
466
467    /// We default a `VecM<u8>` to declaring as a BytesM;
468    /// It's not clear to an end user what the difference is between a `VecM<u8>` and a BytesM,
469    /// so this makes the two types work more like each other.
470    impl<const MAX: u32> HasTypeSpec for VecM<u8, MAX> {
471        fn type_spec() -> TypeSpec {
472            BytesM::<MAX>::type_spec()
473        }
474    }
475
476    impl<const MAX: u32> Declare for VecM<u8, MAX> {
477        fn declare(name: Ident) -> Declaration {
478            BytesM::<MAX>::declare(name)
479        }
480    }
481
482    impl<const MAX: u32> HasTypeSpec for BytesM<MAX> {
483        fn type_spec() -> TypeSpec {
484            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableOpaque {
485                name: ident("unnamed_0"),
486                max: Value::Const(Constant::Unsigned(MAX)),
487            }]))
488        }
489    }
490
491    impl<const MAX: u32> Declare for BytesM<MAX> {
492        fn declare(name: Ident) -> Declaration {
493            Declaration::VariableOpaque {
494                name,
495                max: Value::Const(Constant::Unsigned(MAX)),
496            }
497        }
498    }
499
500    impl<const MAX: u32> Declare for StringM<MAX> {
501        fn declare(name: Ident) -> Declaration {
502            Declaration::String {
503                name,
504                max: Value::Const(Constant::Unsigned(MAX)),
505            }
506        }
507    }
508
509    impl<const MAX: u32> HasTypeSpec for StringM<MAX> {
510        fn type_spec() -> TypeSpec {
511            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::String {
512                name: ident("unnamed_0"),
513                max: Value::Const(Constant::Unsigned(MAX)),
514            }]))
515        }
516    }
517
518    impl<const MAX: u32> HasTypeSpec for &BytesM<MAX> {
519        fn type_spec() -> TypeSpec {
520            BytesM::<MAX>::type_spec()
521        }
522    }
523
524    impl<const MAX: u32> Declare for &BytesM<MAX> {
525        fn declare(name: Ident) -> Declaration {
526            BytesM::<MAX>::declare(name)
527        }
528    }
529
530    impl<const MAX: u32> HasTypeSpec for &mut BytesM<MAX> {
531        fn type_spec() -> TypeSpec {
532            BytesM::<MAX>::type_spec()
533        }
534    }
535
536    impl<const MAX: u32> Declare for &mut BytesM<MAX> {
537        fn declare(name: Ident) -> Declaration {
538            BytesM::<MAX>::declare(name)
539        }
540    }
541
542    impl<const MAX: u32> HasTypeSpec for &StringM<MAX> {
543        fn type_spec() -> TypeSpec {
544            StringM::<MAX>::type_spec()
545        }
546    }
547
548    impl<const MAX: u32> Declare for &StringM<MAX> {
549        fn declare(name: Ident) -> Declaration {
550            StringM::<MAX>::declare(name)
551        }
552    }
553
554    impl<const MAX: u32> HasTypeSpec for &mut StringM<MAX> {
555        fn type_spec() -> TypeSpec {
556            StringM::<MAX>::type_spec()
557        }
558    }
559
560    impl<const MAX: u32> Declare for &mut StringM<MAX> {
561        fn declare(name: Ident) -> Declaration {
562            StringM::<MAX>::declare(name)
563        }
564    }
565
566    impl<const MAX: u32> HasTypeSpec for &VecM<u8, MAX> {
567        fn type_spec() -> TypeSpec {
568            VecM::<u8, MAX>::type_spec()
569        }
570    }
571
572    impl<const MAX: u32> Declare for &VecM<u8, MAX> {
573        fn declare(name: Ident) -> Declaration {
574            VecM::<u8, MAX>::declare(name)
575        }
576    }
577
578    impl<const MAX: u32> HasTypeSpec for &mut VecM<u8, MAX> {
579        fn type_spec() -> TypeSpec {
580            VecM::<u8, MAX>::type_spec()
581        }
582    }
583
584    impl<const MAX: u32> Declare for &mut VecM<u8, MAX> {
585        fn declare(name: Ident) -> Declaration {
586            VecM::<u8, MAX>::declare(name)
587        }
588    }
589
590    impl<T, const MAX: u32> HasTypeSpec for &VecM<T, MAX>
591    where
592        T: HasTypeSpec + StaticIfNoAlloc,
593    {
594        fn type_spec() -> TypeSpec {
595            VecM::<T, MAX>::type_spec()
596        }
597    }
598
599    impl<T, const MAX: u32> Declare for &VecM<T, MAX>
600    where
601        T: HasTypeSpec + StaticIfNoAlloc,
602    {
603        fn declare(name: Ident) -> Declaration {
604            VecM::<T, MAX>::declare(name)
605        }
606    }
607
608    impl<T, const MAX: u32> HasTypeSpec for &mut VecM<T, MAX>
609    where
610        T: HasTypeSpec + StaticIfNoAlloc,
611    {
612        fn type_spec() -> TypeSpec {
613            VecM::<T, MAX>::type_spec()
614        }
615    }
616
617    impl<T, const MAX: u32> Declare for &mut VecM<T, MAX>
618    where
619        T: HasTypeSpec + StaticIfNoAlloc,
620    {
621        fn declare(name: Ident) -> Declaration {
622            VecM::<T, MAX>::declare(name)
623        }
624    }
625};
626
627const _: () = {
628    use alloc::boxed::Box;
629    use xdr_common::{BytesM, StaticIfNoAlloc, StringM, VecM};
630
631    impl<const MAX: u32> Declare for Box<BytesM<MAX>> {
632        fn declare(name: Ident) -> Declaration {
633            BytesM::<MAX>::declare(name)
634        }
635    }
636
637    impl<const MAX: u32> Declare for Box<StringM<MAX>> {
638        fn declare(name: Ident) -> Declaration {
639            StringM::<MAX>::declare(name)
640        }
641    }
642
643    impl<T, const MAX: u32> Declare for Box<VecM<T, MAX>>
644    where
645        T: HasTypeSpec + StaticIfNoAlloc,
646    {
647        fn declare(name: Ident) -> Declaration {
648            VecM::<T, MAX>::declare(name)
649        }
650    }
651
652    impl<const MAX: u32> Declare for Box<VecM<u8, MAX>> {
653        fn declare(name: Ident) -> Declaration {
654            BytesM::<MAX>::declare(name)
655        }
656    }
657
658    impl<const MAX: u32> HasTypeSpec for Box<BytesM<MAX>> {
659        fn type_spec() -> TypeSpec {
660            BytesM::<MAX>::type_spec()
661        }
662    }
663
664    impl<const MAX: u32> HasTypeSpec for Box<StringM<MAX>> {
665        fn type_spec() -> TypeSpec {
666            StringM::<MAX>::type_spec()
667        }
668    }
669
670    impl<T, const MAX: u32> HasTypeSpec for Box<VecM<T, MAX>>
671    where
672        T: HasTypeSpec + StaticIfNoAlloc,
673    {
674        fn type_spec() -> TypeSpec {
675            VecM::<T, MAX>::type_spec()
676        }
677    }
678
679    impl<const MAX: u32> HasTypeSpec for Box<VecM<u8, MAX>> {
680        fn type_spec() -> TypeSpec {
681            BytesM::<MAX>::type_spec()
682        }
683    }
684
685    impl<T: HasTypeSpec> Declare for Box<Option<T>> {
686        fn declare(name: Ident) -> Declaration {
687            <Option<T>>::declare(name)
688        }
689    }
690
691    impl<T: HasTypeSpec> HasTypeSpec for Box<Option<T>> {
692        fn type_spec() -> TypeSpec {
693            <Option<T>>::type_spec()
694        }
695    }
696};
697
698#[cfg(feature = "heapless")]
699const _: () = {
700    use heapless::{String, Vec};
701
702    fn heapless_max_value<const N: usize>() -> Value {
703        const { assert!(N <= u32::MAX as usize, "N exceeds u32::MAX") }
704        Value::Const(Constant::Unsigned(N as u32))
705    }
706
707    impl<const N: usize> HasTypeSpec for Vec<u8, N> {
708        fn type_spec() -> TypeSpec {
709            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableOpaque {
710                name: ident("unnamed_0"),
711                max: heapless_max_value::<N>(),
712            }]))
713        }
714    }
715
716    impl<const N: usize> Declare for Vec<u8, N> {
717        fn declare(name: Ident) -> Declaration {
718            Declaration::VariableOpaque {
719                name,
720                max: heapless_max_value::<N>(),
721            }
722        }
723    }
724
725    impl<const N: usize> HasTypeSpec for &Vec<u8, N> {
726        fn type_spec() -> TypeSpec {
727            Vec::<u8, N>::type_spec()
728        }
729    }
730
731    impl<const N: usize> Declare for &Vec<u8, N> {
732        fn declare(name: Ident) -> Declaration {
733            Vec::<u8, N>::declare(name)
734        }
735    }
736
737    impl<const N: usize> HasTypeSpec for &mut Vec<u8, N> {
738        fn type_spec() -> TypeSpec {
739            Vec::<u8, N>::type_spec()
740        }
741    }
742
743    impl<const N: usize> Declare for &mut Vec<u8, N> {
744        fn declare(name: Ident) -> Declaration {
745            Vec::<u8, N>::declare(name)
746        }
747    }
748
749    impl<T, const N: usize> HasTypeSpec for Vec<T, N>
750    where
751        T: HasTypeSpec,
752    {
753        fn type_spec() -> TypeSpec {
754            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableArray {
755                name: ident("unnamed_0"),
756                elt_ty: T::type_spec(),
757                max: heapless_max_value::<N>(),
758            }]))
759        }
760    }
761
762    impl<T, const N: usize> Declare for Vec<T, N>
763    where
764        T: HasTypeSpec,
765    {
766        fn declare(name: Ident) -> Declaration {
767            Declaration::VariableArray {
768                name,
769                elt_ty: T::type_spec(),
770                max: heapless_max_value::<N>(),
771            }
772        }
773    }
774
775    impl<T, const N: usize> HasTypeSpec for &Vec<T, N>
776    where
777        T: HasTypeSpec,
778    {
779        fn type_spec() -> TypeSpec {
780            Vec::<T, N>::type_spec()
781        }
782    }
783
784    impl<T, const N: usize> Declare for &Vec<T, N>
785    where
786        T: HasTypeSpec,
787    {
788        fn declare(name: Ident) -> Declaration {
789            Vec::<T, N>::declare(name)
790        }
791    }
792
793    impl<T, const N: usize> HasTypeSpec for &mut Vec<T, N>
794    where
795        T: HasTypeSpec,
796    {
797        fn type_spec() -> TypeSpec {
798            Vec::<T, N>::type_spec()
799        }
800    }
801
802    impl<T, const N: usize> Declare for &mut Vec<T, N>
803    where
804        T: HasTypeSpec,
805    {
806        fn declare(name: Ident) -> Declaration {
807            Vec::<T, N>::declare(name)
808        }
809    }
810
811    impl<const N: usize> HasTypeSpec for String<N> {
812        fn type_spec() -> TypeSpec {
813            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::String {
814                name: ident("unnamed_0"),
815                max: heapless_max_value::<N>(),
816            }]))
817        }
818    }
819
820    impl<const N: usize> Declare for String<N> {
821        fn declare(name: Ident) -> Declaration {
822            Declaration::String {
823                name,
824                max: heapless_max_value::<N>(),
825            }
826        }
827    }
828
829    impl<const N: usize> HasTypeSpec for &String<N> {
830        fn type_spec() -> TypeSpec {
831            String::<N>::type_spec()
832        }
833    }
834
835    impl<const N: usize> Declare for &String<N> {
836        fn declare(name: Ident) -> Declaration {
837            String::<N>::declare(name)
838        }
839    }
840
841    impl<const N: usize> HasTypeSpec for &mut String<N> {
842        fn type_spec() -> TypeSpec {
843            String::<N>::type_spec()
844        }
845    }
846
847    impl<const N: usize> Declare for &mut String<N> {
848        fn declare(name: Ident) -> Declaration {
849            String::<N>::declare(name)
850        }
851    }
852};
853
854const _: () = {
855    use alloc::vec::Vec;
856
857    impl HasTypeSpec for Vec<u8> {
858        fn type_spec() -> TypeSpec {
859            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableOpaque {
860                name: ident("unnamed_0"),
861                max: Value::Const(Constant::Unsigned(u32::MAX)),
862            }]))
863        }
864    }
865
866    impl Declare for Vec<u8> {
867        fn declare(name: Ident) -> Declaration {
868            Declaration::VariableOpaque {
869                name,
870                max: Value::Const(Constant::Unsigned(u32::MAX)),
871            }
872        }
873    }
874
875    impl<T: HasTypeSpec> HasTypeSpec for Vec<T> {
876        fn type_spec() -> TypeSpec {
877            TypeSpec::Struct(StructBody::new(alloc::vec![Declaration::VariableArray {
878                name: ident("unnamed_0"),
879                elt_ty: T::type_spec(),
880                max: Value::Const(Constant::Unsigned(u32::MAX)),
881            }]))
882        }
883    }
884
885    impl<T: HasTypeSpec> Declare for Vec<T> {
886        fn declare(name: Ident) -> Declaration {
887            Declaration::VariableArray {
888                name,
889                elt_ty: T::type_spec(),
890                max: Value::Const(Constant::Unsigned(u32::MAX)),
891            }
892        }
893    }
894
895    impl HasTypeSpec for alloc::string::String {
896        fn type_spec() -> TypeSpec {
897            TypeSpec::Struct(StructBody::new(alloc::vec![
898                <alloc::string::String as Declare>::declare(ident("unnamed_0")),
899            ]))
900        }
901    }
902
903    impl Declare for alloc::string::String {
904        fn declare(name: Ident) -> Declaration {
905            Declaration::String {
906                name,
907                max: Value::Const(Constant::Unsigned(u32::MAX)),
908            }
909        }
910    }
911
912    impl HasTypeSpec for &Vec<u8> {
913        fn type_spec() -> TypeSpec {
914            Vec::<u8>::type_spec()
915        }
916    }
917
918    impl Declare for &Vec<u8> {
919        fn declare(name: Ident) -> Declaration {
920            Vec::<u8>::declare(name)
921        }
922    }
923
924    impl HasTypeSpec for &mut Vec<u8> {
925        fn type_spec() -> TypeSpec {
926            Vec::<u8>::type_spec()
927        }
928    }
929
930    impl Declare for &mut Vec<u8> {
931        fn declare(name: Ident) -> Declaration {
932            Vec::<u8>::declare(name)
933        }
934    }
935
936    impl<T: HasTypeSpec> HasTypeSpec for &Vec<T> {
937        fn type_spec() -> TypeSpec {
938            Vec::<T>::type_spec()
939        }
940    }
941
942    impl<T: HasTypeSpec> Declare for &Vec<T> {
943        fn declare(name: Ident) -> Declaration {
944            Vec::<T>::declare(name)
945        }
946    }
947
948    impl<T: HasTypeSpec> HasTypeSpec for &mut Vec<T> {
949        fn type_spec() -> TypeSpec {
950            Vec::<T>::type_spec()
951        }
952    }
953
954    impl<T: HasTypeSpec> Declare for &mut Vec<T> {
955        fn declare(name: Ident) -> Declaration {
956            Vec::<T>::declare(name)
957        }
958    }
959
960    impl HasTypeSpec for &alloc::string::String {
961        fn type_spec() -> TypeSpec {
962            alloc::string::String::type_spec()
963        }
964    }
965
966    impl Declare for &alloc::string::String {
967        fn declare(name: Ident) -> Declaration {
968            alloc::string::String::declare(name)
969        }
970    }
971
972    impl HasTypeSpec for &mut alloc::string::String {
973        fn type_spec() -> TypeSpec {
974            alloc::string::String::type_spec()
975        }
976    }
977
978    impl Declare for &mut alloc::string::String {
979        fn declare(name: Ident) -> Declaration {
980            alloc::string::String::declare(name)
981        }
982    }
983};
984
985/// Unit declares as `void`, which is valid as a standalone struct
986/// field or union arm but produces invalid XDR-IDL when nested inside
987/// container types (`Option<()>`, `Vec<()>`, `VecM<()>`, `[(); N]`,
988/// etc.).  The runtime codecs handle these compositions correctly,
989/// but the schema output is not meaningful.  Avoid `()` inside
990/// generic wrappers in types that require schema generation.
991impl Declare for () {
992    fn declare(_name: Ident) -> Declaration {
993        Declaration::Void
994    }
995}
996
997impl<T> Declare for core::marker::PhantomData<T> {
998    fn declare(_name: Ident) -> Declaration {
999        Declaration::Void
1000    }
1001}
1002
1003impl<T> Declare for T
1004where
1005    T: HasTypeDef,
1006{
1007    fn declare(name: Ident) -> Declaration {
1008        T::named_declaration(name)
1009    }
1010}
1011
1012// TODO: feature-gate this, as some users may not want it?
1013impl<T: HasTypeDef> HasTypeDef for &T {
1014    fn type_def() -> TypeDef {
1015        T::type_def()
1016    }
1017
1018    fn type_ident() -> Ident {
1019        T::type_ident()
1020    }
1021
1022    fn named_declaration(name: Ident) -> Declaration {
1023        T::named_declaration(name)
1024    }
1025}
1026
1027impl<T: HasTypeDef> HasTypeDef for &mut T {
1028    fn type_def() -> TypeDef {
1029        T::type_def()
1030    }
1031
1032    fn type_ident() -> Ident {
1033        T::type_ident()
1034    }
1035
1036    fn named_declaration(name: Ident) -> Declaration {
1037        T::named_declaration(name)
1038    }
1039}
1040
1041// Tuple implementations - map tuples to inline embedded structs with numbered fields
1042// Tuples use TypeSpec::Struct (embedded) instead of TypeDef (named type) to avoid
1043// name conflicts with nested tuples and to embed the struct definition inline in JSON
1044macro_rules! impl_tuple_types {
1045    ( $($idx:tt : $t:ident),+ ) => {
1046        // HasTypeSpec returns an embedded struct, not a named type reference
1047        impl<$($t),+> HasTypeSpec for ($($t,)+)
1048        where
1049            $( $t: Declare, )+
1050        {
1051            fn type_spec() -> TypeSpec {
1052                // Create an embedded struct body
1053                let body = StructBody::new(alloc::vec![
1054                    $(
1055                        $t::declare(ident(concat!("unnamed_", stringify!($idx)))),
1056                    )+
1057                ]);
1058
1059                // Return embedded struct, not a named type
1060                TypeSpec::Struct(body)
1061            }
1062        }
1063
1064        // Declare creates a Declaration::Named with the embedded struct TypeSpec
1065        impl<$($t),+> Declare for ($($t,)+)
1066        where
1067            $( $t: Declare, )+
1068        {
1069            fn declare(name: Ident) -> Declaration {
1070                Declaration::Named {
1071                    name,
1072                    ty: Self::type_spec(),
1073                }
1074            }
1075        }
1076    };
1077}
1078
1079// Implement for tuples of size 1-16 (matching ReadXDR/WriteXDR implementations)
1080impl_tuple_types!(0: T0);
1081impl_tuple_types!(0: T0, 1: T1);
1082impl_tuple_types!(0: T0, 1: T1, 2: T2);
1083impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3);
1084impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4);
1085impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5);
1086impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6);
1087impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7);
1088impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8);
1089impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9);
1090impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10);
1091impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11);
1092impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12);
1093impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12, 13: T13);
1094impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12, 13: T13, 14: T14);
1095impl_tuple_types!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12, 13: T13, 14: T14, 15: T15);