qubit_codec/value/value_bytes_codec_descriptor.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//! Safe type-erased bidirectional bytes value-codec descriptors.
10
11use core::any::type_name;
12use core::fmt::Debug;
13use core::fmt::Formatter;
14use core::fmt::Result as FmtResult;
15use std::any::Any;
16use std::any::TypeId;
17use std::error::Error;
18
19use crate::ValueCodecExecutionError;
20use crate::ValueDecoder;
21use crate::ValueEncoder;
22
23/// Checked type-erased encoding entry point returning caller-owned bytes.
24type EncodeFn = fn(&dyn Any) -> Result<Vec<u8>, ValueCodecExecutionError>;
25/// Type-erased decoding entry point returning a boxed owned value.
26type DecodeFn = fn(&[u8]) -> Result<Box<dyn Any>, ValueCodecExecutionError>;
27
28/// An immutable, safely erased bidirectional bytes codec for one value type.
29///
30/// Each operation creates a fresh default codec; no codec state is retained.
31/// Type identities are process-local and names are diagnostic, not stable IDs.
32///
33/// # Examples
34///
35/// ```
36/// use core::convert::Infallible;
37///
38/// use qubit_codec::ValueBytesCodecDescriptor;
39/// use qubit_codec::ValueDecoder;
40/// use qubit_codec::ValueEncoder;
41///
42/// #[derive(Default)]
43/// struct Bytes;
44/// impl ValueEncoder<Vec<u8>> for Bytes {
45/// type Error = Infallible;
46/// type Output = Vec<u8>;
47/// fn encode(&mut self, value: &Vec<u8>) -> Result<Vec<u8>, Infallible> {
48/// Ok(value.clone())
49/// }
50/// }
51/// impl ValueDecoder<[u8]> for Bytes {
52/// type Error = Infallible;
53/// type Output = Vec<u8>;
54/// fn decode(&mut self, input: &[u8]) -> Result<Vec<u8>, Infallible> {
55/// Ok(input.to_vec())
56/// }
57/// }
58/// let descriptor = ValueBytesCodecDescriptor::of::<Bytes, Vec<u8>>();
59/// let bytes = descriptor.encode(&vec![1_u8, 2]).unwrap();
60/// let decoded = descriptor.decode(&bytes).unwrap();
61/// assert_eq!(*decoded.downcast::<Vec<u8>>().unwrap(), vec![1, 2]);
62/// ```
63#[derive(Clone, Copy)]
64pub struct ValueBytesCodecDescriptor {
65 /// Returns the process-local identity of the concrete codec.
66 codec_type_id: fn() -> TypeId,
67 /// Returns the diagnostic name of the concrete codec.
68 codec_type_name: fn() -> &'static str,
69 /// Returns the process-local identity of the decoded value.
70 value_type_id: fn() -> TypeId,
71 /// Returns the diagnostic name of the decoded value.
72 value_type_name: fn() -> &'static str,
73 /// Checks the erased input type and invokes a fresh default encoder.
74 encode: EncodeFn,
75 /// Invokes a fresh default decoder and boxes its owned output.
76 decode: DecodeFn,
77}
78
79impl ValueBytesCodecDescriptor {
80 /// Creates a descriptor for codec `C` and value type `V`.
81 ///
82 /// Both codec error types must implement `Send + Sync` so execution errors
83 /// can retain their source across thread boundaries. The codec and value
84 /// types themselves need not implement these traits; `V` only needs to be
85 /// `'static` because decoded values are returned through `Any`.
86 ///
87 /// # Type Parameters
88 ///
89 /// - `C`: Default-constructible bytes encoder/decoder with owned errors.
90 /// - `V`: Owned value type accepted and produced by `C`.
91 ///
92 /// # Returns
93 ///
94 /// Returns immutable function metadata without constructing a codec.
95 #[must_use]
96 #[inline]
97 pub const fn of<C, V>() -> Self
98 where
99 C: Default + ValueEncoder<V, Output = Vec<u8>> + ValueDecoder<[u8], Output = V> + 'static,
100 <C as ValueEncoder<V>>::Error: Error + Send + Sync + 'static,
101 <C as ValueDecoder<[u8]>>::Error: Error + Send + Sync + 'static,
102 V: 'static,
103 {
104 Self {
105 codec_type_id: TypeId::of::<C>,
106 codec_type_name: type_name::<C>,
107 value_type_id: TypeId::of::<V>,
108 value_type_name: type_name::<V>,
109 encode: encode::<C, V>,
110 decode: decode::<C, V>,
111 }
112 }
113
114 /// Returns the process-local codec type identity.
115 ///
116 /// # Returns
117 ///
118 /// Returns metadata for the type selected by [`Self::of`], without
119 /// allocation.
120 #[must_use]
121 #[inline]
122 pub fn codec_type_id(&self) -> TypeId {
123 (self.codec_type_id)()
124 }
125
126 /// Returns the diagnostic codec type name.
127 ///
128 /// # Returns
129 ///
130 /// Returns metadata for the type selected by [`Self::of`], without
131 /// allocation.
132 #[must_use]
133 #[inline]
134 pub fn codec_type_name(&self) -> &'static str {
135 (self.codec_type_name)()
136 }
137
138 /// Returns the process-local value type identity.
139 ///
140 /// # Returns
141 ///
142 /// Returns metadata for the type selected by [`Self::of`], without
143 /// allocation.
144 #[must_use]
145 #[inline]
146 pub fn value_type_id(&self) -> TypeId {
147 (self.value_type_id)()
148 }
149
150 /// Returns the diagnostic value type name.
151 ///
152 /// # Returns
153 ///
154 /// Returns metadata for the type selected by [`Self::of`], without
155 /// allocation.
156 #[must_use]
157 #[inline]
158 pub fn value_type_name(&self) -> &'static str {
159 (self.value_type_name)()
160 }
161
162 /// Encodes one erased value after checking its concrete type.
163 ///
164 /// # Parameters
165 ///
166 /// - `value`: Borrowed value whose concrete type must match the descriptor.
167 ///
168 /// # Returns
169 ///
170 /// Returns owned bytes produced by a fresh default codec.
171 ///
172 /// # Errors
173 ///
174 /// Returns a type mismatch or the typed encoder source error.
175 #[inline]
176 pub fn encode(&self, value: &dyn Any) -> Result<Vec<u8>, ValueCodecExecutionError> {
177 (self.encode)(value)
178 }
179
180 /// Decodes one byte slice into a safely erased value of the declared type.
181 ///
182 /// # Parameters
183 ///
184 /// - `input`: Bytes borrowed for one decoding operation.
185 ///
186 /// # Returns
187 ///
188 /// Returns a boxed owned value that can be downcast to the declared type.
189 ///
190 /// # Errors
191 ///
192 /// Returns the typed decoder source error.
193 #[inline]
194 pub fn decode(&self, input: &[u8]) -> Result<Box<dyn Any>, ValueCodecExecutionError> {
195 (self.decode)(input)
196 }
197}
198
199impl Debug for ValueBytesCodecDescriptor {
200 /// Formats descriptor metadata without invoking either codec function.
201 ///
202 /// # Parameters
203 ///
204 /// - `formatter`: Destination receiving the diagnostic type names.
205 ///
206 /// # Returns
207 ///
208 /// Returns success after both names have been formatted.
209 ///
210 /// # Errors
211 ///
212 /// Returns the formatting error if the destination rejects output.
213 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
214 formatter
215 .debug_struct("ValueBytesCodecDescriptor")
216 .field("codec_type_name", &self.codec_type_name())
217 .field("value_type_name", &self.value_type_name())
218 .finish_non_exhaustive()
219 }
220}
221
222/// Downcasts and encodes one typed value using a fresh default codec.
223///
224/// # Type Parameters
225///
226/// - `C`: Default codec with matching bytes encode/decode contracts.
227/// - `V`: Concrete owned value type required by the descriptor.
228///
229/// # Parameters
230///
231/// - `value`: Erased input borrowed until serialization completes.
232///
233/// # Returns
234///
235/// Returns caller-owned encoded bytes.
236///
237/// # Errors
238///
239/// Returns `TypeMismatch` for a different concrete type, or `EncodeFailed`
240/// retaining the codec source error if encoding fails.
241fn encode<C, V>(value: &dyn Any) -> Result<Vec<u8>, ValueCodecExecutionError>
242where
243 C: Default + ValueEncoder<V, Output = Vec<u8>> + ValueDecoder<[u8], Output = V> + 'static,
244 <C as ValueEncoder<V>>::Error: Error + Send + Sync + 'static,
245 <C as ValueDecoder<[u8]>>::Error: Error + Send + Sync + 'static,
246 V: 'static,
247{
248 let value = value
249 .downcast_ref::<V>()
250 .ok_or_else(|| ValueCodecExecutionError::TypeMismatch {
251 expected_type: type_name::<V>(),
252 actual_type: value.type_id(),
253 })?;
254 C::default()
255 .encode(value)
256 .map_err(|source| ValueCodecExecutionError::EncodeFailed {
257 codec_type: type_name::<C>(),
258 source: Box::new(source),
259 })
260}
261
262/// Decodes one byte slice and erases the typed output safely.
263///
264/// # Type Parameters
265///
266/// - `C`: Default codec with matching bytes encode/decode contracts.
267/// - `V`: Concrete owned output type placed in the returned box.
268///
269/// # Parameters
270///
271/// - `input`: Borrowed bytes passed to a fresh default decoder.
272///
273/// # Returns
274///
275/// Returns a newly boxed decoded value with no borrow of the input.
276///
277/// # Errors
278///
279/// Returns `DecodeFailed` retaining the codec source error on decode failure.
280fn decode<C, V>(input: &[u8]) -> Result<Box<dyn Any>, ValueCodecExecutionError>
281where
282 C: Default + ValueEncoder<V, Output = Vec<u8>> + ValueDecoder<[u8], Output = V> + 'static,
283 <C as ValueEncoder<V>>::Error: Error + Send + Sync + 'static,
284 <C as ValueDecoder<[u8]>>::Error: Error + Send + Sync + 'static,
285 V: 'static,
286{
287 C::default()
288 .decode(input)
289 .map(|value| Box::new(value) as Box<dyn Any>)
290 .map_err(|source| ValueCodecExecutionError::DecodeFailed {
291 codec_type: type_name::<C>(),
292 source: Box::new(source),
293 })
294}