Skip to main content

qubit_codec/value/
value_codec_registration_macros.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9//! Distributed value-codec registrations.
10
11/// Registers a default-constructible bidirectional string codec.
12///
13/// The macro must be invoked at module scope; it defines the descriptor, a
14/// registration function, and an inventory submission in an anonymous const.
15/// The `id` must be a constant expression. Use a literal, constant, or
16/// `const fn` result; a runtime function call cannot define a static ID.
17/// The encoder and decoder error types must implement `Error + Send + Sync +
18/// 'static`, as required by [`crate::ValueStringCodecDescriptor::of`].
19/// Without `input`, the encoder input type is `value`. The optional `input`
20/// form selects a borrowed view of the owned `value` type for encoding; the
21/// descriptor still accepts and decodes the owned value type.
22///
23/// # Examples
24///
25/// A registry-only example with a local codec:
26///
27/// ```
28/// use core::convert::Infallible;
29/// use qubit_codec::{ValueDecoder, ValueEncoder, ValueStringCodecRegistry};
30/// use qubit_codec::register_value_string_codec;
31///
32/// #[derive(Default)]
33/// struct TextCodec;
34/// impl ValueEncoder<String> for TextCodec {
35///     type Output = String;
36///     type Error = Infallible;
37///     fn encode(&mut self, value: &String) -> Result<String, Infallible> { Ok(value.clone()) }
38/// }
39/// impl ValueDecoder<str> for TextCodec {
40///     type Output = String;
41///     type Error = Infallible;
42///     fn decode(&mut self, input: &str) -> Result<String, Infallible> { Ok(input.to_owned()) }
43/// }
44/// register_value_string_codec!(id = "example.text", codec = TextCodec, value = String);
45/// let registry = ValueStringCodecRegistry::try_global().unwrap();
46/// let descriptor = registry.get("example.text").unwrap().descriptor();
47/// let encoded = descriptor.encode(&String::from("hello")).unwrap();
48/// assert_eq!("hello", encoded);
49/// let decoded = descriptor.decode(&encoded).unwrap();
50/// assert_eq!("hello", *decoded.downcast::<String>().unwrap());
51/// ```
52///
53/// An invalid ID fails during macro expansion:
54///
55/// ```compile_fail
56/// # use core::convert::Infallible;
57/// # use qubit_codec::{ValueDecoder, ValueEncoder};
58/// # use qubit_codec::register_value_string_codec;
59/// # #[derive(Default)] struct TextCodec;
60/// # impl ValueEncoder<String> for TextCodec { type Output = String; type Error = Infallible; fn encode(&mut self, value: &String) -> Result<String, Infallible> { Ok(value.clone()) } }
61/// # impl ValueDecoder<str> for TextCodec { type Output = String; type Error = Infallible; fn decode(&mut self, input: &str) -> Result<String, Infallible> { Ok(input.to_owned()) } }
62/// register_value_string_codec!(id = "invalid-id", codec = TextCodec, value = String);
63/// ```
64///
65/// A runtime ID cannot initialize the static registration:
66///
67/// ```compile_fail
68/// # use core::convert::Infallible;
69/// # use qubit_codec::{ValueDecoder, ValueEncoder};
70/// # use qubit_codec::register_value_string_codec;
71/// # #[derive(Default)] struct TextCodec;
72/// # impl ValueEncoder<String> for TextCodec { type Output = String; type Error = Infallible; fn encode(&mut self, value: &String) -> Result<String, Infallible> { Ok(value.clone()) } }
73/// # impl ValueDecoder<str> for TextCodec { type Output = String; type Error = Infallible; fn decode(&mut self, input: &str) -> Result<String, Infallible> { Ok(input.to_owned()) } }
74/// fn runtime_id() -> &'static str { "example.text" }
75/// register_value_string_codec!(id = runtime_id(), codec = TextCodec, value = String);
76/// ```
77#[macro_export]
78macro_rules! register_value_string_codec {
79    (id = $id:expr, codec = $codec:ty, value = $value:ty, input = $input:ty $(,)?) => {
80        const _: () = {
81            const ID: $crate::ValueCodecId = $crate::ValueCodecId::new($id);
82            static DESCRIPTOR: $crate::ValueStringCodecDescriptor =
83                $crate::ValueStringCodecDescriptor::of_borrowed::<$codec, $value, $input>();
84            fn registration() -> $crate::ValueStringCodecRegistration {
85                $crate::ValueCodecRegistration::new(
86                    ID,
87                    &DESCRIPTOR,
88                    $crate::ValueCodecRegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
89                )
90            }
91            $crate::__private::inventory::submit! {
92                $crate::ValueStringCodecRegistrationFactory(registration)
93            }
94        };
95    };
96    (id = $id:expr, codec = $codec:ty, value = $value:ty $(,)?) => {
97        const _: () = {
98            const ID: $crate::ValueCodecId = $crate::ValueCodecId::new($id);
99            static DESCRIPTOR: $crate::ValueStringCodecDescriptor =
100                $crate::ValueStringCodecDescriptor::of::<$codec, $value>();
101
102            fn registration() -> $crate::ValueStringCodecRegistration {
103                $crate::ValueCodecRegistration::new(
104                    ID,
105                    &DESCRIPTOR,
106                    $crate::ValueCodecRegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
107                )
108            }
109
110            $crate::__private::inventory::submit! {
111                $crate::ValueStringCodecRegistrationFactory(registration)
112            }
113        };
114    };
115}
116
117/// Registers a default-constructible bidirectional bytes codec.
118///
119/// The macro must be invoked at module scope; it defines the descriptor, a
120/// registration function, and an inventory submission in an anonymous const.
121/// The `id` must be a constant expression. Use a literal, constant, or
122/// `const fn` result; a runtime function call cannot define a static ID.
123/// The encoder and decoder error types must implement `Error + Send + Sync +
124/// 'static`, as required by [`crate::ValueBytesCodecDescriptor::of`].
125///
126/// # Examples
127///
128/// A registry-only example with a local codec:
129///
130/// ```
131/// use core::convert::Infallible;
132/// use qubit_codec::{ValueBytesCodecRegistry, ValueDecoder, ValueEncoder};
133/// use qubit_codec::register_value_bytes_codec;
134///
135/// #[derive(Default)]
136/// struct BytesCodec;
137/// impl ValueEncoder<Vec<u8>> for BytesCodec {
138///     type Output = Vec<u8>;
139///     type Error = Infallible;
140///     fn encode(&mut self, value: &Vec<u8>) -> Result<Vec<u8>, Infallible> { Ok(value.clone()) }
141/// }
142/// impl ValueDecoder<[u8]> for BytesCodec {
143///     type Output = Vec<u8>;
144///     type Error = Infallible;
145///     fn decode(&mut self, input: &[u8]) -> Result<Vec<u8>, Infallible> { Ok(input.to_vec()) }
146/// }
147/// register_value_bytes_codec!(id = "example.bytes", codec = BytesCodec, value = Vec<u8>);
148/// let registry = ValueBytesCodecRegistry::try_global().unwrap();
149/// let descriptor = registry.get("example.bytes").unwrap().descriptor();
150/// let encoded = descriptor.encode(&vec![1_u8, 2]).unwrap();
151/// assert_eq!(vec![1_u8, 2], encoded);
152/// let decoded = descriptor.decode(&encoded).unwrap();
153/// assert_eq!(vec![1_u8, 2], *decoded.downcast::<Vec<u8>>().unwrap());
154/// ```
155///
156/// An invalid ID fails during macro expansion:
157///
158/// ```compile_fail
159/// # use core::convert::Infallible;
160/// # use qubit_codec::{ValueDecoder, ValueEncoder};
161/// # use qubit_codec::register_value_bytes_codec;
162/// # #[derive(Default)] struct BytesCodec;
163/// # impl ValueEncoder<Vec<u8>> for BytesCodec { type Output = Vec<u8>; type Error = Infallible; fn encode(&mut self, value: &Vec<u8>) -> Result<Vec<u8>, Infallible> { Ok(value.clone()) } }
164/// # impl ValueDecoder<[u8]> for BytesCodec { type Output = Vec<u8>; type Error = Infallible; fn decode(&mut self, input: &[u8]) -> Result<Vec<u8>, Infallible> { Ok(input.to_vec()) } }
165/// register_value_bytes_codec!(id = "invalid-id", codec = BytesCodec, value = Vec<u8>);
166/// ```
167#[macro_export]
168macro_rules! register_value_bytes_codec {
169    (id = $id:expr, codec = $codec:ty, value = $value:ty $(,)?) => {
170        const _: () = {
171            const ID: $crate::ValueCodecId = $crate::ValueCodecId::new($id);
172            static DESCRIPTOR: $crate::ValueBytesCodecDescriptor =
173                $crate::ValueBytesCodecDescriptor::of::<$codec, $value>();
174
175            fn registration() -> $crate::ValueBytesCodecRegistration {
176                $crate::ValueCodecRegistration::new(
177                    ID,
178                    &DESCRIPTOR,
179                    $crate::ValueCodecRegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
180                )
181            }
182
183            $crate::__private::inventory::submit! {
184                $crate::ValueBytesCodecRegistrationFactory(registration)
185            }
186        };
187    };
188}