Skip to main content

qubit_codec/value/
value_codec_descriptor.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5// =============================================================================
6
7//! Safe type-erased value-codec descriptors.
8
9use std::any::Any;
10use std::any::TypeId;
11
12use crate::ValueCodecExecutionError;
13use crate::ValueDecoder;
14use crate::ValueEncoder;
15
16type EncodeFn = fn(&dyn Any) -> Result<String, ValueCodecExecutionError>;
17type DecodeFn = fn(&str) -> Result<Box<dyn Any>, ValueCodecExecutionError>;
18
19/// An immutable, safely erased bidirectional string codec for one value type.
20#[derive(Clone, Copy)]
21pub struct ValueCodecDescriptor {
22    codec_type_id: fn() -> TypeId,
23    codec_type_name: fn() -> &'static str,
24    value_type_id: fn() -> TypeId,
25    value_type_name: fn() -> &'static str,
26    encode: EncodeFn,
27    decode: DecodeFn,
28}
29
30impl ValueCodecDescriptor {
31    /// Creates a descriptor for codec `C` and value type `V`.
32    #[must_use]
33    pub const fn of<C, V>() -> Self
34    where
35        C: Default + ValueEncoder<V, Output = String> + ValueDecoder<str, Output = V> + 'static,
36        <C as ValueEncoder<V>>::Error: std::error::Error + 'static,
37        <C as ValueDecoder<str>>::Error: std::error::Error + 'static,
38        V: 'static,
39    {
40        Self {
41            codec_type_id: TypeId::of::<C>,
42            codec_type_name: core::any::type_name::<C>,
43            value_type_id: TypeId::of::<V>,
44            value_type_name: core::any::type_name::<V>,
45            encode: encode::<C, V>,
46            decode: decode::<C, V>,
47        }
48    }
49
50    /// Returns the process-local codec type identity.
51    #[must_use]
52    pub fn codec_type_id(&self) -> TypeId {
53        (self.codec_type_id)()
54    }
55
56    /// Returns the diagnostic codec type name.
57    #[must_use]
58    pub fn codec_type_name(&self) -> &'static str {
59        (self.codec_type_name)()
60    }
61
62    /// Returns the process-local value type identity.
63    #[must_use]
64    pub fn value_type_id(&self) -> TypeId {
65        (self.value_type_id)()
66    }
67
68    /// Returns the diagnostic value type name.
69    #[must_use]
70    pub fn value_type_name(&self) -> &'static str {
71        (self.value_type_name)()
72    }
73
74    /// Encodes one erased value after checking its concrete type.
75    ///
76    /// # Errors
77    ///
78    /// Returns a type mismatch or the typed encoder source error.
79    pub fn encode(&self, value: &dyn Any) -> Result<String, ValueCodecExecutionError> {
80        (self.encode)(value)
81    }
82
83    /// Decodes one string into a safely erased value of the declared type.
84    ///
85    /// # Errors
86    ///
87    /// Returns the typed decoder source error.
88    pub fn decode(&self, input: &str) -> Result<Box<dyn Any>, ValueCodecExecutionError> {
89        (self.decode)(input)
90    }
91}
92
93impl core::fmt::Debug for ValueCodecDescriptor {
94    /// Formats descriptor metadata without invoking either codec function.
95    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96        formatter
97            .debug_struct("ValueCodecDescriptor")
98            .field("codec_type_name", &self.codec_type_name())
99            .field("value_type_name", &self.value_type_name())
100            .finish_non_exhaustive()
101    }
102}
103
104/// Downcasts and encodes one typed value.
105fn encode<C, V>(value: &dyn Any) -> Result<String, ValueCodecExecutionError>
106where
107    C: Default + ValueEncoder<V, Output = String> + ValueDecoder<str, Output = V> + 'static,
108    <C as ValueEncoder<V>>::Error: std::error::Error + 'static,
109    <C as ValueDecoder<str>>::Error: std::error::Error + 'static,
110    V: 'static,
111{
112    let value = value
113        .downcast_ref::<V>()
114        .ok_or_else(|| ValueCodecExecutionError::TypeMismatch {
115            expected_type: core::any::type_name::<V>(),
116            actual_type: value.type_id(),
117        })?;
118    C::default()
119        .encode(value)
120        .map_err(|source| ValueCodecExecutionError::EncodeFailed {
121            codec_type: core::any::type_name::<C>(),
122            source: Box::new(source),
123        })
124}
125
126/// Decodes one string and erases the typed output safely.
127fn decode<C, V>(input: &str) -> Result<Box<dyn Any>, ValueCodecExecutionError>
128where
129    C: Default + ValueEncoder<V, Output = String> + ValueDecoder<str, Output = V> + 'static,
130    <C as ValueEncoder<V>>::Error: std::error::Error + 'static,
131    <C as ValueDecoder<str>>::Error: std::error::Error + 'static,
132    V: 'static,
133{
134    C::default()
135        .decode(input)
136        .map(|value| Box::new(value) as Box<dyn Any>)
137        .map_err(|source| ValueCodecExecutionError::DecodeFailed {
138            codec_type: core::any::type_name::<C>(),
139            source: Box::new(source),
140        })
141}