save_load_traits/
impl_for_string.rs1crate::ix!();
3
4#[async_trait]
5impl SaveToFile for String {
6 type Error = SaveLoadError;
7
8 async fn save_to_file(
9 &self,
10 filename: impl AsRef<Path> + Send
11 ) -> Result<(), Self::Error> {
12 let json_data = serde_json::to_string_pretty(&self)
13 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
14 let mut file = File::create(filename).await?;
15 file.write_all(json_data.as_bytes()).await?;
16 Ok(())
17 }
18}
19
20#[async_trait]
22impl LoadFromFile for String {
23 type Error = SaveLoadError;
24
25 async fn load_from_file(filename: impl AsRef<Path> + Send)
26 -> Result<Self, Self::Error>
27 {
28 let mut file = File::open(filename).await?;
29 let mut buf = Vec::new();
30 file.read_to_end(&mut buf).await?;
31 let string: String =
32 serde_json::from_slice(&buf)
33 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
34 Ok(string)
35 }
36}
37
38#[cfg(test)]
39mod string_file_persistence_behavior {
40 use super::*;
41 use tracing::{info, debug, trace};
42 use traced_test::traced_test;
43 use tempfile::NamedTempFile;
44 use std::path::Path;
45
46 #[traced_test]
47 async fn roundtrip_save_and_load_preserves_exact_string() {
48 let original = String::from("Hello, world!");
49 info!("Testing roundtrip save and load for string: {}", original);
50 let tmp = NamedTempFile::new().expect("unable to create temporary file");
51 let path = tmp.path().to_path_buf();
52 trace!("Saving string to file at {:?}", path);
53 original.save_to_file(&path).await.expect("save_to_file failed");
54 trace!("Loading string from file at {:?}", path);
55 let loaded = String::load_from_file(&path).await.expect("load_from_file failed");
56 info!("Loaded string: {}", loaded);
57 assert_eq!(loaded, original);
58 }
59
60 #[traced_test]
61 async fn save_creates_file_with_pretty_json_representation() {
62 let original = String::from("Test JSON");
63 debug!("Original string for JSON formatting test: {}", original);
64 let tmp = NamedTempFile::new().expect("unable to create temporary file");
65 let path = tmp.path().to_path_buf();
66 original.save_to_file(&path).await.expect("save_to_file failed");
67 let content = tokio::fs::read_to_string(&path).await.expect("failed to read file");
68 let expected = serde_json::to_string_pretty(&original).expect("serde_json formatting failed");
69 debug!("File content: {}", content);
70 assert_eq!(content, expected);
71 }
72
73 #[traced_test]
74 async fn load_from_nonexistent_path_returns_not_found_error() {
75 let nonexistent = Path::new("unlikely_to_exist_12345.json");
76 let result = String::load_from_file(&nonexistent).await;
77 assert!(result.is_err(), "Expected error for nonexistent file");
78 let err = result.unwrap_err();
79 debug!("Received error: {:?}", err);
80 }
81
82 #[traced_test]
83 async fn load_with_invalid_json_data_returns_invalid_data_error() {
84 let tmp = NamedTempFile::new().expect("unable to create temporary file");
85 let path = tmp.path().to_path_buf();
86 tokio::fs::write(&path, b"not a valid json").await.expect("failed to write invalid data");
88 let result = String::load_from_file(&path).await;
89 assert!(result.is_err(), "Expected error for invalid JSON data");
90 let err = result.unwrap_err();
91 debug!("Received error: {:?}", err);
92 }
93}