celers_protocol/
serializer.rs

1//! Pluggable serialization framework
2//!
3//! This module provides a unified [`Serializer`] trait for different serialization
4//! formats (JSON, MessagePack, etc.) with automatic content type detection.
5//!
6//! # Example
7//!
8//! ```
9//! use celers_protocol::serializer::{Serializer, JsonSerializer};
10//!
11//! let serializer = JsonSerializer;
12//! let data = vec![1, 2, 3];
13//! let bytes = serializer.serialize(&data).unwrap();
14//! let decoded: Vec<i32> = serializer.deserialize(&bytes).unwrap();
15//! assert_eq!(data, decoded);
16//! ```
17
18use crate::{ContentEncoding, ContentType};
19use serde::{de::DeserializeOwned, Serialize};
20use std::fmt;
21
22/// Error type for serialization operations
23#[derive(Debug)]
24pub enum SerializerError {
25    /// Serialization failed
26    Serialize(String),
27    /// Deserialization failed
28    Deserialize(String),
29    /// Unsupported content type
30    UnsupportedContentType(String),
31    /// Compression error
32    Compression(String),
33}
34
35impl fmt::Display for SerializerError {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            SerializerError::Serialize(msg) => write!(f, "Serialization error: {}", msg),
39            SerializerError::Deserialize(msg) => write!(f, "Deserialization error: {}", msg),
40            SerializerError::UnsupportedContentType(ct) => {
41                write!(f, "Unsupported content type: {}", ct)
42            }
43            SerializerError::Compression(msg) => write!(f, "Compression error: {}", msg),
44        }
45    }
46}
47
48impl std::error::Error for SerializerError {}
49
50/// Result type for serialization operations
51pub type SerializerResult<T> = Result<T, SerializerError>;
52
53/// Trait for pluggable serialization formats
54///
55/// Implementors of this trait can serialize and deserialize data to/from bytes,
56/// providing their content type and encoding information.
57pub trait Serializer: Send + Sync {
58    /// Returns the content type for this serializer
59    fn content_type(&self) -> ContentType;
60
61    /// Returns the content encoding for this serializer
62    fn content_encoding(&self) -> ContentEncoding;
63
64    /// Serialize a value to bytes
65    fn serialize<T: Serialize>(&self, value: &T) -> SerializerResult<Vec<u8>>;
66
67    /// Deserialize bytes to a value
68    fn deserialize<T: DeserializeOwned>(&self, bytes: &[u8]) -> SerializerResult<T>;
69
70    /// Returns the name of this serializer
71    fn name(&self) -> &'static str;
72}
73
74/// JSON serializer implementation
75#[derive(Debug, Clone, Copy, Default)]
76pub struct JsonSerializer;
77
78impl Serializer for JsonSerializer {
79    #[inline]
80    fn content_type(&self) -> ContentType {
81        ContentType::Json
82    }
83
84    #[inline]
85    fn content_encoding(&self) -> ContentEncoding {
86        ContentEncoding::Utf8
87    }
88
89    fn serialize<T: Serialize>(&self, value: &T) -> SerializerResult<Vec<u8>> {
90        serde_json::to_vec(value).map_err(|e| SerializerError::Serialize(e.to_string()))
91    }
92
93    fn deserialize<T: DeserializeOwned>(&self, bytes: &[u8]) -> SerializerResult<T> {
94        serde_json::from_slice(bytes).map_err(|e| SerializerError::Deserialize(e.to_string()))
95    }
96
97    #[inline]
98    fn name(&self) -> &'static str {
99        "json"
100    }
101}
102
103/// MessagePack serializer implementation
104#[cfg(feature = "msgpack")]
105#[derive(Debug, Clone, Copy, Default)]
106pub struct MessagePackSerializer;
107
108#[cfg(feature = "msgpack")]
109impl Serializer for MessagePackSerializer {
110    #[inline]
111    fn content_type(&self) -> ContentType {
112        ContentType::MessagePack
113    }
114
115    #[inline]
116    fn content_encoding(&self) -> ContentEncoding {
117        ContentEncoding::Binary
118    }
119
120    fn serialize<T: Serialize>(&self, value: &T) -> SerializerResult<Vec<u8>> {
121        rmp_serde::to_vec(value).map_err(|e| SerializerError::Serialize(e.to_string()))
122    }
123
124    fn deserialize<T: DeserializeOwned>(&self, bytes: &[u8]) -> SerializerResult<T> {
125        rmp_serde::from_slice(bytes).map_err(|e| SerializerError::Deserialize(e.to_string()))
126    }
127
128    #[inline]
129    fn name(&self) -> &'static str {
130        "msgpack"
131    }
132}
133
134/// YAML serializer implementation
135#[cfg(feature = "yaml")]
136#[derive(Debug, Clone, Copy, Default)]
137pub struct YamlSerializer;
138
139#[cfg(feature = "yaml")]
140impl Serializer for YamlSerializer {
141    #[inline]
142    fn content_type(&self) -> ContentType {
143        ContentType::Custom("application/x-yaml".to_string())
144    }
145
146    #[inline]
147    fn content_encoding(&self) -> ContentEncoding {
148        ContentEncoding::Utf8
149    }
150
151    fn serialize<T: Serialize>(&self, value: &T) -> SerializerResult<Vec<u8>> {
152        serde_yaml::to_string(value)
153            .map(|s| s.into_bytes())
154            .map_err(|e| SerializerError::Serialize(e.to_string()))
155    }
156
157    fn deserialize<T: DeserializeOwned>(&self, bytes: &[u8]) -> SerializerResult<T> {
158        serde_yaml::from_slice(bytes).map_err(|e| SerializerError::Deserialize(e.to_string()))
159    }
160
161    #[inline]
162    fn name(&self) -> &'static str {
163        "yaml"
164    }
165}
166
167/// BSON serializer implementation
168#[cfg(feature = "bson-format")]
169#[derive(Debug, Clone, Copy, Default)]
170pub struct BsonSerializer;
171
172#[cfg(feature = "bson-format")]
173impl Serializer for BsonSerializer {
174    #[inline]
175    fn content_type(&self) -> ContentType {
176        ContentType::Custom("application/bson".to_string())
177    }
178
179    #[inline]
180    fn content_encoding(&self) -> ContentEncoding {
181        ContentEncoding::Binary
182    }
183
184    fn serialize<T: Serialize>(&self, value: &T) -> SerializerResult<Vec<u8>> {
185        bson::serialize_to_vec(value).map_err(|e| SerializerError::Serialize(e.to_string()))
186    }
187
188    fn deserialize<T: DeserializeOwned>(&self, bytes: &[u8]) -> SerializerResult<T> {
189        bson::deserialize_from_slice(bytes).map_err(|e| SerializerError::Deserialize(e.to_string()))
190    }
191
192    #[inline]
193    fn name(&self) -> &'static str {
194        "bson"
195    }
196}
197
198/// Protobuf serializer implementation
199///
200/// Note: This is a generic Protobuf serializer using prost.
201/// For proper Protobuf support, you should use prost code generation
202/// to create message-specific serializers.
203#[cfg(feature = "protobuf")]
204#[derive(Debug, Clone, Copy, Default)]
205pub struct ProtobufSerializer;
206
207#[cfg(feature = "protobuf")]
208impl ProtobufSerializer {
209    /// Serialize a prost::Message to bytes
210    pub fn serialize_message<T: prost::Message>(&self, value: &T) -> SerializerResult<Vec<u8>> {
211        let mut buf = Vec::new();
212        value
213            .encode(&mut buf)
214            .map_err(|e| SerializerError::Serialize(e.to_string()))?;
215        Ok(buf)
216    }
217
218    /// Deserialize bytes to a prost::Message
219    pub fn deserialize_message<T: prost::Message + Default>(
220        &self,
221        bytes: &[u8],
222    ) -> SerializerResult<T> {
223        T::decode(bytes).map_err(|e| SerializerError::Deserialize(e.to_string()))
224    }
225
226    /// Get content type for Protobuf
227    #[inline]
228    pub fn content_type(&self) -> ContentType {
229        ContentType::Custom("application/protobuf".to_string())
230    }
231
232    /// Get content encoding for Protobuf
233    #[inline]
234    pub fn content_encoding(&self) -> ContentEncoding {
235        ContentEncoding::Binary
236    }
237
238    /// Get the name
239    #[inline]
240    pub fn name(&self) -> &'static str {
241        "protobuf"
242    }
243}
244
245/// Serializer type enum for dynamic dispatch without dyn trait issues
246#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
247pub enum SerializerType {
248    /// JSON serializer
249    #[default]
250    Json,
251    /// MessagePack serializer
252    #[cfg(feature = "msgpack")]
253    MessagePack,
254    /// YAML serializer
255    #[cfg(feature = "yaml")]
256    Yaml,
257    /// BSON serializer
258    #[cfg(feature = "bson-format")]
259    Bson,
260}
261
262impl SerializerType {
263    /// Get a serializer type by content type string
264    pub fn from_content_type(content_type: &str) -> SerializerResult<Self> {
265        match content_type {
266            "application/json" => Ok(SerializerType::Json),
267            #[cfg(feature = "msgpack")]
268            "application/x-msgpack" => Ok(SerializerType::MessagePack),
269            #[cfg(feature = "yaml")]
270            "application/x-yaml" | "application/yaml" | "text/yaml" => Ok(SerializerType::Yaml),
271            #[cfg(feature = "bson-format")]
272            "application/bson" => Ok(SerializerType::Bson),
273            _ => Err(SerializerError::UnsupportedContentType(
274                content_type.to_string(),
275            )),
276        }
277    }
278
279    /// Serialize a value to bytes
280    pub fn serialize<T: Serialize>(&self, value: &T) -> SerializerResult<Vec<u8>> {
281        match self {
282            SerializerType::Json => JsonSerializer.serialize(value),
283            #[cfg(feature = "msgpack")]
284            SerializerType::MessagePack => MessagePackSerializer.serialize(value),
285            #[cfg(feature = "yaml")]
286            SerializerType::Yaml => YamlSerializer.serialize(value),
287            #[cfg(feature = "bson-format")]
288            SerializerType::Bson => BsonSerializer.serialize(value),
289        }
290    }
291
292    /// Deserialize bytes to a value
293    pub fn deserialize<T: DeserializeOwned>(&self, bytes: &[u8]) -> SerializerResult<T> {
294        match self {
295            SerializerType::Json => JsonSerializer.deserialize(bytes),
296            #[cfg(feature = "msgpack")]
297            SerializerType::MessagePack => MessagePackSerializer.deserialize(bytes),
298            #[cfg(feature = "yaml")]
299            SerializerType::Yaml => YamlSerializer.deserialize(bytes),
300            #[cfg(feature = "bson-format")]
301            SerializerType::Bson => BsonSerializer.deserialize(bytes),
302        }
303    }
304
305    /// Get the content type
306    #[inline]
307    pub fn content_type(&self) -> ContentType {
308        match self {
309            SerializerType::Json => JsonSerializer.content_type(),
310            #[cfg(feature = "msgpack")]
311            SerializerType::MessagePack => MessagePackSerializer.content_type(),
312            #[cfg(feature = "yaml")]
313            SerializerType::Yaml => YamlSerializer.content_type(),
314            #[cfg(feature = "bson-format")]
315            SerializerType::Bson => BsonSerializer.content_type(),
316        }
317    }
318
319    /// Get the content encoding
320    #[inline]
321    pub fn content_encoding(&self) -> ContentEncoding {
322        match self {
323            SerializerType::Json => JsonSerializer.content_encoding(),
324            #[cfg(feature = "msgpack")]
325            SerializerType::MessagePack => MessagePackSerializer.content_encoding(),
326            #[cfg(feature = "yaml")]
327            SerializerType::Yaml => YamlSerializer.content_encoding(),
328            #[cfg(feature = "bson-format")]
329            SerializerType::Bson => BsonSerializer.content_encoding(),
330        }
331    }
332
333    /// Get the name
334    #[inline]
335    pub fn name(&self) -> &'static str {
336        match self {
337            SerializerType::Json => JsonSerializer.name(),
338            #[cfg(feature = "msgpack")]
339            SerializerType::MessagePack => MessagePackSerializer.name(),
340            #[cfg(feature = "yaml")]
341            SerializerType::Yaml => YamlSerializer.name(),
342            #[cfg(feature = "bson-format")]
343            SerializerType::Bson => BsonSerializer.name(),
344        }
345    }
346}
347
348impl fmt::Display for SerializerType {
349    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350        write!(f, "{}", self.name())
351    }
352}
353
354impl TryFrom<&str> for SerializerType {
355    type Error = SerializerError;
356
357    fn try_from(content_type: &str) -> Result<Self, Self::Error> {
358        Self::from_content_type(content_type)
359    }
360}
361
362/// Get a serializer type by content type string
363///
364/// Returns the appropriate serializer type for the given content type string,
365/// or an error if the content type is not supported.
366///
367/// # Supported Content Types
368///
369/// - `application/json` - JSON serializer
370/// - `application/x-msgpack` - MessagePack serializer (requires `msgpack` feature)
371/// - `application/x-yaml` - YAML serializer (requires `yaml` feature)
372/// - `application/bson` - BSON serializer (requires `bson-format` feature)
373pub fn get_serializer(content_type: &str) -> SerializerResult<SerializerType> {
374    SerializerType::from_content_type(content_type)
375}
376
377/// Registry of available serializers
378pub struct SerializerRegistry {
379    default: SerializerType,
380}
381
382impl Default for SerializerRegistry {
383    fn default() -> Self {
384        Self::new()
385    }
386}
387
388impl SerializerRegistry {
389    /// Create a new serializer registry with JSON as default
390    pub fn new() -> Self {
391        Self {
392            default: SerializerType::Json,
393        }
394    }
395
396    /// Get the default serializer type
397    #[inline]
398    pub fn default_serializer(&self) -> SerializerType {
399        self.default
400    }
401
402    /// Get a serializer by content type
403    pub fn get(&self, content_type: &str) -> SerializerResult<SerializerType> {
404        get_serializer(content_type)
405    }
406
407    /// List all available serializers
408    pub fn available() -> Vec<&'static str> {
409        vec![
410            "application/json",
411            #[cfg(feature = "msgpack")]
412            "application/x-msgpack",
413            #[cfg(feature = "yaml")]
414            "application/x-yaml",
415            #[cfg(feature = "bson-format")]
416            "application/bson",
417        ]
418    }
419}
420
421#[cfg(test)]
422mod tests {
423    use super::*;
424    use serde::{Deserialize, Serialize};
425
426    #[derive(Debug, PartialEq, Serialize, Deserialize)]
427    struct TestData {
428        name: String,
429        value: i32,
430    }
431
432    #[test]
433    fn test_json_serializer_round_trip() {
434        let serializer = JsonSerializer;
435        let data = TestData {
436            name: "test".to_string(),
437            value: 42,
438        };
439
440        let bytes = serializer.serialize(&data).unwrap();
441        let decoded: TestData = serializer.deserialize(&bytes).unwrap();
442
443        assert_eq!(data, decoded);
444    }
445
446    #[test]
447    fn test_json_serializer_content_type() {
448        let serializer = JsonSerializer;
449        assert_eq!(serializer.content_type(), ContentType::Json);
450        assert_eq!(serializer.content_encoding(), ContentEncoding::Utf8);
451        assert_eq!(serializer.name(), "json");
452    }
453
454    #[cfg(feature = "msgpack")]
455    #[test]
456    fn test_msgpack_serializer_round_trip() {
457        let serializer = MessagePackSerializer;
458        let data = TestData {
459            name: "msgpack_test".to_string(),
460            value: 100,
461        };
462
463        let bytes = serializer.serialize(&data).unwrap();
464        let decoded: TestData = serializer.deserialize(&bytes).unwrap();
465
466        assert_eq!(data, decoded);
467    }
468
469    #[cfg(feature = "msgpack")]
470    #[test]
471    fn test_msgpack_serializer_content_type() {
472        let serializer = MessagePackSerializer;
473        assert_eq!(serializer.content_type(), ContentType::MessagePack);
474        assert_eq!(serializer.content_encoding(), ContentEncoding::Binary);
475        assert_eq!(serializer.name(), "msgpack");
476    }
477
478    #[test]
479    fn test_get_serializer_json() {
480        let serializer = get_serializer("application/json").unwrap();
481        assert_eq!(serializer.name(), "json");
482    }
483
484    #[cfg(feature = "msgpack")]
485    #[test]
486    fn test_get_serializer_msgpack() {
487        let serializer = get_serializer("application/x-msgpack").unwrap();
488        assert_eq!(serializer.name(), "msgpack");
489    }
490
491    #[test]
492    fn test_get_serializer_unsupported() {
493        let result = get_serializer("application/unsupported");
494        assert!(result.is_err());
495        match result {
496            Err(SerializerError::UnsupportedContentType(ct)) => {
497                assert_eq!(ct, "application/unsupported");
498            }
499            _ => panic!("Expected UnsupportedContentType error"),
500        }
501    }
502
503    #[test]
504    fn test_serializer_registry() {
505        let registry = SerializerRegistry::new();
506        assert_eq!(registry.default_serializer().name(), "json");
507
508        let json = registry.get("application/json").unwrap();
509        assert_eq!(json.name(), "json");
510    }
511
512    #[test]
513    fn test_serializer_registry_available() {
514        let available = SerializerRegistry::available();
515        assert!(available.contains(&"application/json"));
516    }
517
518    #[test]
519    fn test_serializer_error_display() {
520        let err = SerializerError::Serialize("test error".to_string());
521        assert_eq!(err.to_string(), "Serialization error: test error");
522
523        let err = SerializerError::Deserialize("parse failed".to_string());
524        assert_eq!(err.to_string(), "Deserialization error: parse failed");
525
526        let err = SerializerError::UnsupportedContentType("text/plain".to_string());
527        assert_eq!(err.to_string(), "Unsupported content type: text/plain");
528
529        let err = SerializerError::Compression("gzip failed".to_string());
530        assert_eq!(err.to_string(), "Compression error: gzip failed");
531    }
532
533    #[cfg(feature = "bson-format")]
534    #[test]
535    fn test_bson_serializer_round_trip() {
536        let serializer = BsonSerializer;
537        let data = TestData {
538            name: "bson_test".to_string(),
539            value: 200,
540        };
541
542        let bytes = serializer.serialize(&data).unwrap();
543        let decoded: TestData = serializer.deserialize(&bytes).unwrap();
544
545        assert_eq!(data, decoded);
546    }
547
548    #[cfg(feature = "bson-format")]
549    #[test]
550    fn test_bson_serializer_content_type() {
551        let serializer = BsonSerializer;
552        assert_eq!(
553            serializer.content_type(),
554            ContentType::Custom("application/bson".to_string())
555        );
556        assert_eq!(serializer.content_encoding(), ContentEncoding::Binary);
557        assert_eq!(serializer.name(), "bson");
558    }
559
560    #[cfg(feature = "bson-format")]
561    #[test]
562    fn test_get_serializer_bson() {
563        let serializer = get_serializer("application/bson").unwrap();
564        assert_eq!(serializer.name(), "bson");
565    }
566
567    #[test]
568    fn test_serializer_type_equality() {
569        let json1 = SerializerType::Json;
570        let json2 = SerializerType::Json;
571        assert_eq!(json1, json2);
572
573        #[cfg(feature = "msgpack")]
574        {
575            let msgpack = SerializerType::MessagePack;
576            assert_ne!(json1, msgpack);
577        }
578    }
579
580    #[test]
581    fn test_serializer_type_hash() {
582        use std::collections::HashSet;
583
584        let mut set = HashSet::new();
585        set.insert(SerializerType::Json);
586        set.insert(SerializerType::Json); // Duplicate
587
588        #[cfg(feature = "msgpack")]
589        set.insert(SerializerType::MessagePack);
590
591        #[cfg(feature = "msgpack")]
592        assert_eq!(set.len(), 2);
593
594        #[cfg(not(feature = "msgpack"))]
595        assert_eq!(set.len(), 1);
596
597        assert!(set.contains(&SerializerType::Json));
598    }
599
600    #[test]
601    fn test_serializer_type_display() {
602        assert_eq!(SerializerType::Json.to_string(), "json");
603
604        #[cfg(feature = "msgpack")]
605        assert_eq!(SerializerType::MessagePack.to_string(), "msgpack");
606
607        #[cfg(feature = "yaml")]
608        assert_eq!(SerializerType::Yaml.to_string(), "yaml");
609
610        #[cfg(feature = "bson-format")]
611        assert_eq!(SerializerType::Bson.to_string(), "bson");
612    }
613
614    #[test]
615    fn test_serializer_type_try_from() {
616        use std::convert::TryFrom;
617
618        let json = SerializerType::try_from("application/json").unwrap();
619        assert_eq!(json, SerializerType::Json);
620
621        #[cfg(feature = "msgpack")]
622        {
623            let msgpack = SerializerType::try_from("application/x-msgpack").unwrap();
624            assert_eq!(msgpack, SerializerType::MessagePack);
625        }
626
627        #[cfg(feature = "yaml")]
628        {
629            let yaml = SerializerType::try_from("application/yaml").unwrap();
630            assert_eq!(yaml, SerializerType::Yaml);
631        }
632
633        // Test error case
634        let result = SerializerType::try_from("application/unsupported");
635        assert!(result.is_err());
636    }
637
638    #[test]
639    fn test_serializer_type_default() {
640        let default_type = SerializerType::default();
641        assert_eq!(default_type, SerializerType::Json);
642    }
643
644    #[test]
645    fn test_serializer_type_copy() {
646        let json = SerializerType::Json;
647        let json_copy = json; // Copy trait
648        let _json_original = json; // Can still use original
649
650        assert_eq!(json_copy, SerializerType::Json);
651    }
652}