train_station/serialization/core/deserializer.rs
1//! Core deserializer for structured data extraction and type conversion
2//!
3//! This module provides the `StructDeserializer` which enables type-safe extraction
4//! of fields from serialized data structures. The deserializer supports both JSON
5//! and binary formats, with comprehensive error handling and flexible field access
6//! patterns.
7//!
8//! # Purpose
9//!
10//! The `StructDeserializer` serves as the counterpart to `StructSerializer`, providing:
11//! - Type-safe field extraction with automatic conversion
12//! - Support for both JSON and binary deserialization
13//! - Flexible field access patterns (required, optional, with defaults)
14//! - Comprehensive error handling with detailed validation messages
15//! - Field existence checking and remaining field enumeration
16//!
17//! # Usage Patterns
18//!
19//! The deserializer supports multiple field extraction patterns:
20//! - **Required fields**: `field<T>(name)` - extracts and converts, fails if missing
21//! - **Optional fields**: `field_optional<T>(name)` - returns `Option<T>`
22//! - **Default fields**: `field_or<T>(name, default)` - uses default if missing
23//! - **Custom error handling**: `field_with_error<T>(name, error_fn)` - custom validation
24//!
25//! # Thread Safety
26//!
27//! This type is not thread-safe. Access from multiple threads requires external
28//! synchronization.
29
30use super::error::{SerializationError, SerializationResult};
31use super::traits::FromFieldValue;
32use super::types::FieldValue;
33use std::collections::HashMap;
34use std::fs::File;
35use std::io::{BufReader, Read};
36use std::path::Path;
37
38/// Deserializer for structured data extraction and type conversion
39///
40/// This struct provides a type-safe interface for deserializing complex structures
41/// that were created with `StructSerializer`. It supports both JSON and binary
42/// formats, with comprehensive error handling and flexible field access patterns.
43///
44/// The deserializer maintains a HashMap of field names to `FieldValue` instances,
45/// allowing for efficient field lookup and extraction. Fields are consumed during
46/// extraction to prevent accidental reuse and ensure proper deserialization flow.
47///
48/// # Fields
49///
50/// * `fields` - Internal storage mapping field names to their serialized values
51///
52/// # Thread Safety
53///
54/// This type is not thread-safe. Access from multiple threads requires external
55/// synchronization.
56///
57/// # Memory Layout
58///
59/// The deserializer uses a HashMap for field storage, providing O(1) average
60/// field lookup time. Memory usage scales linearly with the number of fields.
61#[derive(Debug)]
62pub struct StructDeserializer {
63 /// Field storage for loaded data
64 pub(crate) fields: HashMap<String, FieldValue>,
65}
66
67impl StructDeserializer {
68 /// Create a new StructDeserializer from a field map
69 ///
70 /// This is the recommended way to create a StructDeserializer when you have
71 /// a HashMap of fields, typically from a `FieldValue::Object` variant or when
72 /// constructing a deserializer programmatically.
73 ///
74 /// # Arguments
75 ///
76 /// * `fields` - HashMap containing field names and their serialized values
77 ///
78 /// # Returns
79 ///
80 /// A new StructDeserializer ready for field extraction
81 ///
82 /// # Performance
83 ///
84 /// This constructor performs no validation or conversion, making it the fastest
85 /// way to create a deserializer when you already have the field data.
86 pub fn from_fields(fields: HashMap<String, FieldValue>) -> Self {
87 Self { fields }
88 }
89
90 /// Load a struct from a JSON file
91 ///
92 /// Reads a JSON file from the filesystem and creates a StructDeserializer
93 /// from its contents. The file is expected to contain a valid JSON object
94 /// that can be parsed into field-value pairs.
95 ///
96 /// # Arguments
97 ///
98 /// * `path` - Path to the JSON file to load
99 ///
100 /// # Returns
101 ///
102 /// A StructDeserializer containing the parsed field data, or an error if
103 /// the file cannot be read or parsed
104 ///
105 /// # Errors
106 ///
107 /// Returns an error if:
108 /// - The file does not exist or cannot be opened
109 /// - The file contains invalid JSON
110 /// - The JSON structure is not a valid object
111 /// - I/O errors occur during file reading
112 ///
113 /// # Performance
114 ///
115 /// The entire file is read into memory before parsing. For very large files,
116 /// consider using streaming JSON parsing instead.
117 pub fn load_json<P: AsRef<Path>>(path: P) -> SerializationResult<Self> {
118 let file = File::open(path)?;
119 let mut reader = BufReader::new(file);
120 let mut json_string = String::new();
121 reader.read_to_string(&mut json_string)?;
122
123 Self::from_json(&json_string)
124 }
125
126 /// Load a struct from a binary file
127 ///
128 /// Reads a binary file from the filesystem and creates a StructDeserializer
129 /// from its contents. The file is expected to contain valid binary data
130 /// that was previously serialized using the binary format.
131 ///
132 /// # Arguments
133 ///
134 /// * `path` - Path to the binary file to load
135 ///
136 /// # Returns
137 ///
138 /// A StructDeserializer containing the parsed field data, or an error if
139 /// the file cannot be read or parsed
140 ///
141 /// # Errors
142 ///
143 /// Returns an error if:
144 /// - The file does not exist or cannot be opened
145 /// - The file contains invalid binary format
146 /// - The binary data is corrupted or truncated
147 /// - I/O errors occur during file reading
148 ///
149 /// # Performance
150 ///
151 /// The entire file is read into memory before parsing. Binary format is
152 /// typically more compact and faster to parse than JSON.
153 pub fn load_binary<P: AsRef<Path>>(path: P) -> SerializationResult<Self> {
154 let file = File::open(path)?;
155 let mut reader = BufReader::new(file);
156 let mut binary_data = Vec::new();
157 reader.read_to_end(&mut binary_data)?;
158
159 Self::from_binary(&binary_data)
160 }
161
162 /// Extract a field with automatic type detection
163 ///
164 /// Extracts a field from the deserializer and converts it to the specified type.
165 /// The field is consumed (removed) from the deserializer to prevent accidental
166 /// reuse and ensure proper deserialization flow.
167 ///
168 /// # Arguments
169 ///
170 /// * `name` - Name of the field to extract
171 ///
172 /// # Returns
173 ///
174 /// The converted field value, or an error if the field is missing or cannot
175 /// be converted to the target type
176 ///
177 /// # Errors
178 ///
179 /// Returns an error if:
180 /// - The field does not exist in the deserializer
181 /// - The field value cannot be converted to the target type
182 /// - Type conversion fails due to incompatible data
183 ///
184 /// # Performance
185 ///
186 /// Field extraction is O(1) average case due to HashMap lookup. The field
187 /// is removed from the deserializer to prevent memory leaks.
188 pub fn field<T: FromFieldValue>(&mut self, name: &str) -> SerializationResult<T> {
189 let field_value =
190 self.fields
191 .remove(name)
192 .ok_or_else(|| SerializationError::ValidationFailed {
193 field: name.to_string(),
194 message: "Field not found".to_string(),
195 })?;
196
197 T::from_field_value(field_value, name)
198 }
199
200 /// Extract a field value with type conversion and provide a default if missing
201 ///
202 /// Extracts a field from the deserializer and converts it to the specified type.
203 /// If the field is missing, returns the provided default value instead of
204 /// returning an error.
205 ///
206 /// # Arguments
207 ///
208 /// * `name` - Name of the field to extract
209 /// * `default` - Default value to return if the field is missing
210 ///
211 /// # Returns
212 ///
213 /// The converted field value, or the default value if the field is missing.
214 /// Returns an error only if the field exists but cannot be converted to the
215 /// target type.
216 ///
217 /// # Errors
218 ///
219 /// Returns an error if:
220 /// - The field exists but cannot be converted to the target type
221 /// - Type conversion fails due to incompatible data
222 ///
223 /// # Performance
224 ///
225 /// Field extraction is O(1) average case. The field is removed from the
226 /// deserializer if it exists.
227 pub fn field_or<T: FromFieldValue>(
228 &mut self,
229 name: &str,
230 default: T,
231 ) -> SerializationResult<T> {
232 match self.fields.remove(name) {
233 Some(field_value) => T::from_field_value(field_value, name),
234 None => Ok(default),
235 }
236 }
237
238 /// Extract a field value as an optional type
239 ///
240 /// Extracts a field from the deserializer and converts it to the specified type,
241 /// returning `Some(value)` if the field exists and can be converted, or `None`
242 /// if the field is missing.
243 ///
244 /// # Arguments
245 ///
246 /// * `name` - Name of the field to extract
247 ///
248 /// # Returns
249 ///
250 /// `Some(converted_value)` if the field exists and can be converted,
251 /// `None` if the field is missing, or an error if the field exists but
252 /// cannot be converted to the target type
253 ///
254 /// # Errors
255 ///
256 /// Returns an error if:
257 /// - The field exists but cannot be converted to the target type
258 /// - Type conversion fails due to incompatible data
259 ///
260 /// # Performance
261 ///
262 /// Field extraction is O(1) average case. The field is removed from the
263 /// deserializer if it exists.
264 pub fn field_optional<T: FromFieldValue>(
265 &mut self,
266 name: &str,
267 ) -> SerializationResult<Option<T>> {
268 match self.fields.remove(name) {
269 Some(field_value) => T::from_field_value(field_value, name).map(Some),
270 None => Ok(None),
271 }
272 }
273
274 /// Extract a field value with custom error handling
275 ///
276 /// Extracts a field from the deserializer and converts it to the specified type,
277 /// using a custom error handling function to process any errors that occur
278 /// during extraction or conversion.
279 ///
280 /// # Arguments
281 ///
282 /// * `name` - Name of the field to extract
283 /// * `error_fn` - Function to handle errors during field extraction or conversion
284 ///
285 /// # Returns
286 ///
287 /// The converted field value, or the result of the error handling function
288 /// if an error occurs
289 ///
290 /// # Errors
291 ///
292 /// The error handling function can return any error type that implements
293 /// the appropriate error traits. The function is called with the field name
294 /// and the original error for context.
295 ///
296 /// # Performance
297 ///
298 /// Field extraction is O(1) average case. The error handling function is
299 /// only called when an error occurs.
300 pub fn field_with_error<T: FromFieldValue, F>(
301 &mut self,
302 name: &str,
303 error_fn: F,
304 ) -> SerializationResult<T>
305 where
306 F: FnOnce(&str, &SerializationError) -> SerializationResult<T>,
307 {
308 match self.fields.remove(name) {
309 Some(field_value) => {
310 T::from_field_value(field_value, name).map_err(|err| {
311 // Call error function and return its result
312 match error_fn(name, &err) {
313 Ok(_value) => {
314 // We can't return Ok from map_err, so we need to handle this differently
315 // For now, we'll just return the original error
316 err
317 }
318 Err(_) => err, // Return original error if error_fn fails
319 }
320 })
321 }
322 None => error_fn(
323 name,
324 &SerializationError::ValidationFailed {
325 field: name.to_string(),
326 message: "Field not found".to_string(),
327 },
328 ),
329 }
330 }
331
332 /// Get the names of all remaining fields
333 ///
334 /// Returns a vector containing the names of all fields that have not yet
335 /// been extracted from the deserializer. This is useful for debugging,
336 /// validation, or implementing custom deserialization logic.
337 ///
338 /// # Returns
339 ///
340 /// A vector of field names that are still available for extraction
341 ///
342 /// # Performance
343 ///
344 /// This method performs O(n) work to collect all field names, where n is
345 /// the number of remaining fields. The returned vector is a copy of the
346 /// field names.
347 pub fn remaining_fields(&self) -> Vec<&str> {
348 self.fields.keys().map(|s| s.as_str()).collect()
349 }
350
351 /// Check if a field exists
352 ///
353 /// Returns whether a field with the specified name exists in the deserializer
354 /// and has not yet been extracted. This is useful for conditional field
355 /// extraction or validation.
356 ///
357 /// # Arguments
358 ///
359 /// * `name` - Name of the field to check
360 ///
361 /// # Returns
362 ///
363 /// `true` if the field exists and has not been extracted, `false` otherwise
364 ///
365 /// # Performance
366 ///
367 /// This method performs O(1) average case lookup using HashMap contains_key.
368 pub fn has_field(&self, name: &str) -> bool {
369 self.fields.contains_key(name)
370 }
371
372 /// Create a deserializer from a JSON string
373 ///
374 /// Parses a JSON string and creates a StructDeserializer from its contents.
375 /// The JSON string is expected to contain a valid JSON object that can be
376 /// parsed into field-value pairs.
377 ///
378 /// # Arguments
379 ///
380 /// * `json` - JSON string to parse
381 ///
382 /// # Returns
383 ///
384 /// A StructDeserializer containing the parsed field data, or an error if
385 /// the JSON string cannot be parsed
386 ///
387 /// # Errors
388 ///
389 /// Returns an error if:
390 /// - The JSON string is malformed or invalid
391 /// - The JSON structure is not a valid object
392 /// - Parsing fails due to unexpected JSON format
393 ///
394 /// # Performance
395 ///
396 /// JSON parsing is performed in a single pass. The entire JSON string is
397 /// processed to build the field map.
398 pub fn from_json(json: &str) -> SerializationResult<Self> {
399 crate::serialization::json::from_json_internal(json)
400 }
401
402 /// Create a deserializer from binary data
403 ///
404 /// Parses binary data and creates a StructDeserializer from its contents.
405 /// The binary data is expected to contain valid binary format data that was
406 /// previously serialized using the binary format.
407 ///
408 /// # Arguments
409 ///
410 /// * `data` - Binary data to parse
411 ///
412 /// # Returns
413 ///
414 /// A StructDeserializer containing the parsed field data, or an error if
415 /// the binary data cannot be parsed
416 ///
417 /// # Errors
418 ///
419 /// Returns an error if:
420 /// - The binary data is malformed or corrupted
421 /// - The binary format is invalid or unsupported
422 /// - Parsing fails due to unexpected binary structure
423 /// - The data is truncated or incomplete
424 ///
425 /// # Performance
426 ///
427 /// Binary parsing is typically faster than JSON parsing due to the more
428 /// compact format and lack of string parsing overhead.
429 pub fn from_binary(data: &[u8]) -> SerializationResult<Self> {
430 crate::serialization::binary::from_binary_internal(data)
431 }
432}