Skip to main content

numcodecs/
erased.rs

1use std::{any::Any, error::Error, fmt};
2
3use schemars::{JsonSchema, Schema, SchemaGenerator};
4use serde::{Deserializer, Serialize, Serializer};
5
6use crate::{AnyArray, AnyArrayView, AnyArrayViewMut, AnyCowArray, Codec, DynCodec, DynCodecType};
7
8/// Type-erased [`Error`] type.
9pub struct ErasedError {
10    error: Box<dyn 'static + Error + Send + Sync>,
11}
12
13impl ErasedError {
14    /// Erase the type information of the concrete `err`or.
15    pub fn new<T: 'static + Error + Send + Sync>(err: T) -> Self {
16        Self {
17            error: Box::new(err),
18        }
19    }
20}
21
22impl fmt::Debug for ErasedError {
23    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
24        fmt::Debug::fmt(&self.error, fmt)
25    }
26}
27
28impl fmt::Display for ErasedError {
29    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
30        fmt::Display::fmt(&self.error, fmt)
31    }
32}
33
34impl Error for ErasedError {
35    fn source(&self) -> Option<&(dyn Error + 'static)> {
36        self.error.source()
37    }
38}
39
40/// Type-erased dynamically typed compression codec.
41pub struct ErasedDynCodec {
42    codec: Box<dyn ErasedDynCodecDispatch>,
43}
44
45impl ErasedDynCodec {
46    /// Erase the type information of the concrete `codec`.
47    pub fn new<T: DynCodec>(codec: T) -> Self {
48        Self {
49            codec: Box::new(codec),
50        }
51    }
52
53    /// Try to downcast into a concretely-typed codec.
54    ///
55    /// # Errors
56    ///
57    /// Returns `self` if the type-erased codec is not of the concrete type.
58    pub fn downcast<T: DynCodec>(self) -> Result<T, Self> {
59        if self.codec.erased_as_any().is::<T>() {
60            let raw = Box::into_raw(self.codec);
61            #[expect(unsafe_code)]
62            // SAFETY: we have checked that self.codec is of type T
63            let codec = unsafe { Box::from_raw(raw.cast::<T>()) };
64            Ok(*codec)
65        } else {
66            Err(self)
67        }
68    }
69
70    /// Try to downcast to a concretely-typed codec reference.
71    #[must_use]
72    pub fn downcast_ref<T: DynCodec>(&self) -> Option<&T> {
73        self.codec.erased_as_any().downcast_ref()
74    }
75
76    /// Try to downcast to a concretely-typed mutable codec reference.
77    #[must_use]
78    pub fn downcast_mut<T: DynCodec>(&mut self) -> Option<&mut T> {
79        self.codec.erased_as_any_mut().downcast_mut()
80    }
81
82    /// Generate the schema for any codec config.
83    pub fn codec_config_schema(generator: &mut SchemaGenerator) -> Schema {
84        #[derive(JsonSchema)]
85        #[schemars(extend("additionalProperties" = {"type": "object"}))]
86        /// The configuration for a codec.
87        struct Codec {
88            /// The `codec_id` of the codec, which is looked up in the global
89            /// registry.
90            #[expect(dead_code)]
91            id: String,
92        }
93
94        Codec::json_schema(generator)
95    }
96}
97
98impl Clone for ErasedDynCodec {
99    fn clone(&self) -> Self {
100        Self {
101            codec: self.codec.erased_clone(),
102        }
103    }
104}
105
106impl Codec for ErasedDynCodec {
107    type Error = ErasedError;
108
109    fn encode(&self, data: AnyCowArray) -> Result<AnyArray, Self::Error> {
110        self.codec.erased_encode(data)
111    }
112
113    fn decode(&self, encoded: AnyCowArray) -> Result<AnyArray, Self::Error> {
114        self.codec.erased_decode(encoded)
115    }
116
117    fn decode_into(
118        &self,
119        encoded: AnyArrayView,
120        decoded: AnyArrayViewMut,
121    ) -> Result<(), Self::Error> {
122        self.codec.erased_decode_into(encoded, decoded)
123    }
124}
125
126impl DynCodec for ErasedDynCodec {
127    type Type = ErasedDynCodecType;
128
129    fn ty(&self) -> Self::Type {
130        ErasedDynCodecType {
131            ty: self.codec.erased_ty(),
132        }
133    }
134
135    fn get_config<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
136        erased_serde::serialize(self.codec.erased_as_serialize(), serializer)
137    }
138}
139
140/// Type-erased dynamically typed compression codec type.
141pub struct ErasedDynCodecType {
142    ty: Box<dyn ErasedDynCodecTypeDispatch>,
143}
144
145impl ErasedDynCodecType {
146    /// Erase the type information of the concrete codec `ty`pe.
147    pub fn new<T: DynCodecType>(ty: T) -> Self {
148        Self { ty: Box::new(ty) }
149    }
150
151    /// Try to downcast into a concretely-typed codec type.
152    ///
153    /// # Errors
154    ///
155    /// Returns `self` if the type-erased codec type is not of the concrete
156    /// type.
157    pub fn downcast<T: DynCodecType>(self) -> Result<T, Self> {
158        if self.ty.erased_as_any().is::<T>() {
159            let raw = Box::into_raw(self.ty);
160            #[expect(unsafe_code)]
161            // SAFETY: we have checked that self.ty is of type T
162            let ty = unsafe { Box::from_raw(raw.cast::<T>()) };
163            Ok(*ty)
164        } else {
165            Err(self)
166        }
167    }
168
169    /// Try to downcast to a concretely-typed codec type reference.
170    #[must_use]
171    pub fn downcast_ref<T: DynCodecType>(&self) -> Option<&T> {
172        self.ty.erased_as_any().downcast_ref()
173    }
174
175    /// Try to downcast to a concretely-typed mutable codec type reference.
176    #[must_use]
177    pub fn downcast_mut<T: DynCodecType>(&mut self) -> Option<&mut T> {
178        self.ty.erased_as_any_mut().downcast_mut()
179    }
180}
181
182impl DynCodecType for ErasedDynCodecType {
183    type Codec = ErasedDynCodec;
184
185    fn codec_id(&self) -> &str {
186        self.ty.erased_codec_id()
187    }
188
189    fn codec_config_schema(&self) -> Schema {
190        self.ty.erased_codec_config_schema()
191    }
192
193    fn codec_from_config<'de, D: Deserializer<'de>>(
194        &self,
195        config: D,
196    ) -> Result<Self::Codec, D::Error> {
197        match self
198            .ty
199            .erased_codec_from_config(&mut <dyn erased_serde::Deserializer>::erase(config))
200        {
201            Ok(codec) => Ok(ErasedDynCodec { codec }),
202            Err(err) => Err(serde::de::Error::custom(err)), // TODO: improve
203        }
204    }
205}
206
207trait ErasedDynCodecDispatch: 'static + Send + Sync {
208    fn erased_encode(&self, data: AnyCowArray) -> Result<AnyArray, ErasedError>;
209    fn erased_decode(&self, encoded: AnyCowArray) -> Result<AnyArray, ErasedError>;
210    fn erased_decode_into(
211        &self,
212        encoded: AnyArrayView,
213        decoded: AnyArrayViewMut,
214    ) -> Result<(), ErasedError>;
215
216    fn erased_clone(&self) -> Box<dyn ErasedDynCodecDispatch>;
217
218    fn erased_ty(&self) -> Box<dyn ErasedDynCodecTypeDispatch>;
219
220    fn erased_as_any(&self) -> &dyn Any;
221    fn erased_as_any_mut(&mut self) -> &mut dyn Any;
222
223    fn erased_as_serialize(&self) -> &dyn erased_serde::Serialize;
224}
225
226trait ErasedDynCodecTypeDispatch: 'static + Send + Sync {
227    fn erased_codec_id(&self) -> &str;
228    fn erased_codec_config_schema(&self) -> Schema;
229    fn erased_codec_from_config(
230        &self,
231        config: &mut dyn erased_serde::Deserializer,
232    ) -> Result<Box<dyn ErasedDynCodecDispatch>, erased_serde::Error>;
233
234    fn erased_as_any(&self) -> &dyn Any;
235    fn erased_as_any_mut(&mut self) -> &mut dyn Any;
236}
237
238impl<T: DynCodec> ErasedDynCodecDispatch for T {
239    fn erased_encode(&self, data: AnyCowArray) -> Result<AnyArray, ErasedError> {
240        Codec::encode(self, data).map_err(ErasedError::new)
241    }
242
243    fn erased_decode(&self, encoded: AnyCowArray) -> Result<AnyArray, ErasedError> {
244        Codec::decode(self, encoded).map_err(ErasedError::new)
245    }
246
247    fn erased_decode_into(
248        &self,
249        encoded: AnyArrayView,
250        decoded: AnyArrayViewMut,
251    ) -> Result<(), ErasedError> {
252        Codec::decode_into(self, encoded, decoded).map_err(ErasedError::new)
253    }
254
255    fn erased_clone(&self) -> Box<dyn ErasedDynCodecDispatch> {
256        Box::new(Clone::clone(self))
257    }
258
259    fn erased_ty(&self) -> Box<dyn ErasedDynCodecTypeDispatch> {
260        Box::new(DynCodec::ty(self))
261    }
262
263    fn erased_as_any(&self) -> &dyn Any {
264        self
265    }
266
267    fn erased_as_any_mut(&mut self) -> &mut dyn Any {
268        self
269    }
270
271    fn erased_as_serialize(&self) -> &dyn erased_serde::Serialize {
272        #[repr(transparent)]
273        struct SerializeDynCodec<T: DynCodec>(T);
274
275        impl<T: DynCodec> Serialize for SerializeDynCodec<T> {
276            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
277                DynCodec::get_config(&self.0, serializer)
278            }
279        }
280
281        #[expect(unsafe_code)]
282        // SAFETY: SerializeDynCodec is a transparent newtype around Self
283        unsafe {
284            &*std::ptr::from_ref(self).cast::<SerializeDynCodec<Self>>()
285        }
286    }
287}
288
289impl<T: DynCodecType> ErasedDynCodecTypeDispatch for T {
290    fn erased_codec_id(&self) -> &str {
291        DynCodecType::codec_id(self)
292    }
293
294    fn erased_codec_config_schema(&self) -> Schema {
295        DynCodecType::codec_config_schema(self)
296    }
297
298    fn erased_codec_from_config(
299        &self,
300        config: &mut dyn erased_serde::Deserializer,
301    ) -> Result<Box<dyn ErasedDynCodecDispatch>, erased_serde::Error> {
302        match DynCodecType::codec_from_config(self, config) {
303            Ok(codec) => Ok(Box::new(codec)),
304            Err(err) => Err(err),
305        }
306    }
307
308    fn erased_as_any(&self) -> &dyn Any {
309        self
310    }
311
312    fn erased_as_any_mut(&mut self) -> &mut dyn Any {
313        self
314    }
315}