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}