Skip to main content

rmk_types/
fmt.rs

1//! Formatting helpers shared across the RMK crates.
2
3/// Implements `core::fmt::Debug` and (under `feature = "defmt"`) `defmt::Format`
4/// for `$ty`, rendering as a list whose entries come from iterating `$iter`.
5///
6/// `$iter` is evaluated in a scope where `$self` is bound to `&self`. Items
7/// must implement `Debug` and, when `defmt` is enabled, `defmt::Format`.
8///
9/// One invocation keeps the log and defmt renderings from drifting apart.
10#[macro_export]
11macro_rules! impl_debug_list {
12    ($ty:ty, |$self:ident| $iter:expr $(,)?) => {
13        impl ::core::fmt::Debug for $ty {
14            fn fmt(&$self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
15                f.debug_list().entries($iter).finish()
16            }
17        }
18        #[cfg(feature = "defmt")]
19        impl ::defmt::Format for $ty {
20            fn format(&$self, f: ::defmt::Formatter) {
21                ::defmt::write!(f, "[");
22                let mut first = true;
23                for v in $iter {
24                    if first {
25                        first = false;
26                    } else {
27                        ::defmt::write!(f, ", ");
28                    }
29                    ::defmt::write!(f, "{}", v);
30                }
31                ::defmt::write!(f, "]");
32            }
33        }
34    };
35}
36
37/// Bridges a `#[bitfield(u8)]` type to a non-bitfield TypeScript object: Serialize /
38/// Deserialize (a named-bool object for human-readable formats, the packed `u8` on the
39/// wire), the `.d.ts` decl, and the self-describing wasm ABI — all from one field table.
40#[macro_export]
41macro_rules! bitfield_named_serde {
42    ($bitfield:ident, $typescript_type:literal, { $( $field:ident = $setter:ident ),+ $(,)? }) => {
43        impl serde::Serialize for $bitfield {
44            fn serialize<S: serde::Serializer>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error> {
45                if serializer.is_human_readable() {
46                    #[derive(serde::Serialize)]
47                    struct Repr { $($field: bool,)+ }
48                    serde::Serialize::serialize(&Repr { $($field: self.$field(),)+ }, serializer)
49                } else {
50                    serializer.serialize_u8(self.into_bits())
51                }
52            }
53        }
54
55        impl<'de> serde::Deserialize<'de> for $bitfield {
56            fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> ::core::result::Result<Self, D::Error> {
57                if deserializer.is_human_readable() {
58                    // A flag left out is a flag that is off: the only reading a
59                    // bitfield has for an absent bool. Lets a caller name the
60                    // flags it sets instead of all of them.
61                    #[derive(serde::Deserialize)]
62                    #[serde(default)]
63                    struct Repr { $($field: bool,)+ }
64
65                    impl ::core::default::Default for Repr {
66                        fn default() -> Self { Self { $($field: false,)+ } }
67                    }
68                    let r = <Repr as serde::Deserialize>::deserialize(deserializer)?;
69                    ::core::result::Result::Ok(Self::new() $(.$setter(r.$field))+)
70                } else {
71                    ::core::result::Result::Ok(Self::from_bits(<u8 as serde::Deserialize>::deserialize(deserializer)?))
72                }
73            }
74        }
75
76        // Static `.d.ts` shape, built from the same field table.
77        #[cfg(feature = "wasm")]
78        const _: () = {
79            #[::wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
80            const TS_APPEND_CONTENT: &'static str = concat!(
81                "export type ", stringify!($bitfield), " = {",
82                $( " ", stringify!($field), ": boolean;", )+
83                " };"
84            );
85        };
86
87        // Self-describing wasm ABI, marshaled via the human-readable `Serialize` object.
88        $crate::wasm_object_abi!($bitfield, $typescript_type);
89    };
90}
91
92/// Gives a type the wasm ABI to be returned to JS as an object, keeping its own name
93/// in the generated `.d.ts` — so a `#[wasm_bindgen]` fn returning it needs no
94/// `unchecked_return_type`.
95///
96/// The value is converted with `serde_wasm_bindgen`, so the type must impl
97/// `Serialize`/`Deserialize` (whose human-readable form is that object) and declare
98/// its TS shape with a `typescript_custom_section` (`export type <Type> = …`).
99#[macro_export]
100macro_rules! wasm_object_abi {
101    ($ty:ident, $typescript_type:literal) => {
102        #[cfg(feature = "wasm")]
103        const _: () = {
104            use ::wasm_bindgen::convert::{IntoWasmAbi, OptionIntoWasmAbi};
105            use ::wasm_bindgen::describe::WasmDescribe;
106            use ::wasm_bindgen::prelude::*;
107
108            #[wasm_bindgen]
109            extern "C" {
110                #[wasm_bindgen(typescript_type = $typescript_type)]
111                type WasmObject;
112            }
113
114            impl From<$ty> for JsValue {
115                #[inline]
116                fn from(value: $ty) -> Self {
117                    ::serde_wasm_bindgen::to_value(&value).unwrap_throw()
118                }
119            }
120
121            impl WasmDescribe for $ty {
122                #[inline]
123                fn describe() {
124                    WasmObject::describe();
125                }
126            }
127
128            impl IntoWasmAbi for $ty {
129                type Abi = <JsValue as IntoWasmAbi>::Abi;
130                #[inline]
131                fn into_abi(self) -> Self::Abi {
132                    JsValue::from(self).into_abi()
133                }
134            }
135
136            impl OptionIntoWasmAbi for $ty {
137                #[inline]
138                fn none() -> Self::Abi {
139                    <JsValue as OptionIntoWasmAbi>::none()
140                }
141            }
142        };
143    };
144}