qubit_codec/value/value_codec_registration.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
11use crate::ValueBytesCodecDescriptor;
12use crate::ValueCodecId;
13use crate::ValueCodecRegistrationSource;
14use crate::ValueStringCodecDescriptor;
15
16/// One statically linked value-codec implementation.
17///
18/// A registration bundles the stable identifier used for lookup and duplicate
19/// detection, a borrowed executable descriptor, and the link-time location the
20/// registration was declared at. It is produced by
21/// [`register_value_string_codec!`](crate::register_value_string_codec) or
22/// [`register_value_bytes_codec!`](crate::register_value_bytes_codec) rather
23/// than built by hand.
24///
25/// The registration macros validate codecs through the matching descriptor.
26/// Encoder and decoder error types must implement `Error + Send + Sync +
27/// 'static`; codec and value types do not need those bounds.
28///
29/// # Type Parameters
30///
31/// - `D`: Executable codec descriptor type; `'static` lets the registry keep a
32/// borrowed `&'static D` for the whole program run.
33///
34/// # Examples
35///
36/// ```
37/// # #[cfg(feature = "json")]
38/// # {
39/// use qubit_codec::JsonStringValueCodec;
40/// use qubit_codec::ValueCodecId;
41/// use qubit_codec::ValueCodecRegistration;
42/// use qubit_codec::ValueCodecRegistrationSource;
43/// use qubit_codec::ValueStringCodecDescriptor;
44/// use qubit_codec::ValueStringCodecRegistration;
45///
46/// static DESCRIPTOR: ValueStringCodecDescriptor =
47/// ValueStringCodecDescriptor::of::<JsonStringValueCodec<u32>, u32>();
48///
49/// let registration: ValueStringCodecRegistration = ValueCodecRegistration::new(
50/// ValueCodecId::new("example.u32"),
51/// &DESCRIPTOR,
52/// ValueCodecRegistrationSource::new(
53/// "qubit-codec",
54/// "doctest",
55/// "src/value/value_codec_registration.rs",
56/// 1,
57/// ),
58/// );
59/// assert_eq!(registration.id().as_str(), "example.u32");
60/// # }
61/// ```
62#[derive(Clone, Copy, Debug)]
63pub struct ValueCodecRegistration<D: 'static> {
64 /// Stable identifier shared with registry lookups and duplicate detection.
65 id: ValueCodecId,
66 /// Executable descriptor borrowed from static storage.
67 descriptor: &'static D,
68 /// Link-time location the registration was declared at.
69 source: ValueCodecRegistrationSource,
70}
71
72/// A string-wire value-codec registration.
73pub type ValueStringCodecRegistration = ValueCodecRegistration<ValueStringCodecDescriptor>;
74
75/// A bytes-wire value-codec registration.
76pub type ValueBytesCodecRegistration = ValueCodecRegistration<ValueBytesCodecDescriptor>;
77
78impl<D: 'static> ValueCodecRegistration<D> {
79 /// Creates a registration from validated static facts.
80 ///
81 /// # Parameters
82 ///
83 /// - `id`: Stable identifier for registry lookups.
84 /// - `descriptor`: Executable descriptor borrowed from static storage.
85 /// - `source`: Link-time location recorded for diagnostics.
86 ///
87 /// # Returns
88 ///
89 /// Returns a registration that borrows `descriptor` for the whole run.
90 ///
91 /// Prefer [`register_value_string_codec!
92 /// `](crate::register_value_string_codec)
93 /// or [`register_value_bytes_codec!`](crate::register_value_bytes_codec)
94 /// over calling this directly.
95 #[doc(hidden)]
96 #[inline]
97 #[must_use]
98 pub const fn new(id: ValueCodecId, descriptor: &'static D, source: ValueCodecRegistrationSource) -> Self {
99 Self { id, descriptor, source }
100 }
101
102 /// Returns the stable value-codec ID.
103 ///
104 /// # Returns
105 ///
106 /// Returns the identifier this registration was filed under.
107 #[inline]
108 #[must_use]
109 pub const fn id(&self) -> ValueCodecId {
110 self.id
111 }
112
113 /// Returns the executable descriptor.
114 ///
115 /// # Returns
116 ///
117 /// Returns the borrowed static descriptor, valid for the whole program run.
118 #[inline]
119 #[must_use]
120 pub const fn descriptor(&self) -> &'static D {
121 self.descriptor
122 }
123
124 /// Returns the linked source location.
125 ///
126 /// # Returns
127 ///
128 /// Returns the link-time crate, module, file, and line of the declaration.
129 #[inline]
130 #[must_use]
131 pub const fn source(&self) -> ValueCodecRegistrationSource {
132 self.source
133 }
134}
135
136/// Registers a default-constructible bidirectional string codec.
137///
138/// The macro must be invoked at module scope; it defines the descriptor, a
139/// registration function, and an inventory submission in an anonymous const.
140/// The `id` must be a constant expression. Use a literal, constant, or
141/// `const fn` result; a runtime function call cannot define a static ID.
142/// The encoder and decoder error types must implement `Error + Send + Sync +
143/// 'static`, as required by [`ValueStringCodecDescriptor::of`].
144///
145/// # Examples
146///
147/// ```compile_fail
148/// use qubit_codec::JsonStringValueCodec;
149/// use qubit_codec::register_value_string_codec;
150///
151/// fn invalid_id() -> &'static str { "example.u32" }
152/// register_value_string_codec!(id = invalid_id(), codec = JsonStringValueCodec<u32>, value = u32);
153/// ```
154///
155/// ```compile_fail
156/// use qubit_codec::JsonStringValueCodec;
157/// use qubit_codec::register_value_string_codec;
158/// register_value_string_codec!(id = "invalid-id", codec = JsonStringValueCodec<u32>, value = u32);
159/// ```
160///
161/// ```
162/// # #[cfg(feature = "json")]
163/// # {
164/// use qubit_codec::JsonStringValueCodec;
165/// use qubit_codec::register_value_string_codec;
166///
167/// register_value_string_codec!(
168/// id = "example.u32",
169/// codec = JsonStringValueCodec<u32>,
170/// value = u32,
171/// );
172/// # }
173/// ```
174#[macro_export]
175macro_rules! register_value_string_codec {
176 (id = $id:expr, codec = $codec:ty, value = $value:ty $(,)?) => {
177 const _: () = {
178 const ID: $crate::ValueCodecId = $crate::ValueCodecId::new($id);
179 static DESCRIPTOR: $crate::ValueStringCodecDescriptor =
180 $crate::ValueStringCodecDescriptor::of::<$codec, $value>();
181
182 fn registration() -> $crate::ValueStringCodecRegistration {
183 $crate::ValueCodecRegistration::new(
184 ID,
185 &DESCRIPTOR,
186 $crate::ValueCodecRegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
187 )
188 }
189
190 $crate::__private::inventory::submit! {
191 $crate::ValueStringCodecRegistrationFactory(registration)
192 }
193 };
194 };
195}
196
197/// Registers a default-constructible bidirectional bytes codec.
198///
199/// The macro must be invoked at module scope; it defines the descriptor, a
200/// registration function, and an inventory submission in an anonymous const.
201/// The `id` must be a constant expression. Use a literal, constant, or
202/// `const fn` result; a runtime function call cannot define a static ID.
203/// The encoder and decoder error types must implement `Error + Send + Sync +
204/// 'static`, as required by [`ValueBytesCodecDescriptor::of`].
205///
206/// # Examples
207///
208/// ```compile_fail
209/// use qubit_codec::JsonBytesValueCodec;
210/// use qubit_codec::register_value_bytes_codec;
211/// register_value_bytes_codec!(id = "invalid-id", codec = JsonBytesValueCodec<u32>, value = u32);
212/// ```
213///
214/// ```
215/// # #[cfg(feature = "json")]
216/// # {
217/// use qubit_codec::JsonBytesValueCodec;
218/// use qubit_codec::register_value_bytes_codec;
219///
220/// register_value_bytes_codec!(
221/// id = "example.u32",
222/// codec = JsonBytesValueCodec<u32>,
223/// value = u32,
224/// );
225/// # }
226/// ```
227#[macro_export]
228macro_rules! register_value_bytes_codec {
229 (id = $id:expr, codec = $codec:ty, value = $value:ty $(,)?) => {
230 const _: () = {
231 const ID: $crate::ValueCodecId = $crate::ValueCodecId::new($id);
232 static DESCRIPTOR: $crate::ValueBytesCodecDescriptor =
233 $crate::ValueBytesCodecDescriptor::of::<$codec, $value>();
234
235 fn registration() -> $crate::ValueBytesCodecRegistration {
236 $crate::ValueCodecRegistration::new(
237 ID,
238 &DESCRIPTOR,
239 $crate::ValueCodecRegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
240 )
241 }
242
243 $crate::__private::inventory::submit! {
244 $crate::ValueBytesCodecRegistrationFactory(registration)
245 }
246 };
247 };
248}