Skip to main content

serializer/machine/
format.rs

1//! Format detection and dual-mode support
2//!
3//! Automatically detects DX-Machine vs DX Serializer format based on magic bytes.
4
5use crate::types::DxValue;
6
7/// Binary format type
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum DxFormat {
10    /// DX-Machine: Ultra-fast binary (0x5A 0x44)
11    Zero,
12    /// DX Serializer: Human-optimized text (0x44 0x58)
13    Text,
14    /// Unknown format
15    Unknown,
16}
17
18/// Detect format from magic bytes
19#[inline]
20pub fn detect_format(bytes: &[u8]) -> DxFormat {
21    if bytes.len() < 2 {
22        return DxFormat::Unknown;
23    }
24
25    match &bytes[0..2] {
26        [0x5A, 0x44] => DxFormat::Zero, // "ZD" little-endian
27        [0x44, 0x58] => DxFormat::Text, // "DX" (hypothetical)
28        _ => DxFormat::Unknown,
29    }
30}
31
32/// Parse DX format (auto-detect)
33///
34/// This function automatically detects whether the input is DX-Machine binary
35/// or DX Serializer text format and parses accordingly.
36pub fn parse_auto(bytes: &[u8]) -> Result<DxValue, String> {
37    match detect_format(bytes) {
38        DxFormat::Zero => {
39            // Parse as DX-Machine binary
40            Err(
41                "DX-Machine to DxValue conversion not yet implemented (use direct struct access)"
42                    .to_string(),
43            )
44        }
45        DxFormat::Text | DxFormat::Unknown => {
46            // Parse as DX Serializer text (fallback)
47            crate::parse(bytes).map_err(|e| format!("Parse error: {:?}", e))
48        }
49    }
50}
51
52/// Configuration for format selection
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
54pub enum FormatMode {
55    /// Always use DX-Machine binary
56    Zero,
57    /// Always use Dx Serializer text
58    Text,
59    /// Auto-detect based on input
60    #[default]
61    Auto,
62}
63
64impl FormatMode {
65    /// Parse from string
66    pub fn from_str(s: &str) -> Option<Self> {
67        match s.to_lowercase().as_str() {
68            "zero" | "binary" => Some(Self::Zero),
69            "text" | "dsr" => Some(Self::Text),
70            "auto" => Some(Self::Auto),
71            _ => None,
72        }
73    }
74
75    /// Get format name
76    pub fn name(&self) -> &'static str {
77        match self {
78            Self::Zero => "zero",
79            Self::Text => "text",
80            Self::Auto => "auto",
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_detect_zero_format() {
91        let bytes = [0x5A, 0x44, 0x01, 0x04]; // DX-Machine header
92        assert_eq!(detect_format(&bytes), DxFormat::Zero);
93    }
94
95    #[test]
96    fn test_detect_text_format() {
97        let bytes = [0x44, 0x58, b'_', b'I']; // Dx Serializer text format
98        assert_eq!(detect_format(&bytes), DxFormat::Text);
99    }
100
101    #[test]
102    fn test_detect_unknown() {
103        let bytes = [0x00, 0x00, 0x00, 0x00];
104        assert_eq!(detect_format(&bytes), DxFormat::Unknown);
105    }
106
107    #[test]
108    fn test_detect_too_small() {
109        let bytes = [0x5A];
110        assert_eq!(detect_format(&bytes), DxFormat::Unknown);
111    }
112
113    #[test]
114    fn test_format_mode_from_str() {
115        assert_eq!(FormatMode::from_str("zero"), Some(FormatMode::Zero));
116        assert_eq!(FormatMode::from_str("text"), Some(FormatMode::Text));
117        assert_eq!(FormatMode::from_str("dsr"), Some(FormatMode::Text));
118        assert_eq!(FormatMode::from_str("auto"), Some(FormatMode::Auto));
119        assert_eq!(FormatMode::from_str("binary"), Some(FormatMode::Zero));
120        assert_eq!(FormatMode::from_str("invalid"), None);
121    }
122
123    #[test]
124    fn test_format_mode_name() {
125        assert_eq!(FormatMode::Zero.name(), "zero");
126        assert_eq!(FormatMode::Text.name(), "text");
127        assert_eq!(FormatMode::Auto.name(), "auto");
128    }
129}