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