1use std::{collections::HashMap, fs};
31use train_station::serialization::{
32 FieldValue, FromFieldValue, SerializationError, SerializationResult, StructDeserializer,
33 StructSerializable, StructSerializer, ToFieldValue,
34};
35
36#[derive(Debug, Clone, PartialEq)]
38pub struct UserProfile {
39 pub id: u32,
40 pub username: String,
41 pub email: String,
42 pub age: i32, pub is_active: bool,
44 pub score: f32, }
46
47impl StructSerializable for UserProfile {
48 fn to_serializer(&self) -> StructSerializer {
49 StructSerializer::new()
50 .field("id", &self.id)
51 .field("username", &self.username)
52 .field("email", &self.email)
53 .field("age", &self.age)
54 .field("is_active", &self.is_active)
55 .field("score", &self.score)
56 }
57
58 fn from_deserializer(deserializer: &mut StructDeserializer) -> SerializationResult<Self> {
59 let id = deserializer.field("id")?;
60 let username = deserializer.field("username")?;
61 let email = deserializer.field("email")?;
62 let age = deserializer.field("age")?;
63 let is_active = deserializer.field("is_active")?;
64 let score = deserializer.field("score")?;
65
66 Ok(UserProfile {
67 id,
68 username,
69 email,
70 age,
71 is_active,
72 score,
73 })
74 }
75}
76
77impl ToFieldValue for UserProfile {
78 fn to_field_value(&self) -> FieldValue {
79 match self.to_json() {
81 Ok(json_str) => {
82 FieldValue::from_json_object(json_str)
84 }
85 Err(_) => FieldValue::from_string("serialization_error".to_string()),
86 }
87 }
88}
89
90impl FromFieldValue for UserProfile {
91 fn from_field_value(value: FieldValue, field_name: &str) -> SerializationResult<Self> {
92 if let Ok(json_data) = value.as_json_object() {
94 return Self::from_json(json_data).map_err(|e| SerializationError::ValidationFailed {
95 field: field_name.to_string(),
96 message: format!("Failed to deserialize UserProfile from JSON: {}", e),
97 });
98 }
99
100 if let Ok(binary_data) = value.as_binary_object() {
102 return Self::from_binary(binary_data).map_err(|e| {
103 SerializationError::ValidationFailed {
104 field: field_name.to_string(),
105 message: format!("Failed to deserialize UserProfile from binary: {}", e),
106 }
107 });
108 }
109
110 Err(SerializationError::ValidationFailed {
111 field: field_name.to_string(),
112 message: format!(
113 "Expected JsonObject or BinaryObject for UserProfile, found {}",
114 value.type_name()
115 ),
116 })
117 }
118}
119
120#[derive(Debug, Clone, PartialEq)]
122pub struct AppSettings {
123 pub app_name: String,
124 pub version: String,
125 pub debug_mode: bool,
126 pub max_connections: u32,
127 pub timeout_seconds: f32,
128 pub features: Vec<String>,
129 pub environment_vars: HashMap<String, String>,
130 pub optional_database_url: Option<String>,
131}
132
133impl StructSerializable for AppSettings {
134 fn to_serializer(&self) -> StructSerializer {
135 StructSerializer::new()
136 .field("app_name", &self.app_name)
137 .field("version", &self.version)
138 .field("debug_mode", &self.debug_mode)
139 .field("max_connections", &self.max_connections)
140 .field("timeout_seconds", &self.timeout_seconds)
141 .field("features", &self.features)
142 .field("environment_vars", &self.environment_vars)
143 .field("optional_database_url", &self.optional_database_url)
144 }
145
146 fn from_deserializer(deserializer: &mut StructDeserializer) -> SerializationResult<Self> {
147 let app_name = deserializer.field("app_name")?;
148 let version = deserializer.field("version")?;
149 let debug_mode = deserializer.field("debug_mode")?;
150 let max_connections = deserializer.field("max_connections")?;
151 let timeout_seconds = deserializer.field("timeout_seconds")?;
152 let features = deserializer.field("features")?;
153 let environment_vars = deserializer.field("environment_vars")?;
154 let optional_database_url = deserializer.field("optional_database_url")?;
155
156 Ok(AppSettings {
157 app_name,
158 version,
159 debug_mode,
160 max_connections,
161 timeout_seconds,
162 features,
163 environment_vars,
164 optional_database_url,
165 })
166 }
167}
168
169impl ToFieldValue for AppSettings {
170 fn to_field_value(&self) -> FieldValue {
171 match self.to_json() {
173 Ok(json_str) => FieldValue::from_json_object(json_str),
174 Err(_) => FieldValue::from_string("serialization_error".to_string()),
175 }
176 }
177}
178
179impl FromFieldValue for AppSettings {
180 fn from_field_value(value: FieldValue, field_name: &str) -> SerializationResult<Self> {
181 if let Ok(json_data) = value.as_json_object() {
183 return Self::from_json(json_data).map_err(|e| SerializationError::ValidationFailed {
184 field: field_name.to_string(),
185 message: format!("Failed to deserialize AppSettings from JSON: {}", e),
186 });
187 }
188
189 if let Ok(binary_data) = value.as_binary_object() {
191 return Self::from_binary(binary_data).map_err(|e| {
192 SerializationError::ValidationFailed {
193 field: field_name.to_string(),
194 message: format!("Failed to deserialize AppSettings from binary: {}", e),
195 }
196 });
197 }
198
199 Err(SerializationError::ValidationFailed {
200 field: field_name.to_string(),
201 message: format!(
202 "Expected JsonObject or BinaryObject for AppSettings, found {}",
203 value.type_name()
204 ),
205 })
206 }
207}
208
209fn main() -> Result<(), Box<dyn std::error::Error>> {
210 println!("=== Basic Struct Serialization Example ===\n");
211
212 demonstrate_user_profile_serialization()?;
213 demonstrate_app_settings_serialization()?;
214 demonstrate_format_comparison()?;
215 demonstrate_roundtrip_verification()?;
216 demonstrate_field_access_patterns()?;
217 cleanup_temp_files()?;
218
219 println!("\n=== Example completed successfully! ===");
220 Ok(())
221}
222
223fn demonstrate_user_profile_serialization() -> Result<(), Box<dyn std::error::Error>> {
225 println!("--- User Profile Serialization ---");
226
227 let user = UserProfile {
229 id: 12345,
230 username: "alice_cooper".to_string(),
231 email: "alice@example.com".to_string(),
232 age: 28,
233 is_active: true,
234 score: 95.7,
235 };
236
237 println!("Original user profile:");
238 println!(" ID: {}", user.id);
239 println!(" Username: {}", user.username);
240 println!(" Email: {}", user.email);
241 println!(" Age: {}", user.age);
242 println!(" Active: {}", user.is_active);
243 println!(" Score: {}", user.score);
244
245 let json_data = user.to_json()?;
247 println!("\nSerialized to JSON:");
248 println!("{}", json_data);
249
250 user.save_json("temp_user_profile.json")?;
252 println!("Saved to file: temp_user_profile.json");
253
254 let loaded_user = UserProfile::load_json("temp_user_profile.json")?;
256 println!("\nLoaded user profile:");
257 println!(" ID: {}", loaded_user.id);
258 println!(" Username: {}", loaded_user.username);
259 println!(" Email: {}", loaded_user.email);
260 println!(" Age: {}", loaded_user.age);
261 println!(" Active: {}", loaded_user.is_active);
262 println!(" Score: {}", loaded_user.score);
263
264 assert_eq!(user, loaded_user);
266 println!("Data integrity verification: PASSED");
267
268 Ok(())
269}
270
271fn demonstrate_app_settings_serialization() -> Result<(), Box<dyn std::error::Error>> {
273 println!("\n--- App Settings Serialization ---");
274
275 let mut env_vars = HashMap::new();
277 env_vars.insert("LOG_LEVEL".to_string(), "info".to_string());
278 env_vars.insert("PORT".to_string(), "8080".to_string());
279 env_vars.insert("HOST".to_string(), "localhost".to_string());
280
281 let settings = AppSettings {
282 app_name: "Train Station Example".to_string(),
283 version: "1.0.0".to_string(),
284 debug_mode: true,
285 max_connections: 100,
286 timeout_seconds: 30.5,
287 features: vec![
288 "authentication".to_string(),
289 "logging".to_string(),
290 "metrics".to_string(),
291 ],
292 environment_vars: env_vars,
293 optional_database_url: Some("postgresql://localhost:5432/mydb".to_string()),
294 };
295
296 println!("Original app settings:");
297 println!(" App Name: {}", settings.app_name);
298 println!(" Version: {}", settings.version);
299 println!(" Debug Mode: {}", settings.debug_mode);
300 println!(" Max Connections: {}", settings.max_connections);
301 println!(" Timeout: {} seconds", settings.timeout_seconds);
302 println!(" Features: {:?}", settings.features);
303 println!(" Environment Variables: {:?}", settings.environment_vars);
304 println!(" Database URL: {:?}", settings.optional_database_url);
305
306 let binary_data = settings.to_binary()?;
308 println!("\nSerialized to binary: {} bytes", binary_data.len());
309
310 settings.save_binary("temp_app_settings.bin")?;
312 println!("Saved to file: temp_app_settings.bin");
313
314 let loaded_settings = AppSettings::load_binary("temp_app_settings.bin")?;
316 println!("\nLoaded app settings:");
317 println!(" App Name: {}", loaded_settings.app_name);
318 println!(" Version: {}", loaded_settings.version);
319 println!(" Debug Mode: {}", loaded_settings.debug_mode);
320 println!(" Features count: {}", loaded_settings.features.len());
321 println!(
322 " Environment variables count: {}",
323 loaded_settings.environment_vars.len()
324 );
325
326 assert_eq!(settings, loaded_settings);
328 println!("Data integrity verification: PASSED");
329
330 Ok(())
331}
332
333fn demonstrate_format_comparison() -> Result<(), Box<dyn std::error::Error>> {
335 println!("\n--- Format Comparison ---");
336
337 let user = UserProfile {
338 id: 98765,
339 username: "bob_builder".to_string(),
340 email: "bob@construction.com".to_string(),
341 age: 35,
342 is_active: false,
343 score: 87.2,
344 };
345
346 user.save_json("temp_format_comparison.json")?;
348 user.save_binary("temp_format_comparison.bin")?;
349
350 let json_size = fs::metadata("temp_format_comparison.json")?.len();
352 let binary_size = fs::metadata("temp_format_comparison.bin")?.len();
353
354 println!("Format comparison for UserProfile:");
355 println!(" JSON file size: {} bytes", json_size);
356 println!(" Binary file size: {} bytes", binary_size);
357 println!(
358 " Size ratio (JSON/Binary): {:.2}x",
359 json_size as f64 / binary_size as f64
360 );
361
362 let json_content = fs::read_to_string("temp_format_comparison.json")?;
364 println!("\nJSON format (human-readable):");
365 println!("{}", json_content);
366
367 println!("\nBinary format (first 32 bytes as hex):");
368 let binary_content = fs::read("temp_format_comparison.bin")?;
369 for (i, byte) in binary_content.iter().take(32).enumerate() {
370 if i % 16 == 0 && i > 0 {
371 println!();
372 }
373 print!("{:02x} ", byte);
374 }
375 println!("\n... ({} total bytes)", binary_content.len());
376
377 let json_loaded = UserProfile::load_json("temp_format_comparison.json")?;
379 let binary_loaded = UserProfile::load_binary("temp_format_comparison.bin")?;
380
381 assert_eq!(json_loaded, binary_loaded);
382 println!("\nFormat consistency verification: PASSED");
383
384 Ok(())
385}
386
387fn demonstrate_roundtrip_verification() -> Result<(), Box<dyn std::error::Error>> {
389 println!("\n--- Roundtrip Verification ---");
390
391 let test_users = [
393 UserProfile {
394 id: 0,
395 username: "".to_string(),
396 email: "empty@test.com".to_string(),
397 age: 0,
398 is_active: false,
399 score: 0.0,
400 },
401 UserProfile {
402 id: u32::MAX,
403 username: "maximal_user_with_very_long_name_123456789".to_string(),
404 email: "test@verylongdomainname.example.org".to_string(),
405 age: i32::MAX,
406 is_active: true,
407 score: 999999.5,
408 },
409 UserProfile {
410 id: 42,
411 username: "unicode_tëst_🦀".to_string(),
412 email: "unicode@tëst.com".to_string(),
413 age: 25,
414 is_active: true,
415 score: -123.456,
416 },
417 ];
418
419 println!(
420 "Testing roundtrip serialization with {} variations:",
421 test_users.len()
422 );
423
424 for (i, user) in test_users.iter().enumerate() {
425 println!(
426 " Test case {}: ID={}, Username='{}'",
427 i + 1,
428 user.id,
429 user.username
430 );
431
432 let json_data = user.to_json()?;
434 let json_parsed = UserProfile::from_json(&json_data)?;
435 assert_eq!(*user, json_parsed);
436
437 let binary_data = user.to_binary()?;
439 let binary_parsed = UserProfile::from_binary(&binary_data)?;
440 assert_eq!(*user, binary_parsed);
441
442 println!(" JSON roundtrip: PASSED");
443 println!(" Binary roundtrip: PASSED");
444 }
445
446 println!("All roundtrip tests: PASSED");
447
448 Ok(())
449}
450
451fn demonstrate_field_access_patterns() -> Result<(), Box<dyn std::error::Error>> {
453 println!("\n--- Field Access Patterns ---");
454
455 let settings = AppSettings {
456 app_name: "Field Test App".to_string(),
457 version: "2.1.0".to_string(),
458 debug_mode: false,
459 max_connections: 50,
460 timeout_seconds: 15.0,
461 features: vec!["basic".to_string(), "advanced".to_string()],
462 environment_vars: HashMap::new(),
463 optional_database_url: None,
464 };
465
466 let json_data = settings.to_json()?;
468 println!("JSON structure for field inspection:");
469
470 let field_count = json_data.matches(':').count();
472 println!("Estimated fields: {}", field_count);
473
474 let lines: Vec<&str> = json_data.lines().take(5).collect();
476 for line in lines {
477 println!(" {}", line.trim());
478 }
479 if json_data.lines().count() > 5 {
480 println!(" ... ({} more lines)", json_data.lines().count() - 5);
481 }
482
483 println!("\nOptional field handling:");
485 println!(
486 " Database URL is None: {}",
487 settings.optional_database_url.is_none()
488 );
489
490 let settings_with_db = AppSettings {
492 optional_database_url: Some("sqlite:///tmp/test.db".to_string()),
493 ..settings.clone()
494 };
495
496 println!(
497 " Database URL with value: {:?}",
498 settings_with_db.optional_database_url
499 );
500
501 let json_none = settings.to_json()?;
503 let json_some = settings_with_db.to_json()?;
504
505 let parsed_none = AppSettings::from_json(&json_none)?;
506 let parsed_some = AppSettings::from_json(&json_some)?;
507
508 assert_eq!(settings, parsed_none);
509 assert_eq!(settings_with_db, parsed_some);
510 assert!(parsed_none.optional_database_url.is_none());
511 assert!(parsed_some.optional_database_url.is_some());
512
513 println!("Optional field serialization: PASSED");
514
515 Ok(())
516}
517
518fn cleanup_temp_files() -> Result<(), Box<dyn std::error::Error>> {
520 println!("\n--- Cleanup ---");
521
522 let files_to_remove = [
523 "temp_user_profile.json",
524 "temp_app_settings.bin",
525 "temp_format_comparison.json",
526 "temp_format_comparison.bin",
527 ];
528
529 for file in &files_to_remove {
530 if fs::metadata(file).is_ok() {
531 fs::remove_file(file)?;
532 println!("Removed: {}", file);
533 }
534 }
535
536 println!("Cleanup completed");
537 Ok(())
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543
544 #[test]
545 fn test_user_profile_serialization() {
546 let user = UserProfile {
547 id: 123,
548 username: "test_user".to_string(),
549 email: "test@example.com".to_string(),
550 age: 30,
551 is_active: true,
552 score: 88.5,
553 };
554
555 let json_data = user.to_json().unwrap();
557 let parsed_user = UserProfile::from_json(&json_data).unwrap();
558 assert_eq!(user, parsed_user);
559
560 let binary_data = user.to_binary().unwrap();
562 let parsed_user = UserProfile::from_binary(&binary_data).unwrap();
563 assert_eq!(user, parsed_user);
564 }
565
566 #[test]
567 fn test_app_settings_serialization() {
568 let mut env_vars = HashMap::new();
569 env_vars.insert("TEST".to_string(), "value".to_string());
570
571 let settings = AppSettings {
572 app_name: "Test App".to_string(),
573 version: "1.0.0".to_string(),
574 debug_mode: true,
575 max_connections: 10,
576 timeout_seconds: 5.0,
577 features: vec!["test".to_string()],
578 environment_vars: env_vars,
579 optional_database_url: Some("test://db".to_string()),
580 };
581
582 let json_data = settings.to_json().unwrap();
584 let parsed_settings = AppSettings::from_json(&json_data).unwrap();
585 assert_eq!(settings, parsed_settings);
586
587 let binary_data = settings.to_binary().unwrap();
589 let parsed_settings = AppSettings::from_binary(&binary_data).unwrap();
590 assert_eq!(settings, parsed_settings);
591 }
592
593 #[test]
594 fn test_optional_field_handling() {
595 let settings_none = AppSettings {
596 app_name: "Test".to_string(),
597 version: "1.0.0".to_string(),
598 debug_mode: false,
599 max_connections: 1,
600 timeout_seconds: 1.0,
601 features: vec![],
602 environment_vars: HashMap::new(),
603 optional_database_url: None,
604 };
605
606 let settings_some = AppSettings {
607 optional_database_url: Some("db://test".to_string()),
608 ..settings_none.clone()
609 };
610
611 let json_none = settings_none.to_json().unwrap();
613 let json_some = settings_some.to_json().unwrap();
614
615 let parsed_none = AppSettings::from_json(&json_none).unwrap();
616 let parsed_some = AppSettings::from_json(&json_some).unwrap();
617
618 assert!(parsed_none.optional_database_url.is_none());
619 assert!(parsed_some.optional_database_url.is_some());
620 assert_eq!(settings_none, parsed_none);
621 assert_eq!(settings_some, parsed_some);
622 }
623}