error_handling/
error_handling.rs

1//! Error Handling and Validation Example
2//!
3//! This example demonstrates robust error handling patterns for serialization:
4//! - Common serialization error scenarios and recovery
5//! - Data validation and schema evolution
6//! - Graceful degradation strategies
7//! - Error reporting and debugging techniques
8//! - Production-ready error handling patterns
9//!
10//! # Learning Objectives
11//!
12//! - Understand common serialization failure modes
13//! - Learn error recovery and fallback strategies
14//! - Master data validation techniques
15//! - Explore schema evolution patterns
16//! - Implement production-ready error handling
17//!
18//! # Prerequisites
19//!
20//! - Understanding of basic serialization (see basic_structs.rs)
21//! - Knowledge of Rust error handling patterns
22//! - Familiarity with data validation concepts
23//!
24//! # Usage
25//!
26//! ```bash
27//! cargo run --example error_handling
28//! ```
29
30use std::{collections::HashMap, fs, io::Write};
31use train_station::serialization::{
32    FieldValue, FromFieldValue, SerializationError, SerializationResult, StructDeserializer,
33    StructSerializable, StructSerializer, ToFieldValue,
34};
35
36/// Versioned data structure for schema evolution testing
37#[derive(Debug, Clone, PartialEq)]
38pub struct VersionedData {
39    pub version: u32,
40    pub name: String,
41    pub value: f64,
42    // New fields for schema evolution
43    pub optional_field: Option<String>,
44    pub new_field: Option<i32>,
45}
46
47impl StructSerializable for VersionedData {
48    fn to_serializer(&self) -> StructSerializer {
49        StructSerializer::new()
50            .field("version", &self.version)
51            .field("name", &self.name)
52            .field("value", &self.value)
53            .field("optional_field", &self.optional_field)
54            .field("new_field", &self.new_field)
55    }
56
57    fn from_deserializer(deserializer: &mut StructDeserializer) -> SerializationResult<Self> {
58        let version = deserializer.field("version")?;
59        let name = deserializer.field("name")?;
60        let value = deserializer.field("value")?;
61
62        // Handle optional fields gracefully for schema evolution
63        let optional_field = deserializer.field_optional("optional_field")?;
64        let new_field = deserializer.field_optional("new_field")?;
65
66        // Validate version compatibility
67        if version > 3 {
68            return Err(SerializationError::ValidationFailed {
69                field: "version".to_string(),
70                message: format!("Unsupported version: {}. Maximum supported: 3", version),
71            });
72        }
73
74        Ok(VersionedData {
75            version,
76            name,
77            value,
78            optional_field,
79            new_field,
80        })
81    }
82}
83
84impl ToFieldValue for VersionedData {
85    fn to_field_value(&self) -> FieldValue {
86        match self.to_json() {
87            Ok(json_str) => FieldValue::from_json_object(json_str),
88            Err(_) => FieldValue::from_string("serialization_error".to_string()),
89        }
90    }
91}
92
93impl FromFieldValue for VersionedData {
94    fn from_field_value(value: FieldValue, field_name: &str) -> SerializationResult<Self> {
95        // Try JSON object first
96        if let Ok(json_data) = value.as_json_object() {
97            return Self::from_json(json_data).map_err(|e| SerializationError::ValidationFailed {
98                field: field_name.to_string(),
99                message: format!("Failed to deserialize VersionedData from JSON: {}", e),
100            });
101        }
102
103        // Try binary object
104        if let Ok(binary_data) = value.as_binary_object() {
105            return Self::from_binary(binary_data).map_err(|e| {
106                SerializationError::ValidationFailed {
107                    field: field_name.to_string(),
108                    message: format!("Failed to deserialize VersionedData from binary: {}", e),
109                }
110            });
111        }
112
113        Err(SerializationError::ValidationFailed {
114            field: field_name.to_string(),
115            message: format!(
116                "Expected JsonObject or BinaryObject for VersionedData, found {}",
117                value.type_name()
118            ),
119        })
120    }
121}
122
123/// Validated user input with constraints
124#[derive(Debug, Clone, PartialEq)]
125pub struct ValidatedUserInput {
126    pub username: String,
127    pub email: String,
128    pub age: u16,
129    pub preferences: HashMap<String, String>,
130}
131
132impl StructSerializable for ValidatedUserInput {
133    fn to_serializer(&self) -> StructSerializer {
134        StructSerializer::new()
135            .field("username", &self.username)
136            .field("email", &self.email)
137            .field("age", &self.age)
138            .field("preferences", &self.preferences)
139    }
140
141    fn from_deserializer(deserializer: &mut StructDeserializer) -> SerializationResult<Self> {
142        let username: String = deserializer.field("username")?;
143        let email: String = deserializer.field("email")?;
144        let age: u16 = deserializer.field("age")?;
145        let preferences: HashMap<String, String> = deserializer.field("preferences")?;
146
147        // Validate username
148        if username.is_empty() || username.len() > 50 {
149            return Err(SerializationError::ValidationFailed {
150                field: "username".to_string(),
151                message: "Username must be 1-50 characters long".to_string(),
152            });
153        }
154
155        if !username
156            .chars()
157            .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
158        {
159            return Err(SerializationError::ValidationFailed {
160                field: "username".to_string(),
161                message:
162                    "Username can only contain alphanumeric characters, underscores, and hyphens"
163                        .to_string(),
164            });
165        }
166
167        // Validate email (basic check)
168        if !email.contains('@') || !email.contains('.') || email.len() < 5 {
169            return Err(SerializationError::ValidationFailed {
170                field: "email".to_string(),
171                message: "Invalid email format".to_string(),
172            });
173        }
174
175        // Validate age
176        if !(13..=120).contains(&age) {
177            return Err(SerializationError::ValidationFailed {
178                field: "age".to_string(),
179                message: "Age must be between 13 and 120".to_string(),
180            });
181        }
182
183        // Validate preferences
184        if preferences.len() > 20 {
185            return Err(SerializationError::ValidationFailed {
186                field: "preferences".to_string(),
187                message: "Too many preferences (maximum 20)".to_string(),
188            });
189        }
190
191        for (key, value) in &preferences {
192            if key.len() > 50 || value.len() > 200 {
193                return Err(SerializationError::ValidationFailed {
194                    field: "preferences".to_string(),
195                    message: format!("Preference key/value too long: {}", key),
196                });
197            }
198        }
199
200        Ok(ValidatedUserInput {
201            username,
202            email,
203            age,
204            preferences,
205        })
206    }
207}
208
209impl ToFieldValue for ValidatedUserInput {
210    fn to_field_value(&self) -> FieldValue {
211        match self.to_json() {
212            Ok(json_str) => FieldValue::from_json_object(json_str),
213            Err(_) => FieldValue::from_string("serialization_error".to_string()),
214        }
215    }
216}
217
218impl FromFieldValue for ValidatedUserInput {
219    fn from_field_value(value: FieldValue, field_name: &str) -> SerializationResult<Self> {
220        // Try JSON object first
221        if let Ok(json_data) = value.as_json_object() {
222            return Self::from_json(json_data).map_err(|e| SerializationError::ValidationFailed {
223                field: field_name.to_string(),
224                message: format!("Failed to deserialize ValidatedUserInput from JSON: {}", e),
225            });
226        }
227
228        // Try binary object
229        if let Ok(binary_data) = value.as_binary_object() {
230            return Self::from_binary(binary_data).map_err(|e| {
231                SerializationError::ValidationFailed {
232                    field: field_name.to_string(),
233                    message: format!(
234                        "Failed to deserialize ValidatedUserInput from binary: {}",
235                        e
236                    ),
237                }
238            });
239        }
240
241        Err(SerializationError::ValidationFailed {
242            field: field_name.to_string(),
243            message: format!(
244                "Expected JsonObject or BinaryObject for ValidatedUserInput, found {}",
245                value.type_name()
246            ),
247        })
248    }
249}
250
251/// Recovery helper for handling partial data
252#[derive(Debug, Clone, PartialEq)]
253pub struct RecoverableData {
254    pub critical_field: String,
255    pub important_field: Option<String>,
256    pub optional_field: Option<String>,
257    pub metadata: HashMap<String, String>,
258}
259
260impl StructSerializable for RecoverableData {
261    fn to_serializer(&self) -> StructSerializer {
262        StructSerializer::new()
263            .field("critical_field", &self.critical_field)
264            .field("important_field", &self.important_field)
265            .field("optional_field", &self.optional_field)
266            .field("metadata", &self.metadata)
267    }
268
269    fn from_deserializer(deserializer: &mut StructDeserializer) -> SerializationResult<Self> {
270        // Critical field - must exist
271        let critical_field = deserializer.field("critical_field")?;
272
273        // Important field - try to recover if missing
274        let important_field = deserializer.field_optional("important_field")?;
275
276        // Optional field - graceful fallback
277        let optional_field = deserializer.field_optional("optional_field")?;
278
279        // Metadata - recover what we can
280        let metadata = deserializer.field_or("metadata", HashMap::new())?;
281
282        Ok(RecoverableData {
283            critical_field,
284            important_field,
285            optional_field,
286            metadata,
287        })
288    }
289}
290
291impl ToFieldValue for RecoverableData {
292    fn to_field_value(&self) -> FieldValue {
293        match self.to_json() {
294            Ok(json_str) => FieldValue::from_json_object(json_str),
295            Err(_) => FieldValue::from_string("serialization_error".to_string()),
296        }
297    }
298}
299
300impl FromFieldValue for RecoverableData {
301    fn from_field_value(value: FieldValue, field_name: &str) -> SerializationResult<Self> {
302        // Try JSON object first
303        if let Ok(json_data) = value.as_json_object() {
304            return Self::from_json(json_data).map_err(|e| SerializationError::ValidationFailed {
305                field: field_name.to_string(),
306                message: format!("Failed to deserialize RecoverableData from JSON: {}", e),
307            });
308        }
309
310        // Try binary object
311        if let Ok(binary_data) = value.as_binary_object() {
312            return Self::from_binary(binary_data).map_err(|e| {
313                SerializationError::ValidationFailed {
314                    field: field_name.to_string(),
315                    message: format!("Failed to deserialize RecoverableData from binary: {}", e),
316                }
317            });
318        }
319
320        Err(SerializationError::ValidationFailed {
321            field: field_name.to_string(),
322            message: format!(
323                "Expected JsonObject or BinaryObject for RecoverableData, found {}",
324                value.type_name()
325            ),
326        })
327    }
328}
329
330fn main() -> Result<(), Box<dyn std::error::Error>> {
331    println!("=== Error Handling and Validation Example ===\n");
332
333    demonstrate_common_error_scenarios()?;
334    demonstrate_validation_patterns()?;
335    demonstrate_schema_evolution()?;
336    demonstrate_recovery_strategies()?;
337    demonstrate_production_error_handling()?;
338    cleanup_temp_files()?;
339
340    println!("\n=== Example completed successfully! ===");
341    Ok(())
342}
343
344/// Demonstrate common serialization error scenarios
345fn demonstrate_common_error_scenarios() -> Result<(), Box<dyn std::error::Error>> {
346    println!("--- Common Error Scenarios ---");
347
348    fs::create_dir_all("temp_error_tests")?;
349
350    // Scenario 1: Corrupted JSON file
351    println!("1. Corrupted JSON File:");
352    let corrupted_json = r#"{"name": "test", "value": 42, "incomplete"#;
353    fs::write("temp_error_tests/corrupted.json", corrupted_json)?;
354
355    match VersionedData::load_json("temp_error_tests/corrupted.json") {
356        Ok(_) => println!("   Unexpected: Corrupted JSON was parsed successfully"),
357        Err(e) => println!("   Expected error: {}", e),
358    }
359
360    // Scenario 2: Missing required fields
361    println!("\n2. Missing Required Fields:");
362    let incomplete_json = r#"{"name": "test"}"#;
363    fs::write("temp_error_tests/incomplete.json", incomplete_json)?;
364
365    match VersionedData::load_json("temp_error_tests/incomplete.json") {
366        Ok(_) => println!("   Unexpected: Incomplete JSON was parsed successfully"),
367        Err(e) => println!("   Expected error: {}", e),
368    }
369
370    // Scenario 3: Type mismatches
371    println!("\n3. Type Mismatch:");
372    let type_mismatch_json = r#"{"version": "not_a_number", "name": "test", "value": 42.0}"#;
373    fs::write("temp_error_tests/type_mismatch.json", type_mismatch_json)?;
374
375    match VersionedData::load_json("temp_error_tests/type_mismatch.json") {
376        Ok(_) => println!("   Unexpected: Type mismatch was handled gracefully"),
377        Err(e) => println!("   Expected error: {}", e),
378    }
379
380    // Scenario 4: File not found
381    println!("\n4. File Not Found:");
382    match VersionedData::load_json("temp_error_tests/nonexistent.json") {
383        Ok(_) => println!("   Unexpected: Non-existent file was loaded"),
384        Err(e) => println!("   Expected error: {}", e),
385    }
386
387    // Scenario 5: Binary format mismatch
388    println!("\n5. Binary Format Mismatch:");
389    let invalid_binary = vec![0xFF, 0xFF, 0xFF, 0xFF]; // Invalid binary data
390    fs::write("temp_error_tests/invalid.bin", invalid_binary)?;
391
392    match VersionedData::load_binary("temp_error_tests/invalid.bin") {
393        Ok(_) => println!("   Unexpected: Invalid binary was parsed successfully"),
394        Err(e) => println!("   Expected error: {}", e),
395    }
396
397    // Scenario 6: Wrong format loading
398    println!("\n6. Wrong Format Loading:");
399    let valid_data = VersionedData {
400        version: 1,
401        name: "test".to_string(),
402        value: 42.0,
403        optional_field: None,
404        new_field: None,
405    };
406    valid_data.save_binary("temp_error_tests/valid.bin")?;
407
408    // Try to load binary file as JSON
409    match VersionedData::load_json("temp_error_tests/valid.bin") {
410        Ok(_) => println!("   Unexpected: Binary file was loaded as JSON"),
411        Err(e) => println!("   Expected error: {}", e),
412    }
413
414    Ok(())
415}
416
417/// Demonstrate validation patterns
418fn demonstrate_validation_patterns() -> Result<(), Box<dyn std::error::Error>> {
419    println!("\n--- Validation Patterns ---");
420
421    println!("Testing input validation with various scenarios:");
422
423    // Valid input
424    println!("\n1. Valid Input:");
425    let mut valid_preferences = HashMap::new();
426    valid_preferences.insert("theme".to_string(), "dark".to_string());
427    valid_preferences.insert("language".to_string(), "en".to_string());
428
429    let valid_input = ValidatedUserInput {
430        username: "john_doe".to_string(),
431        email: "john@example.com".to_string(),
432        age: 25,
433        preferences: valid_preferences,
434    };
435
436    match valid_input.to_json() {
437        Ok(json) => {
438            println!("   ✓ Valid input serialized successfully");
439            match ValidatedUserInput::from_json(&json) {
440                Ok(_) => println!("   ✓ Valid input deserialized successfully"),
441                Err(e) => println!("   ✗ Deserialization failed: {}", e),
442            }
443        }
444        Err(e) => println!("   ✗ Serialization failed: {}", e),
445    }
446
447    // Test validation errors
448    let validation_tests = vec![
449        (
450            "Empty username",
451            ValidatedUserInput {
452                username: "".to_string(),
453                email: "test@example.com".to_string(),
454                age: 25,
455                preferences: HashMap::new(),
456            },
457        ),
458        (
459            "Invalid username characters",
460            ValidatedUserInput {
461                username: "user@name!".to_string(),
462                email: "test@example.com".to_string(),
463                age: 25,
464                preferences: HashMap::new(),
465            },
466        ),
467        (
468            "Invalid email",
469            ValidatedUserInput {
470                username: "username".to_string(),
471                email: "invalid_email".to_string(),
472                age: 25,
473                preferences: HashMap::new(),
474            },
475        ),
476        (
477            "Age too low",
478            ValidatedUserInput {
479                username: "username".to_string(),
480                email: "test@example.com".to_string(),
481                age: 10,
482                preferences: HashMap::new(),
483            },
484        ),
485        (
486            "Age too high",
487            ValidatedUserInput {
488                username: "username".to_string(),
489                email: "test@example.com".to_string(),
490                age: 150,
491                preferences: HashMap::new(),
492            },
493        ),
494    ];
495
496    for (description, invalid_input) in validation_tests {
497        println!("\n2. {}:", description);
498        match invalid_input.to_json() {
499            Ok(json) => match ValidatedUserInput::from_json(&json) {
500                Ok(_) => println!("   ✗ Unexpected: Invalid input was accepted"),
501                Err(e) => println!("   ✓ Expected validation error: {}", e),
502            },
503            Err(e) => println!("   ✗ Serialization error: {}", e),
504        }
505    }
506
507    // Test preferences validation
508    println!("\n3. Preferences Validation:");
509    let mut too_many_preferences = HashMap::new();
510    for i in 0..25 {
511        too_many_preferences.insert(format!("pref_{}", i), "value".to_string());
512    }
513
514    let invalid_prefs_input = ValidatedUserInput {
515        username: "username".to_string(),
516        email: "test@example.com".to_string(),
517        age: 25,
518        preferences: too_many_preferences,
519    };
520
521    match invalid_prefs_input.to_json() {
522        Ok(json) => match ValidatedUserInput::from_json(&json) {
523            Ok(_) => println!("   ✗ Unexpected: Too many preferences were accepted"),
524            Err(e) => println!("   ✓ Expected validation error: {}", e),
525        },
526        Err(e) => println!("   ✗ Serialization error: {}", e),
527    }
528
529    Ok(())
530}
531
532/// Demonstrate schema evolution patterns
533fn demonstrate_schema_evolution() -> Result<(), Box<dyn std::error::Error>> {
534    println!("\n--- Schema Evolution Patterns ---");
535
536    fs::create_dir_all("temp_schema_tests")?;
537
538    // Create data with different schema versions
539    println!("Creating data with different schema versions:");
540
541    // Version 1 data (minimal)
542    let v1_json = r#"{
543        "version": 1,
544        "name": "legacy_data",
545        "value": 123.45
546    }"#;
547    fs::write("temp_schema_tests/v1_data.json", v1_json)?;
548    println!("  ✓ Version 1 data created (minimal fields)");
549
550    // Version 2 data (with optional field)
551    let v2_json = r#"{
552        "version": 2,
553        "name": "v2_data",
554        "value": 678.90,
555        "optional_field": "added_in_v2"
556    }"#;
557    fs::write("temp_schema_tests/v2_data.json", v2_json)?;
558    println!("  ✓ Version 2 data created (with optional field)");
559
560    // Version 3 data (with all fields)
561    let v3_data = VersionedData {
562        version: 3,
563        name: "v3_data".to_string(),
564        value: 999.99,
565        optional_field: Some("present".to_string()),
566        new_field: Some(42),
567    };
568    v3_data.save_json("temp_schema_tests/v3_data.json")?;
569    println!("  ✓ Version 3 data created (all fields)");
570
571    // Test backward compatibility
572    println!("\nTesting backward compatibility:");
573
574    // Load v1 data with current deserializer
575    match VersionedData::load_json("temp_schema_tests/v1_data.json") {
576        Ok(data) => {
577            println!("  ✓ V1 data loaded successfully:");
578            println!("    Name: {}", data.name);
579            println!("    Value: {}", data.value);
580            println!("    Optional field: {:?}", data.optional_field);
581            println!("    New field: {:?}", data.new_field);
582        }
583        Err(e) => println!("  ✗ Failed to load V1 data: {}", e),
584    }
585
586    // Load v2 data with current deserializer
587    match VersionedData::load_json("temp_schema_tests/v2_data.json") {
588        Ok(data) => {
589            println!("  ✓ V2 data loaded successfully:");
590            println!("    Name: {}", data.name);
591            println!("    Value: {}", data.value);
592            println!("    Optional field: {:?}", data.optional_field);
593            println!("    New field: {:?}", data.new_field);
594        }
595        Err(e) => println!("  ✗ Failed to load V2 data: {}", e),
596    }
597
598    // Test future version rejection
599    println!("\nTesting future version handling:");
600    let future_version_json = r#"{
601        "version": 99,
602        "name": "future_data",
603        "value": 123.45,
604        "unknown_field": "should_be_ignored"
605    }"#;
606    fs::write("temp_schema_tests/future_data.json", future_version_json)?;
607
608    match VersionedData::load_json("temp_schema_tests/future_data.json") {
609        Ok(_) => println!("  ✗ Unexpected: Future version was accepted"),
610        Err(e) => println!("  ✓ Expected rejection of future version: {}", e),
611    }
612
613    // Demonstrate migration strategy
614    println!("\nDemonstrating migration strategy:");
615    println!("  Strategy: Load old format, upgrade to new format, save");
616
617    // Simulate migrating v1 data to v3 format
618    let v1_loaded = VersionedData::load_json("temp_schema_tests/v1_data.json")?;
619    let v1_upgraded = VersionedData {
620        version: 3,
621        name: v1_loaded.name,
622        value: v1_loaded.value,
623        optional_field: Some("migrated_default".to_string()),
624        new_field: Some(0),
625    };
626
627    v1_upgraded.save_json("temp_schema_tests/v1_migrated.json")?;
628    println!("  ✓ V1 data migrated to V3 format");
629
630    Ok(())
631}
632
633/// Demonstrate recovery strategies
634fn demonstrate_recovery_strategies() -> Result<(), Box<dyn std::error::Error>> {
635    println!("\n--- Recovery Strategies ---");
636
637    fs::create_dir_all("temp_recovery_tests")?;
638
639    // Strategy 1: Graceful degradation
640    println!("1. Graceful Degradation Strategy:");
641
642    // Create complete data
643    let complete_data = RecoverableData {
644        critical_field: "essential_info".to_string(),
645        important_field: Some("important_info".to_string()),
646        optional_field: Some("nice_to_have".to_string()),
647        metadata: {
648            let mut map = HashMap::new();
649            map.insert("key1".to_string(), "value1".to_string());
650            map.insert("key2".to_string(), "value2".to_string());
651            map
652        },
653    };
654
655    // Save complete data
656    complete_data.save_json("temp_recovery_tests/complete.json")?;
657
658    // Create partial data (missing some fields)
659    let partial_json = r#"{
660        "critical_field": "essential_info",
661        "optional_field": "nice_to_have"
662    }"#;
663    fs::write("temp_recovery_tests/partial.json", partial_json)?;
664
665    // Load partial data and demonstrate recovery
666    match RecoverableData::load_json("temp_recovery_tests/partial.json") {
667        Ok(recovered) => {
668            println!("  ✓ Partial data recovered successfully:");
669            println!("    Critical field: {}", recovered.critical_field);
670            println!(
671                "    Important field: {:?} (missing, set to None)",
672                recovered.important_field
673            );
674            println!("    Optional field: {:?}", recovered.optional_field);
675            println!(
676                "    Metadata: {} entries (defaulted to empty)",
677                recovered.metadata.len()
678            );
679        }
680        Err(e) => println!("  ✗ Recovery failed: {}", e),
681    }
682
683    // Strategy 2: Error context preservation
684    println!("\n2. Error Context Preservation:");
685
686    let malformed_json = r#"{
687        "critical_field": "essential_info",
688        "important_field": 12345,
689        "metadata": "not_a_map"
690    }"#;
691    fs::write("temp_recovery_tests/malformed.json", malformed_json)?;
692
693    match RecoverableData::load_json("temp_recovery_tests/malformed.json") {
694        Ok(_) => println!("  ✗ Unexpected: Malformed data was accepted"),
695        Err(e) => {
696            println!("  ✓ Error context preserved:");
697            println!("    Error: {}", e);
698            println!("    Error type: {:?}", std::mem::discriminant(&e));
699        }
700    }
701
702    // Strategy 3: Fallback data sources
703    println!("\n3. Fallback Data Sources:");
704
705    // Primary source (corrupted)
706    let corrupted_primary = "corrupted data";
707    fs::write("temp_recovery_tests/primary.json", corrupted_primary)?;
708
709    // Backup source (valid)
710    let backup_data = RecoverableData {
711        critical_field: "backup_critical".to_string(),
712        important_field: Some("backup_important".to_string()),
713        optional_field: None,
714        metadata: HashMap::new(),
715    };
716    backup_data.save_json("temp_recovery_tests/backup.json")?;
717
718    // Default fallback
719    let default_data = RecoverableData {
720        critical_field: "default_critical".to_string(),
721        important_field: None,
722        optional_field: None,
723        metadata: HashMap::new(),
724    };
725
726    println!("  Attempting to load data with fallback chain:");
727
728    // Try primary source
729    let loaded_data = match RecoverableData::load_json("temp_recovery_tests/primary.json") {
730        Ok(data) => {
731            println!("    ✓ Loaded from primary source");
732            data
733        }
734        Err(_) => {
735            println!("    ✗ Primary source failed, trying backup");
736
737            // Try backup source
738            match RecoverableData::load_json("temp_recovery_tests/backup.json") {
739                Ok(data) => {
740                    println!("    ✓ Loaded from backup source");
741                    data
742                }
743                Err(_) => {
744                    println!("    ✗ Backup source failed, using default");
745                    default_data
746                }
747            }
748        }
749    };
750
751    println!("  Final loaded data:");
752    println!("    Critical field: {}", loaded_data.critical_field);
753
754    Ok(())
755}
756
757/// Demonstrate production-ready error handling
758fn demonstrate_production_error_handling() -> Result<(), Box<dyn std::error::Error>> {
759    println!("\n--- Production Error Handling ---");
760
761    fs::create_dir_all("temp_production_tests")?;
762
763    // Error logging and monitoring
764    println!("1. Error Logging and Monitoring:");
765
766    let test_data = VersionedData {
767        version: 2,
768        name: "production_test".to_string(),
769        value: 42.0,
770        optional_field: Some("test".to_string()),
771        new_field: None,
772    };
773
774    // Create error log for demonstration
775    let mut error_log = fs::OpenOptions::new()
776        .create(true)
777        .append(true)
778        .open("temp_production_tests/error.log")?;
779
780    // Function to log errors in production format
781    let mut log_error =
782        |error: &SerializationError, context: &str| -> Result<(), Box<dyn std::error::Error>> {
783            let timestamp = std::time::SystemTime::now()
784                .duration_since(std::time::UNIX_EPOCH)?
785                .as_secs();
786
787            writeln!(error_log, "[{}] ERROR in {}: {}", timestamp, context, error)?;
788            Ok(())
789        };
790
791    // Simulate various error scenarios with logging
792    let error_scenarios = vec![
793        ("corrupted_file.json", "invalid json content"),
794        ("missing_fields.json", r#"{"version": 1}"#),
795        (
796            "type_error.json",
797            r#"{"version": "not_number", "name": "test", "value": 42.0}"#,
798        ),
799    ];
800
801    for (filename, content) in error_scenarios {
802        let filepath = format!("temp_production_tests/{}", filename);
803        fs::write(&filepath, content)?;
804
805        match VersionedData::load_json(&filepath) {
806            Ok(_) => println!("  ✗ Unexpected success for {}", filename),
807            Err(e) => {
808                log_error(&e, &format!("load_config({})", filename))?;
809                println!("  ✓ Error logged for {}: {}", filename, e);
810            }
811        }
812    }
813
814    // Health check pattern
815    println!("\n2. Health Check Pattern:");
816
817    let health_check = || -> Result<bool, SerializationError> {
818        // Check if we can serialize/deserialize basic data
819        let test_data = VersionedData {
820            version: 1,
821            name: "health_check".to_string(),
822            value: 1.0,
823            optional_field: None,
824            new_field: None,
825        };
826
827        let serialized = test_data.to_json()?;
828        let _deserialized = VersionedData::from_json(&serialized)?;
829        Ok(true)
830    };
831
832    match health_check() {
833        Ok(_) => println!("  ✓ Serialization system health check passed"),
834        Err(e) => {
835            log_error(&e, "health_check")?;
836            println!("  ✗ Serialization system health check failed: {}", e);
837        }
838    }
839
840    // Circuit breaker pattern simulation
841    println!("\n3. Circuit Breaker Pattern:");
842
843    struct CircuitBreaker {
844        failure_count: u32,
845        failure_threshold: u32,
846        is_open: bool,
847    }
848
849    impl CircuitBreaker {
850        fn new(threshold: u32) -> Self {
851            Self {
852                failure_count: 0,
853                failure_threshold: threshold,
854                is_open: false,
855            }
856        }
857
858        fn call<F, T>(&mut self, operation: F) -> Result<T, String>
859        where
860            F: FnOnce() -> Result<T, SerializationError>,
861        {
862            if self.is_open {
863                return Err("Circuit breaker is open".to_string());
864            }
865
866            match operation() {
867                Ok(result) => {
868                    self.failure_count = 0; // Reset on success
869                    Ok(result)
870                }
871                Err(e) => {
872                    self.failure_count += 1;
873                    if self.failure_count >= self.failure_threshold {
874                        self.is_open = true;
875                        println!(
876                            "    Circuit breaker opened after {} failures",
877                            self.failure_count
878                        );
879                    }
880                    Err(e.to_string())
881                }
882            }
883        }
884    }
885
886    let mut circuit_breaker = CircuitBreaker::new(3);
887
888    // Simulate operations that fail
889    for i in 1..=5 {
890        let result = circuit_breaker
891            .call(|| VersionedData::load_json("temp_production_tests/corrupted_file.json"));
892
893        match result {
894            Ok(_) => println!("  Operation {} succeeded", i),
895            Err(e) => println!("  Operation {} failed: {}", i, e),
896        }
897    }
898
899    // Retry mechanism
900    println!("\n4. Retry Mechanism:");
901
902    let retry_operation = |max_attempts: u32| -> Result<VersionedData, String> {
903        for attempt in 1..=max_attempts {
904            println!("    Attempt {}/{}", attempt, max_attempts);
905
906            // Try different sources in order
907            let sources = vec![
908                "temp_production_tests/corrupted_file.json",
909                "temp_production_tests/missing_fields.json",
910                "temp_production_tests/backup_valid.json",
911            ];
912
913            if attempt == max_attempts {
914                // On final attempt, create valid backup
915                test_data
916                    .save_json("temp_production_tests/backup_valid.json")
917                    .map_err(|e| format!("Failed to create backup: {}", e))?;
918            }
919
920            for source in &sources {
921                match VersionedData::load_json(source) {
922                    Ok(data) => {
923                        println!("    ✓ Succeeded loading from {}", source);
924                        return Ok(data);
925                    }
926                    Err(_) => {
927                        println!("    ✗ Failed to load from {}", source);
928                        continue;
929                    }
930                }
931            }
932
933            if attempt < max_attempts {
934                println!("    Waiting before retry...");
935                // In real code, would sleep here
936            }
937        }
938
939        Err("All retry attempts exhausted".to_string())
940    };
941
942    match retry_operation(3) {
943        Ok(data) => println!("  ✓ Retry succeeded: {}", data.name),
944        Err(e) => println!("  ✗ Retry failed: {}", e),
945    }
946
947    Ok(())
948}
949
950/// Clean up temporary files created during the example
951fn cleanup_temp_files() -> Result<(), Box<dyn std::error::Error>> {
952    println!("\n--- Cleanup ---");
953
954    let directories_to_remove = [
955        "temp_error_tests",
956        "temp_schema_tests",
957        "temp_recovery_tests",
958        "temp_production_tests",
959    ];
960
961    for dir in &directories_to_remove {
962        if std::path::Path::new(dir).exists() {
963            fs::remove_dir_all(dir)?;
964            println!("Removed directory: {}", dir);
965        }
966    }
967
968    println!("Cleanup completed");
969    Ok(())
970}
971
972#[cfg(test)]
973mod tests {
974    use super::*;
975
976    #[test]
977    fn test_versioned_data_validation() {
978        // Valid version should work
979        let valid_data = VersionedData {
980            version: 2,
981            name: "test".to_string(),
982            value: 1.0,
983            optional_field: None,
984            new_field: None,
985        };
986
987        let json = valid_data.to_json().unwrap();
988        let parsed = VersionedData::from_json(&json).unwrap();
989        assert_eq!(valid_data, parsed);
990
991        // Invalid version should fail
992        let invalid_json = r#"{"version": 99, "name": "test", "value": 1.0}"#;
993        assert!(VersionedData::from_json(invalid_json).is_err());
994    }
995
996    #[test]
997    fn test_user_input_validation() {
998        // Valid input
999        let valid_input = ValidatedUserInput {
1000            username: "valid_user".to_string(),
1001            email: "user@example.com".to_string(),
1002            age: 25,
1003            preferences: HashMap::new(),
1004        };
1005
1006        let json = valid_input.to_json().unwrap();
1007        let parsed = ValidatedUserInput::from_json(&json).unwrap();
1008        assert_eq!(valid_input, parsed);
1009
1010        // Invalid username
1011        let invalid_input = ValidatedUserInput {
1012            username: "".to_string(),
1013            email: "user@example.com".to_string(),
1014            age: 25,
1015            preferences: HashMap::new(),
1016        };
1017
1018        let json = invalid_input.to_json().unwrap();
1019        assert!(ValidatedUserInput::from_json(&json).is_err());
1020    }
1021
1022    #[test]
1023    fn test_recoverable_data_fallbacks() {
1024        // Complete data should work normally
1025        let complete = RecoverableData {
1026            critical_field: "critical".to_string(),
1027            important_field: Some("important".to_string()),
1028            optional_field: Some("optional".to_string()),
1029            metadata: HashMap::new(),
1030        };
1031
1032        let json = complete.to_json().unwrap();
1033        let parsed = RecoverableData::from_json(&json).unwrap();
1034        assert_eq!(complete, parsed);
1035
1036        // Partial data should recover gracefully
1037        let partial_json = r#"{"critical_field": "critical"}"#;
1038        let parsed = RecoverableData::from_json(partial_json).unwrap();
1039        assert_eq!(parsed.critical_field, "critical");
1040        assert!(parsed.important_field.is_none());
1041        assert!(parsed.optional_field.is_none());
1042        assert!(parsed.metadata.is_empty());
1043    }
1044}