Skip to main content

sa_token_adapter/
serializer.rs

1// Author: 金书记 | Author: Jin Shuji
2//
3//! Pluggable Serialization Trait and Implementations | 可插拔序列化 trait 与实现
4//!
5//! Unified encode/decode for storage payloads with rolling-upgrade support
6//! (JSON default, optional fory binary). | 统一存储编解码,支持滚动升级(默认 JSON,可选 fory 二进制)。
7//!
8//! ## Design Goals | 设计目标
9//!
10//! 1. **Format Agnostic**: JSON (default), binary (`fory`), future formats
11//!    **格式无关**:JSON(默认)、二进制(`fory`)、未来格式
12//! 2. **Bytes Path**: `encode_bytes` / `decode_bytes` for Redis-friendly payloads
13//!    **字节路径**:面向 Redis 等场景的 `encode_bytes` / `decode_bytes`
14//! 3. **Rolling Upgrade**: Read path auto-detects legacy JSON + magic-prefixed binary
15//!    **滚动升级**:读路径自动探测存量 JSON + 魔数前缀二进制
16//! 4. **Fine-Grained Errors**: `EncodeFailed` / `DecodeFailed` / `FormatMismatch` / `VersionIncompatible`
17//!    **精细错误**:编码失败 / 解码失败 / 格式不匹配 / 版本不兼容
18//!
19//! Prefer [`SharedSerializer`] at call sites (Clone-friendly enum, no trait object).
20//! 调用方优先使用 [`SharedSerializer`](Clone 友好枚举,无 trait object)。
21
22use serde::{Serialize, de::DeserializeOwned};
23
24/// Storage value encoding format | 存储值编码格式
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ValueKind {
27    /// Pure JSON text (no prefix, compatible with existing data) | 纯 JSON 文本(无前缀,兼容存量数据)
28    Json,
29    /// fory binary Base64-encoded with magic prefix | fory 二进制经 Base64 编码并带魔数前缀
30    Binary,
31}
32
33/// Serializer error (A2-2: fine-grained variants) | 序列化错误(A2-2:精细错误变体)
34///
35/// - `EncodeFailed`: serde encode failure — log data model, do not retry
36///   编码失败:记录数据模型问题,不要重试
37/// - `DecodeFailed`: malformed payload — log and consider fallback/migration
38///   解码失败:记录并考虑降级/迁移
39/// - `FormatMismatch`: e.g. JsonSerializer sees binary magic — check config
40///   格式不匹配:检查序列化器配置
41/// - `VersionIncompatible`: reserved for schema evolution
42///   版本不兼容:预留模式演进
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum SerializerError {
45    /// Encoding failed | 编码失败
46    EncodeFailed(String),
47    /// Decoding failed | 解码失败
48    DecodeFailed(String),
49    /// Format mismatch between serializer and payload | 序列化器与 payload 格式不匹配
50    FormatMismatch {
51        /// Expected format name | 期望格式名
52        expected: &'static str,
53        /// Actual detected format | 实际探测到的格式
54        actual: &'static str,
55    },
56    /// Version incompatible (future schema evolution) | 版本不兼容(未来模式演进)
57    VersionIncompatible,
58}
59
60impl std::fmt::Display for SerializerError {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        match self {
63            Self::EncodeFailed(msg) => write!(f, "Serialization encoding failed: {msg}"),
64            Self::DecodeFailed(msg) => write!(f, "Serialization decoding failed: {msg}"),
65            Self::FormatMismatch { expected, actual } => {
66                write!(f, "Format mismatch: expected {expected}, got {actual}")
67            }
68            Self::VersionIncompatible => {
69                write!(f, "Version incompatible: stored data version is too new")
70            }
71        }
72    }
73}
74
75impl std::error::Error for SerializerError {}
76
77/// Pluggable serializer: domain object ↔ storage string | 可插拔序列化器:领域对象 ↔ 存储字符串
78///
79/// # Methods | 方法
80/// - `name` / `kind` / `encode` / `decode` — core String path | 核心 String 路径
81/// - `encode_bytes` / `decode_bytes` — optional zero-copy path (A2-3) | 可选零拷贝路径
82pub trait SaSerializer: Send + Sync {
83    /// Serializer identifier (e.g. "json", "fory") | 序列化器标识
84    fn name(&self) -> &'static str;
85
86    /// Detect payload format for rolling upgrades | 探测 payload 格式以支持滚动升级
87    fn kind(&self, raw: &str) -> ValueKind;
88
89    /// Encode domain object to storage string | 编码领域对象为存储字符串
90    ///
91    /// Errors: `EncodeFailed` on serde failure | 错误:serde 失败时 `EncodeFailed`
92    fn encode<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, SerializerError>;
93
94    /// Decode storage string to domain object | 解码存储字符串为领域对象
95    ///
96    /// Errors: `DecodeFailed` / `FormatMismatch` | 错误:`DecodeFailed` / `FormatMismatch`
97    fn decode<T: DeserializeOwned>(&self, raw: &str) -> Result<T, SerializerError>;
98
99    /// Encode to bytes (default: UTF-8 of `encode`) | 编码为 bytes(默认委托 `encode`)
100    #[inline]
101    fn encode_bytes<T: Serialize + ?Sized>(&self, value: &T) -> Result<Vec<u8>, SerializerError> {
102        self.encode(value).map(|s| s.into_bytes())
103    }
104
105    /// Decode from bytes (default: UTF-8 then `decode`) | 从 bytes 解码(默认 UTF-8 再 `decode`)
106    #[inline]
107    fn decode_bytes<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, SerializerError> {
108        let s = std::str::from_utf8(bytes)
109            .map_err(|e| SerializerError::DecodeFailed(format!("Invalid UTF-8: {e}")))?;
110        self.decode(s)
111    }
112}
113
114/// Binary payload magic (`\u{0001}STF`) | 二进制 payload 魔数
115pub const BINARY_MAGIC: &str = "\u{0001}STF";
116
117/// JSON serializer configuration (A2-4) | JSON 序列化器配置(A2-4)
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
119pub struct JsonSerializerConfig {
120    /// Pretty-print JSON (dev only) | 美化打印(仅开发)
121    pub pretty_print: bool,
122    /// Escape non-ASCII (reserved) | 转义非 ASCII(预留)
123    pub escape_unicode: bool,
124}
125
126/// Default JSON serializer | 默认 JSON 序列化器
127#[derive(Debug, Clone, Copy, Default)]
128pub struct JsonSerializer {
129    config: JsonSerializerConfig,
130}
131
132impl JsonSerializer {
133    /// Create with custom config (A2-4) | 使用自定义配置创建(A2-4)
134    pub fn with_config(config: JsonSerializerConfig) -> Self {
135        Self { config }
136    }
137}
138
139impl SaSerializer for JsonSerializer {
140    #[inline]
141    fn name(&self) -> &'static str {
142        "json"
143    }
144
145    #[inline]
146    fn kind(&self, raw: &str) -> ValueKind {
147        if raw.starts_with(BINARY_MAGIC) {
148            ValueKind::Binary
149        } else {
150            ValueKind::Json
151        }
152    }
153
154    #[inline]
155    fn encode<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, SerializerError> {
156        if self.config.pretty_print {
157            serde_json::to_string_pretty(value)
158                .map_err(|e| SerializerError::EncodeFailed(e.to_string()))
159        } else {
160            serde_json::to_string(value).map_err(|e| SerializerError::EncodeFailed(e.to_string()))
161        }
162    }
163
164    #[inline]
165    fn decode<T: DeserializeOwned>(&self, raw: &str) -> Result<T, SerializerError> {
166        if raw.starts_with(BINARY_MAGIC) {
167            return Err(SerializerError::FormatMismatch {
168                expected: "json",
169                actual: "binary",
170            });
171        }
172        serde_json::from_str(raw).map_err(|e| SerializerError::DecodeFailed(e.to_string()))
173    }
174
175    #[inline]
176    fn encode_bytes<T: Serialize + ?Sized>(&self, value: &T) -> Result<Vec<u8>, SerializerError> {
177        if self.config.pretty_print {
178            serde_json::to_vec_pretty(value)
179                .map_err(|e| SerializerError::EncodeFailed(e.to_string()))
180        } else {
181            serde_json::to_vec(value).map_err(|e| SerializerError::EncodeFailed(e.to_string()))
182        }
183    }
184}
185
186#[cfg(feature = "fory")]
187mod fory_impl {
188    use super::*;
189    use base64::{Engine as _, engine::general_purpose::STANDARD};
190    use fory::Fory;
191    use std::sync::OnceLock;
192
193    fn fory_runtime() -> &'static Fory {
194        static RUNTIME: OnceLock<Fory> = OnceLock::new();
195        RUNTIME.get_or_init(Fory::default)
196    }
197
198    /// Fory serializer configuration (A2-4) | Fory 序列化器配置(A2-4)
199    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
200    pub struct ForySerializerConfig {
201        /// Compression level 0-9 (documented; runtime may ignore) | 压缩级别 0-9(文档项,运行时可能忽略)
202        pub compression_level: u8,
203    }
204
205    impl Default for ForySerializerConfig {
206        fn default() -> Self {
207            Self {
208                compression_level: 6,
209            }
210        }
211    }
212
213    /// fory binary serializer (feature `fory`) | fory 二进制序列化器
214    #[derive(Debug, Clone, Copy, Default)]
215    pub struct ForySerializer {
216        #[allow(dead_code)]
217        config: ForySerializerConfig,
218    }
219
220    impl ForySerializer {
221        /// Create with custom config | 使用自定义配置创建
222        pub fn with_config(config: ForySerializerConfig) -> Self {
223            Self { config }
224        }
225    }
226
227    impl SaSerializer for ForySerializer {
228        #[inline]
229        fn name(&self) -> &'static str {
230            "fory"
231        }
232
233        #[inline]
234        fn kind(&self, raw: &str) -> ValueKind {
235            if raw.starts_with(BINARY_MAGIC) {
236                ValueKind::Binary
237            } else {
238                ValueKind::Json
239            }
240        }
241
242        fn encode<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, SerializerError> {
243            let json = serde_json::to_string(value)
244                .map_err(|e| SerializerError::EncodeFailed(e.to_string()))?;
245            let bytes = fory_runtime()
246                .serialize(&json)
247                .map_err(|e| SerializerError::EncodeFailed(e.to_string()))?;
248            Ok(format!("{}{}", super::BINARY_MAGIC, STANDARD.encode(bytes)))
249        }
250
251        fn decode<T: DeserializeOwned>(&self, raw: &str) -> Result<T, SerializerError> {
252            if raw.starts_with(BINARY_MAGIC) {
253                let b64 = &raw[super::BINARY_MAGIC.len()..];
254                let bytes = STANDARD
255                    .decode(b64)
256                    .map_err(|e| SerializerError::DecodeFailed(e.to_string()))?;
257                let json: String = fory_runtime()
258                    .deserialize(&bytes)
259                    .map_err(|e| SerializerError::DecodeFailed(e.to_string()))?;
260                serde_json::from_str(&json)
261                    .map_err(|e| SerializerError::DecodeFailed(e.to_string()))
262            } else {
263                // Rolling upgrade: legacy pure JSON | 滚动升级:存量纯 JSON
264                serde_json::from_str(raw).map_err(|e| SerializerError::DecodeFailed(e.to_string()))
265            }
266        }
267
268        fn encode_bytes<T: Serialize + ?Sized>(
269            &self,
270            value: &T,
271        ) -> Result<Vec<u8>, SerializerError> {
272            let json = serde_json::to_string(value)
273                .map_err(|e| SerializerError::EncodeFailed(e.to_string()))?;
274            fory_runtime()
275                .serialize(&json)
276                .map_err(|e| SerializerError::EncodeFailed(e.to_string()))
277        }
278
279        fn decode_bytes<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, SerializerError> {
280            let json: String = fory_runtime()
281                .deserialize(bytes)
282                .map_err(|e| SerializerError::DecodeFailed(e.to_string()))?;
283            serde_json::from_str(&json).map_err(|e| SerializerError::DecodeFailed(e.to_string()))
284        }
285    }
286}
287
288#[cfg(feature = "fory")]
289pub use fory_impl::{ForySerializer, ForySerializerConfig};
290
291/// Shared serializer handle (Clone-friendly enum) | 共享序列化器句柄(Clone 友好枚举)
292#[derive(Clone)]
293pub enum SharedSerializer {
294    /// Default JSON | 默认 JSON
295    Json(JsonSerializer),
296    /// fory binary (feature `fory`) | fory 二进制
297    #[cfg(feature = "fory")]
298    Fory(ForySerializer),
299}
300
301impl Default for SharedSerializer {
302    fn default() -> Self {
303        Self::Json(JsonSerializer::default())
304    }
305}
306
307impl std::fmt::Debug for SharedSerializer {
308    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309        write!(f, "SharedSerializer({})", self.name())
310    }
311}
312
313impl SharedSerializer {
314    /// Serializer name | 序列化器名称
315    #[inline]
316    pub fn name(&self) -> &'static str {
317        match self {
318            Self::Json(s) => s.name(),
319            #[cfg(feature = "fory")]
320            Self::Fory(s) => s.name(),
321        }
322    }
323
324    /// Detect value kind | 探测值格式
325    #[inline]
326    pub fn kind(&self, raw: &str) -> ValueKind {
327        match self {
328            Self::Json(s) => s.kind(raw),
329            #[cfg(feature = "fory")]
330            Self::Fory(s) => s.kind(raw),
331        }
332    }
333
334    /// Encode domain object | 编码领域对象
335    #[inline]
336    pub fn encode<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, SerializerError> {
337        match self {
338            Self::Json(s) => s.encode(value),
339            #[cfg(feature = "fory")]
340            Self::Fory(s) => s.encode(value),
341        }
342    }
343
344    /// Decode storage string | 解码存储字符串
345    #[inline]
346    pub fn decode<T: DeserializeOwned>(&self, raw: &str) -> Result<T, SerializerError> {
347        match self {
348            Self::Json(s) => s.decode(raw),
349            #[cfg(feature = "fory")]
350            Self::Fory(s) => s.decode(raw),
351        }
352    }
353
354    /// Encode to bytes | 编码为 bytes
355    #[inline]
356    pub fn encode_bytes<T: Serialize + ?Sized>(
357        &self,
358        value: &T,
359    ) -> Result<Vec<u8>, SerializerError> {
360        match self {
361            Self::Json(s) => s.encode_bytes(value),
362            #[cfg(feature = "fory")]
363            Self::Fory(s) => s.encode_bytes(value),
364        }
365    }
366
367    /// Decode from bytes | 从 bytes 解码
368    #[inline]
369    pub fn decode_bytes<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, SerializerError> {
370        match self {
371            Self::Json(s) => s.decode_bytes(bytes),
372            #[cfg(feature = "fory")]
373            Self::Fory(s) => s.decode_bytes(bytes),
374        }
375    }
376
377    /// Borrow as JsonSerializer if variant matches | 若为 JSON 变体则借用
378    #[inline]
379    pub fn as_json(&self) -> Option<&JsonSerializer> {
380        match self {
381            Self::Json(s) => Some(s),
382            #[cfg(feature = "fory")]
383            _ => None,
384        }
385    }
386
387    /// Borrow as ForySerializer if variant matches | 若为 fory 变体则借用
388    #[cfg(feature = "fory")]
389    #[inline]
390    pub fn as_fory(&self) -> Option<&ForySerializer> {
391        match self {
392            Self::Fory(s) => Some(s),
393            _ => None,
394        }
395    }
396}
397
398impl From<JsonSerializer> for SharedSerializer {
399    fn from(value: JsonSerializer) -> Self {
400        Self::Json(value)
401    }
402}
403
404#[cfg(feature = "fory")]
405impl From<ForySerializer> for SharedSerializer {
406    fn from(value: ForySerializer) -> Self {
407        Self::Fory(value)
408    }
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414
415    #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
416    struct Sample {
417        id: u32,
418        name: String,
419    }
420
421    #[test]
422    fn json_roundtrip() {
423        let ser = SharedSerializer::default();
424        let sample = Sample {
425            id: 1,
426            name: "alice".into(),
427        };
428        let raw = ser.encode(&sample).unwrap();
429        assert_eq!(ser.kind(&raw), ValueKind::Json);
430        assert_eq!(ser.decode::<Sample>(&raw).unwrap(), sample);
431    }
432
433    #[test]
434    fn json_rejects_binary_magic() {
435        let ser = SharedSerializer::default();
436        let err = ser
437            .decode::<Sample>(&format!("{BINARY_MAGIC}xxx"))
438            .unwrap_err();
439        assert!(matches!(
440            err,
441            SerializerError::FormatMismatch {
442                expected: "json",
443                actual: "binary"
444            }
445        ));
446    }
447
448    #[cfg(feature = "fory")]
449    #[test]
450    fn fory_roundtrip() {
451        let ser = SharedSerializer::from(ForySerializer::default());
452        let sample = Sample {
453            id: 2,
454            name: "bob".into(),
455        };
456        let raw = ser.encode(&sample).unwrap();
457        assert_eq!(ser.kind(&raw), ValueKind::Binary);
458        assert!(raw.starts_with(BINARY_MAGIC));
459        assert_eq!(ser.decode::<Sample>(&raw).unwrap(), sample);
460    }
461
462    #[cfg(feature = "fory")]
463    #[test]
464    fn fory_reads_legacy_json() {
465        let ser = SharedSerializer::from(ForySerializer::default());
466        let json = r#"{"id":3,"name":"carol"}"#;
467        assert_eq!(ser.kind(json), ValueKind::Json);
468        assert_eq!(
469            ser.decode::<Sample>(json).unwrap(),
470            Sample {
471                id: 3,
472                name: "carol".into(),
473            }
474        );
475    }
476}