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    ///
87
88    pub fn from_fields(fields: HashMap<String, FieldValue>) -> Self {
89        Self { fields }
90    }
91
92    /// Load a struct from a JSON file
93    ///
94    /// Reads a JSON file from the filesystem and creates a StructDeserializer
95    /// from its contents. The file is expected to contain a valid JSON object
96    /// that can be parsed into field-value pairs.
97    ///
98    /// # Arguments
99    ///
100    /// * `path` - Path to the JSON file to load
101    ///
102    /// # Returns
103    ///
104    /// A StructDeserializer containing the parsed field data, or an error if
105    /// the file cannot be read or parsed
106    ///
107    /// # Errors
108    ///
109    /// Returns an error if:
110    /// - The file does not exist or cannot be opened
111    /// - The file contains invalid JSON
112    /// - The JSON structure is not a valid object
113    /// - I/O errors occur during file reading
114    ///
115    /// # Performance
116    ///
117    /// The entire file is read into memory before parsing. For very large files,
118    /// consider using streaming JSON parsing instead.
119    ///
120
121    pub fn load_json<P: AsRef<Path>>(path: P) -> SerializationResult<Self> {
122        let file = File::open(path)?;
123        let mut reader = BufReader::new(file);
124        let mut json_string = String::new();
125        reader.read_to_string(&mut json_string)?;
126
127        Self::from_json(&json_string)
128    }
129
130    /// Load a struct from a binary file
131    ///
132    /// Reads a binary file from the filesystem and creates a StructDeserializer
133    /// from its contents. The file is expected to contain valid binary data
134    /// that was previously serialized using the binary format.
135    ///
136    /// # Arguments
137    ///
138    /// * `path` - Path to the binary file to load
139    ///
140    /// # Returns
141    ///
142    /// A StructDeserializer containing the parsed field data, or an error if
143    /// the file cannot be read or parsed
144    ///
145    /// # Errors
146    ///
147    /// Returns an error if:
148    /// - The file does not exist or cannot be opened
149    /// - The file contains invalid binary format
150    /// - The binary data is corrupted or truncated
151    /// - I/O errors occur during file reading
152    ///
153    /// # Performance
154    ///
155    /// The entire file is read into memory before parsing. Binary format is
156    /// typically more compact and faster to parse than JSON.
157    ///
158
159    pub fn load_binary<P: AsRef<Path>>(path: P) -> SerializationResult<Self> {
160        let file = File::open(path)?;
161        let mut reader = BufReader::new(file);
162        let mut binary_data = Vec::new();
163        reader.read_to_end(&mut binary_data)?;
164
165        Self::from_binary(&binary_data)
166    }
167
168    /// Extract a field with automatic type detection
169    ///
170    /// Extracts a field from the deserializer and converts it to the specified type.
171    /// The field is consumed (removed) from the deserializer to prevent accidental
172    /// reuse and ensure proper deserialization flow.
173    ///
174    /// # Arguments
175    ///
176    /// * `name` - Name of the field to extract
177    ///
178    /// # Returns
179    ///
180    /// The converted field value, or an error if the field is missing or cannot
181    /// be converted to the target type
182    ///
183    /// # Errors
184    ///
185    /// Returns an error if:
186    /// - The field does not exist in the deserializer
187    /// - The field value cannot be converted to the target type
188    /// - Type conversion fails due to incompatible data
189    ///
190    /// # Performance
191    ///
192    /// Field extraction is O(1) average case due to HashMap lookup. The field
193    /// is removed from the deserializer to prevent memory leaks.
194    ///
195
196    pub fn field<T: FromFieldValue>(&mut self, name: &str) -> SerializationResult<T> {
197        let field_value =
198            self.fields
199                .remove(name)
200                .ok_or_else(|| SerializationError::ValidationFailed {
201                    field: name.to_string(),
202                    message: "Field not found".to_string(),
203                })?;
204
205        T::from_field_value(field_value, name)
206    }
207
208    /// Extract a field value with type conversion and provide a default if missing
209    ///
210    /// Extracts a field from the deserializer and converts it to the specified type.
211    /// If the field is missing, returns the provided default value instead of
212    /// returning an error.
213    ///
214    /// # Arguments
215    ///
216    /// * `name` - Name of the field to extract
217    /// * `default` - Default value to return if the field is missing
218    ///
219    /// # Returns
220    ///
221    /// The converted field value, or the default value if the field is missing.
222    /// Returns an error only if the field exists but cannot be converted to the
223    /// target type.
224    ///
225    /// # Errors
226    ///
227    /// Returns an error if:
228    /// - The field exists but cannot be converted to the target type
229    /// - Type conversion fails due to incompatible data
230    ///
231    /// # Performance
232    ///
233    /// Field extraction is O(1) average case. The field is removed from the
234    /// deserializer if it exists.
235    ///
236
237    pub fn field_or<T: FromFieldValue>(
238        &mut self,
239        name: &str,
240        default: T,
241    ) -> SerializationResult<T> {
242        match self.fields.remove(name) {
243            Some(field_value) => T::from_field_value(field_value, name),
244            None => Ok(default),
245        }
246    }
247
248    /// Extract a field value as an optional type
249    ///
250    /// Extracts a field from the deserializer and converts it to the specified type,
251    /// returning `Some(value)` if the field exists and can be converted, or `None`
252    /// if the field is missing.
253    ///
254    /// # Arguments
255    ///
256    /// * `name` - Name of the field to extract
257    ///
258    /// # Returns
259    ///
260    /// `Some(converted_value)` if the field exists and can be converted,
261    /// `None` if the field is missing, or an error if the field exists but
262    /// cannot be converted to the target type
263    ///
264    /// # Errors
265    ///
266    /// Returns an error if:
267    /// - The field exists but cannot be converted to the target type
268    /// - Type conversion fails due to incompatible data
269    ///
270    /// # Performance
271    ///
272    /// Field extraction is O(1) average case. The field is removed from the
273    /// deserializer if it exists.
274    ///
275
276    pub fn field_optional<T: FromFieldValue>(
277        &mut self,
278        name: &str,
279    ) -> SerializationResult<Option<T>> {
280        match self.fields.remove(name) {
281            Some(field_value) => T::from_field_value(field_value, name).map(Some),
282            None => Ok(None),
283        }
284    }
285
286    /// Extract a field value with custom error handling
287    ///
288    /// Extracts a field from the deserializer and converts it to the specified type,
289    /// using a custom error handling function to process any errors that occur
290    /// during extraction or conversion.
291    ///
292    /// # Arguments
293    ///
294    /// * `name` - Name of the field to extract
295    /// * `error_fn` - Function to handle errors during field extraction or conversion
296    ///
297    /// # Returns
298    ///
299    /// The converted field value, or the result of the error handling function
300    /// if an error occurs
301    ///
302    /// # Errors
303    ///
304    /// The error handling function can return any error type that implements
305    /// the appropriate error traits. The function is called with the field name
306    /// and the original error for context.
307    ///
308    /// # Performance
309    ///
310    /// Field extraction is O(1) average case. The error handling function is
311    /// only called when an error occurs.
312    ///
313
314    pub fn field_with_error<T: FromFieldValue, F>(
315        &mut self,
316        name: &str,
317        error_fn: F,
318    ) -> SerializationResult<T>
319    where
320        F: FnOnce(&str, &SerializationError) -> SerializationResult<T>,
321    {
322        match self.fields.remove(name) {
323            Some(field_value) => {
324                T::from_field_value(field_value, name).map_err(|err| {
325                    // Call error function and return its result
326                    match error_fn(name, &err) {
327                        Ok(_value) => {
328                            // We can't return Ok from map_err, so we need to handle this differently
329                            // For now, we'll just return the original error
330                            err
331                        }
332                        Err(_) => err, // Return original error if error_fn fails
333                    }
334                })
335            }
336            None => error_fn(
337                name,
338                &SerializationError::ValidationFailed {
339                    field: name.to_string(),
340                    message: "Field not found".to_string(),
341                },
342            ),
343        }
344    }
345
346    /// Get the names of all remaining fields
347    ///
348    /// Returns a vector containing the names of all fields that have not yet
349    /// been extracted from the deserializer. This is useful for debugging,
350    /// validation, or implementing custom deserialization logic.
351    ///
352    /// # Returns
353    ///
354    /// A vector of field names that are still available for extraction
355    ///
356    /// # Performance
357    ///
358    /// This method performs O(n) work to collect all field names, where n is
359    /// the number of remaining fields. The returned vector is a copy of the
360    /// field names.
361    ///
362
363    pub fn remaining_fields(&self) -> Vec<&str> {
364        self.fields.keys().map(|s| s.as_str()).collect()
365    }
366
367    /// Check if a field exists
368    ///
369    /// Returns whether a field with the specified name exists in the deserializer
370    /// and has not yet been extracted. This is useful for conditional field
371    /// extraction or validation.
372    ///
373    /// # Arguments
374    ///
375    /// * `name` - Name of the field to check
376    ///
377    /// # Returns
378    ///
379    /// `true` if the field exists and has not been extracted, `false` otherwise
380    ///
381    /// # Performance
382    ///
383    /// This method performs O(1) average case lookup using HashMap contains_key.
384    ///
385
386    pub fn has_field(&self, name: &str) -> bool {
387        self.fields.contains_key(name)
388    }
389
390    /// Create a deserializer from a JSON string
391    ///
392    /// Parses a JSON string and creates a StructDeserializer from its contents.
393    /// The JSON string is expected to contain a valid JSON object that can be
394    /// parsed into field-value pairs.
395    ///
396    /// # Arguments
397    ///
398    /// * `json` - JSON string to parse
399    ///
400    /// # Returns
401    ///
402    /// A StructDeserializer containing the parsed field data, or an error if
403    /// the JSON string cannot be parsed
404    ///
405    /// # Errors
406    ///
407    /// Returns an error if:
408    /// - The JSON string is malformed or invalid
409    /// - The JSON structure is not a valid object
410    /// - Parsing fails due to unexpected JSON format
411    ///
412    /// # Performance
413    ///
414    /// JSON parsing is performed in a single pass. The entire JSON string is
415    /// processed to build the field map.
416    ///
417
418    pub fn from_json(json: &str) -> SerializationResult<Self> {
419        crate::serialization::json::from_json_internal(json)
420    }
421
422    /// Create a deserializer from binary data
423    ///
424    /// Parses binary data and creates a StructDeserializer from its contents.
425    /// The binary data is expected to contain valid binary format data that was
426    /// previously serialized using the binary format.
427    ///
428    /// # Arguments
429    ///
430    /// * `data` - Binary data to parse
431    ///
432    /// # Returns
433    ///
434    /// A StructDeserializer containing the parsed field data, or an error if
435    /// the binary data cannot be parsed
436    ///
437    /// # Errors
438    ///
439    /// Returns an error if:
440    /// - The binary data is malformed or corrupted
441    /// - The binary format is invalid or unsupported
442    /// - Parsing fails due to unexpected binary structure
443    /// - The data is truncated or incomplete
444    ///
445    /// # Performance
446    ///
447    /// Binary parsing is typically faster than JSON parsing due to the more
448    /// compact format and lack of string parsing overhead.
449    ///
450
451    pub fn from_binary(data: &[u8]) -> SerializationResult<Self> {
452        crate::serialization::binary::from_binary_internal(data)
453    }
454}