Skip to main content

rez_next_package/serialization/
types.rs

1//! Package serialization types: PackageFormat, SerializationOptions, PackageMetadata, PackageContainer
2
3use crate::Package;
4use chrono::Utc;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::path::Path;
8
9/// Package serialization format
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PackageFormat {
12    /// YAML format (package.yaml)
13    Yaml,
14    /// JSON format (package.json)
15    Json,
16    /// Python format (package.py)
17    Python,
18    /// Compressed YAML format (package.yaml.gz)
19    YamlCompressed,
20    /// Compressed JSON format (package.json.gz)
21    JsonCompressed,
22    /// Base64-wrapped bincode payload stored in `package.bin`
23    Binary,
24
25    /// TOML format (package.toml)
26    Toml,
27    /// XML format (package.xml)
28    Xml,
29}
30
31/// Serialization options
32#[derive(Debug, Clone)]
33pub struct SerializationOptions {
34    /// Pretty print output
35    pub pretty_print: bool,
36    /// Include metadata
37    pub include_metadata: bool,
38    /// Include timestamps
39    pub include_timestamps: bool,
40    /// Compression level (0-9, only for compressed formats)
41    pub compression_level: u32,
42    /// Custom field filters
43    pub field_filters: Vec<String>,
44    /// Include only specified fields
45    pub include_only: Option<Vec<String>>,
46    /// Exclude specified fields
47    pub exclude_fields: Option<Vec<String>>,
48    /// Custom serialization rules
49    pub custom_rules: HashMap<String, String>,
50}
51
52/// Package metadata for serialization
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct PackageMetadata {
55    /// Serialization timestamp
56    pub serialized_at: String,
57    /// Serialization format
58    pub format: String,
59    /// Serializer version
60    pub serializer_version: String,
61    /// Original file path
62    pub original_path: Option<String>,
63    /// Checksum
64    pub checksum: Option<String>,
65    /// Custom metadata
66    pub custom: HashMap<String, String>,
67}
68
69/// Enhanced package container with metadata
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct PackageContainer {
72    /// Package data
73    pub package: Package,
74    /// Metadata
75    pub metadata: PackageMetadata,
76    /// Schema version
77    pub schema_version: String,
78}
79
80// ── PackageFormat impl ────────────────────────────────────────────────────────
81
82impl PackageFormat {
83    /// Detect format from file extension
84    pub fn from_extension(path: &Path) -> Option<Self> {
85        let path_str = path.to_string_lossy();
86
87        if path_str.ends_with(".yaml.gz") || path_str.ends_with(".yml.gz") {
88            return Some(Self::YamlCompressed);
89        }
90        if path_str.ends_with(".json.gz") {
91            return Some(Self::JsonCompressed);
92        }
93
94        match path.extension()?.to_str()? {
95            "yaml" | "yml" => Some(Self::Yaml),
96            "json" => Some(Self::Json),
97            "py" => Some(Self::Python),
98            "bin" => Some(Self::Binary),
99            "toml" => Some(Self::Toml),
100            "xml" => Some(Self::Xml),
101            _ => None,
102        }
103    }
104
105    /// Get the default file name for this format
106    pub fn default_filename(&self) -> &'static str {
107        match self {
108            Self::Yaml => "package.yaml",
109            Self::Json => "package.json",
110            Self::Python => "package.py",
111            Self::YamlCompressed => "package.yaml.gz",
112            Self::JsonCompressed => "package.json.gz",
113            Self::Binary => "package.bin",
114            Self::Toml => "package.toml",
115            Self::Xml => "package.xml",
116        }
117    }
118
119    /// Check if format supports compression
120    pub fn supports_compression(&self) -> bool {
121        matches!(self, Self::YamlCompressed | Self::JsonCompressed)
122    }
123
124    /// Check if format is text-based
125    pub fn is_text_format(&self) -> bool {
126        !matches!(self, Self::Binary)
127    }
128
129    /// Get MIME type for the format
130    pub fn mime_type(&self) -> &'static str {
131        match self {
132            Self::Yaml | Self::YamlCompressed => "application/x-yaml",
133            Self::Json | Self::JsonCompressed => "application/json",
134            Self::Python => "text/x-python",
135            Self::Binary => "application/octet-stream",
136            Self::Toml => "application/toml",
137            Self::Xml => "application/xml",
138        }
139    }
140}
141
142// ── SerializationOptions impl ─────────────────────────────────────────────────
143
144impl SerializationOptions {
145    /// Create default serialization options
146    pub fn new() -> Self {
147        Self {
148            pretty_print: true,
149            include_metadata: true,
150            include_timestamps: true,
151            compression_level: 6,
152            field_filters: Vec::new(),
153            include_only: None,
154            exclude_fields: None,
155            custom_rules: HashMap::new(),
156        }
157    }
158
159    /// Create minimal serialization options
160    pub fn minimal() -> Self {
161        Self {
162            pretty_print: false,
163            include_metadata: false,
164            include_timestamps: false,
165            compression_level: 1,
166            field_filters: Vec::new(),
167            include_only: None,
168            exclude_fields: None,
169            custom_rules: HashMap::new(),
170        }
171    }
172
173    /// Create compact serialization options
174    pub fn compact() -> Self {
175        Self {
176            pretty_print: false,
177            include_metadata: true,
178            include_timestamps: false,
179            compression_level: 9,
180            field_filters: Vec::new(),
181            include_only: None,
182            exclude_fields: Some(vec!["description".to_string(), "help".to_string()]),
183            custom_rules: HashMap::new(),
184        }
185    }
186
187    /// Add field filter
188    pub fn add_field_filter(&mut self, filter: String) {
189        self.field_filters.push(filter);
190    }
191
192    /// Set include only fields
193    pub fn set_include_only(&mut self, fields: Vec<String>) {
194        self.include_only = Some(fields);
195    }
196
197    /// Set exclude fields
198    pub fn set_exclude_fields(&mut self, fields: Vec<String>) {
199        self.exclude_fields = Some(fields);
200    }
201
202    /// Add custom rule
203    pub fn add_custom_rule(&mut self, field: String, rule: String) {
204        self.custom_rules.insert(field, rule);
205    }
206}
207
208impl Default for SerializationOptions {
209    fn default() -> Self {
210        Self::new()
211    }
212}
213
214// ── PackageMetadata impl ──────────────────────────────────────────────────────
215
216impl PackageMetadata {
217    /// Create new metadata
218    pub fn new(format: String) -> Self {
219        Self {
220            serialized_at: Utc::now().to_rfc3339(),
221            format,
222            serializer_version: env!("CARGO_PKG_VERSION").to_string(),
223            original_path: None,
224            checksum: None,
225            custom: HashMap::new(),
226        }
227    }
228
229    /// Set original path
230    pub fn set_original_path(&mut self, path: String) {
231        self.original_path = Some(path);
232    }
233
234    /// Set checksum
235    pub fn set_checksum(&mut self, checksum: String) {
236        self.checksum = Some(checksum);
237    }
238
239    /// Add custom metadata
240    pub fn add_custom(&mut self, key: String, value: String) {
241        self.custom.insert(key, value);
242    }
243}
244
245// ── PackageContainer impl ─────────────────────────────────────────────────────
246
247impl PackageContainer {
248    /// Create new container
249    pub fn new(package: Package, format: String) -> Self {
250        Self {
251            package,
252            metadata: PackageMetadata::new(format),
253            schema_version: "1.0".to_string(),
254        }
255    }
256
257    /// Create container with metadata
258    pub fn with_metadata(package: Package, metadata: PackageMetadata) -> Self {
259        Self {
260            package,
261            metadata,
262            schema_version: "1.0".to_string(),
263        }
264    }
265}