Skip to main content

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    /// # Type Parameters
83    ///
84    /// - `C`: Default-constructible bytes encoder/decoder with owned errors.
85    /// - `V`: Owned value type accepted and produced by `C`.
86    ///
87    /// # Returns
88    ///
89    /// Returns immutable function metadata without constructing a codec.
90    #[must_use]
91    #[inline]
92    pub const fn of<C, V>() -> Self
93    where
94        C: Default + ValueEncoder<V, Output = Vec<u8>> + ValueDecoder<[u8], Output = V> + 'static,
95        <C as ValueEncoder<V>>::Error: Error + 'static,
96        <C as ValueDecoder<[u8]>>::Error: Error + 'static,
97        V: 'static,
98    {
99        Self {
100            codec_type_id: TypeId::of::<C>,
101            codec_type_name: type_name::<C>,
102            value_type_id: TypeId::of::<V>,
103            value_type_name: type_name::<V>,
104            encode: encode::<C, V>,
105            decode: decode::<C, V>,
106        }
107    }
108
109    /// Returns the process-local codec type identity.
110    ///
111    /// # Returns
112    ///
113    /// Returns metadata for the type selected by [`Self::of`], without
114    /// allocation.
115    #[must_use]
116    #[inline]
117    pub fn codec_type_id(&self) -> TypeId {
118        (self.codec_type_id)()
119    }
120
121    /// Returns the diagnostic codec type name.
122    ///
123    /// # Returns
124    ///
125    /// Returns metadata for the type selected by [`Self::of`], without
126    /// allocation.
127    #[must_use]
128    #[inline]
129    pub fn codec_type_name(&self) -> &'static str {
130        (self.codec_type_name)()
131    }
132
133    /// Returns the process-local value type identity.
134    ///
135    /// # Returns
136    ///
137    /// Returns metadata for the type selected by [`Self::of`], without
138    /// allocation.
139    #[must_use]
140    #[inline]
141    pub fn value_type_id(&self) -> TypeId {
142        (self.value_type_id)()
143    }
144
145    /// Returns the diagnostic value type name.
146    ///
147    /// # Returns
148    ///
149    /// Returns metadata for the type selected by [`Self::of`], without
150    /// allocation.
151    #[must_use]
152    #[inline]
153    pub fn value_type_name(&self) -> &'static str {
154        (self.value_type_name)()
155    }
156
157    /// Encodes one erased value after checking its concrete type.
158    ///
159    /// # Parameters
160    ///
161    /// - `value`: Borrowed value whose concrete type must match the descriptor.
162    ///
163    /// # Returns
164    ///
165    /// Returns owned bytes produced by a fresh default codec.
166    ///
167    /// # Errors
168    ///
169    /// Returns a type mismatch or the typed encoder source error.
170    #[inline]
171    pub fn encode(&self, value: &dyn Any) -> Result<Vec<u8>, ValueCodecExecutionError> {
172        (self.encode)(value)
173    }
174
175    /// Decodes one byte slice into a safely erased value of the declared type.
176    ///
177    /// # Parameters
178    ///
179    /// - `input`: Bytes borrowed for one decoding operation.
180    ///
181    /// # Returns
182    ///
183    /// Returns a boxed owned value that can be downcast to the declared type.
184    ///
185    /// # Errors
186    ///
187    /// Returns the typed decoder source error.
188    #[inline]
189    pub fn decode(&self, input: &[u8]) -> Result<Box<dyn Any>, ValueCodecExecutionError> {
190        (self.decode)(input)
191    }
192}
193
194impl Debug for ValueBytesCodecDescriptor {
195    /// Formats descriptor metadata without invoking either codec function.
196    ///
197    /// # Parameters
198    ///
199    /// - `formatter`: Destination receiving the diagnostic type names.
200    ///
201    /// # Returns
202    ///
203    /// Returns success after both names have been formatted.
204    ///
205    /// # Errors
206    ///
207    /// Returns the formatting error if the destination rejects output.
208    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
209        formatter
210            .debug_struct("ValueBytesCodecDescriptor")
211            .field("codec_type_name", &self.codec_type_name())
212            .field("value_type_name", &self.value_type_name())
213            .finish_non_exhaustive()
214    }
215}
216
217/// Downcasts and encodes one typed value using a fresh default codec.
218///
219/// # Type Parameters
220///
221/// - `C`: Default codec with matching bytes encode/decode contracts.
222/// - `V`: Concrete owned value type required by the descriptor.
223///
224/// # Parameters
225///
226/// - `value`: Erased input borrowed until serialization completes.
227///
228/// # Returns
229///
230/// Returns caller-owned encoded bytes.
231///
232/// # Errors
233///
234/// Returns `TypeMismatch` for a different concrete type, or `EncodeFailed`
235/// retaining the codec source error if encoding fails.
236fn encode<C, V>(value: &dyn Any) -> Result<Vec<u8>, ValueCodecExecutionError>
237where
238    C: Default + ValueEncoder<V, Output = Vec<u8>> + ValueDecoder<[u8], Output = V> + 'static,
239    <C as ValueEncoder<V>>::Error: Error + 'static,
240    <C as ValueDecoder<[u8]>>::Error: Error + 'static,
241    V: 'static,
242{
243    let value = value
244        .downcast_ref::<V>()
245        .ok_or_else(|| ValueCodecExecutionError::TypeMismatch {
246            expected_type: type_name::<V>(),
247            actual_type: value.type_id(),
248        })?;
249    C::default()
250        .encode(value)
251        .map_err(|source| ValueCodecExecutionError::EncodeFailed {
252            codec_type: type_name::<C>(),
253            source: Box::new(source),
254        })
255}
256
257/// Decodes one byte slice and erases the typed output safely.
258///
259/// # Type Parameters
260///
261/// - `C`: Default codec with matching bytes encode/decode contracts.
262/// - `V`: Concrete owned output type placed in the returned box.
263///
264/// # Parameters
265///
266/// - `input`: Borrowed bytes passed to a fresh default decoder.
267///
268/// # Returns
269///
270/// Returns a newly boxed decoded value with no borrow of the input.
271///
272/// # Errors
273///
274/// Returns `DecodeFailed` retaining the codec source error on decode failure.
275fn decode<C, V>(input: &[u8]) -> Result<Box<dyn Any>, ValueCodecExecutionError>
276where
277    C: Default + ValueEncoder<V, Output = Vec<u8>> + ValueDecoder<[u8], Output = V> + 'static,
278    <C as ValueEncoder<V>>::Error: Error + 'static,
279    <C as ValueDecoder<[u8]>>::Error: Error + 'static,
280    V: 'static,
281{
282    C::default()
283        .decode(input)
284        .map(|value| Box::new(value) as Box<dyn Any>)
285        .map_err(|source| ValueCodecExecutionError::DecodeFailed {
286            codec_type: type_name::<C>(),
287            source: Box::new(source),
288        })
289}