1use std::{collections::HashMap, fs, io::Write};
31use train_station::serialization::{
32 FieldValue, FromFieldValue, SerializationError, SerializationResult, StructDeserializer,
33 StructSerializable, StructSerializer, ToFieldValue,
34};
35
36#[derive(Debug, Clone, PartialEq)]
38pub struct VersionedData {
39 pub version: u32,
40 pub name: String,
41 pub value: f64,
42 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 let optional_field = deserializer.field_optional("optional_field")?;
64 let new_field = deserializer.field_optional("new_field")?;
65
66 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 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 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#[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 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 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 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 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 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 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#[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 let critical_field = deserializer.field("critical_field")?;
272
273 let important_field = deserializer.field_optional("important_field")?;
275
276 let optional_field = deserializer.field_optional("optional_field")?;
278
279 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 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 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
344fn 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 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 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 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 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 println!("\n5. Binary Format Mismatch:");
389 let invalid_binary = vec![0xFF, 0xFF, 0xFF, 0xFF]; 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 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 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
417fn 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 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 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 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
532fn 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 println!("Creating data with different schema versions:");
540
541 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 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 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 println!("\nTesting backward compatibility:");
573
574 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 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 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 println!("\nDemonstrating migration strategy:");
615 println!(" Strategy: Load old format, upgrade to new format, save");
616
617 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
633fn 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 println!("1. Graceful Degradation Strategy:");
641
642 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 complete_data.save_json("temp_recovery_tests/complete.json")?;
657
658 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 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 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 println!("\n3. Fallback Data Sources:");
704
705 let corrupted_primary = "corrupted data";
707 fs::write("temp_recovery_tests/primary.json", corrupted_primary)?;
708
709 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 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 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 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
757fn 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 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 let mut error_log = fs::OpenOptions::new()
776 .create(true)
777 .append(true)
778 .open("temp_production_tests/error.log")?;
779
780 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 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 println!("\n2. Health Check Pattern:");
816
817 let health_check = || -> Result<bool, SerializationError> {
818 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 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; 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 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 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 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 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 }
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
950fn 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 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 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 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 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 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 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}