batch_mode_batch_metadata/
metadata.rs

1// ---------------- [ File: batch-mode-batch-metadata/src/metadata.rs ]
2crate::ix!();
3
4#[derive(Builder,Debug,Clone,Serialize,Deserialize)]
5#[builder(setter(into))]
6pub struct BatchMetadata {
7    batch_id:       String,
8    input_file_id:  String,
9
10    #[builder(default)]
11    output_file_id: Option<String>,
12
13    #[builder(default)]
14    error_file_id:  Option<String>,
15}
16
17impl BatchMetadata {
18
19    pub fn with_input_id_and_batch_id(input_id: &str, batch_id: &str) -> Self {
20        Self {
21            batch_id:       batch_id.to_string(),
22            input_file_id:  input_id.to_string(),
23            output_file_id: None,
24            error_file_id:  None,
25        }
26    }
27
28    pub fn batch_id(&self) -> &str {
29        &self.batch_id
30    }
31
32    pub fn input_file_id(&self) -> &str {
33        self.input_file_id.as_ref()
34    }
35
36    pub fn output_file_id(&self) -> Result<&str,BatchMetadataError> {
37        let output_file_id = self.output_file_id.as_ref()
38            .ok_or(BatchMetadataError::MissingOutputFileId)?;
39        Ok(output_file_id)
40    }
41
42    pub fn error_file_id(&self) -> Result<&str,BatchMetadataError> {
43        let error_file_id = self.error_file_id.as_ref()
44            .ok_or(BatchMetadataError::MissingErrorFileId)?;
45        Ok(error_file_id)
46    }
47
48    pub fn set_output_file_id(&mut self, new_id: Option<String>) {
49        self.output_file_id = new_id;
50    }
51
52    pub fn set_error_file_id(&mut self, new_id: Option<String>) {
53        self.error_file_id = new_id;
54    }
55}
56
57#[async_trait]
58impl SaveToFile for BatchMetadata {
59
60    type Error = BatchMetadataError;
61
62    async fn save_to_file(
63        &self,
64        metadata_filename: impl AsRef<Path> + Send,
65
66    ) -> Result<(), Self::Error> {
67
68        info!("saving batch metadata to file {:?}", metadata_filename.as_ref());
69
70        let metadata_json = serde_json::to_string(&self)?;
71
72        std::fs::write(metadata_filename, metadata_json)?;
73
74        Ok(())
75    }
76}
77
78#[async_trait]
79impl LoadFromFile for BatchMetadata {
80
81    type Error = BatchMetadataError;
82
83    async fn load_from_file(metadata_filename: impl AsRef<Path> + Send) 
84        -> Result<Self, Self::Error> 
85    {
86        info!("loading batch metadata from file {:?}", metadata_filename.as_ref());
87
88        let metadata_json = std::fs::read_to_string(metadata_filename)?;
89        let metadata      = serde_json::from_str(&metadata_json)?;
90
91        Ok(metadata)
92    }
93}
94
95#[cfg(test)]
96mod batch_metadata_exhaustive_tests {
97    use super::*;
98
99    #[traced_test]
100    fn with_input_id_and_batch_id_sets_expected_fields() {
101        trace!("===== BEGIN TEST: with_input_id_and_batch_id_sets_expected_fields =====");
102        let input_id = "some_input_id";
103        let batch_id = "some_batch_id";
104        let metadata = BatchMetadata::with_input_id_and_batch_id(input_id, batch_id);
105        debug!("Constructed metadata: {:?}", metadata);
106
107        pretty_assert_eq!(
108            metadata.batch_id(),
109            batch_id,
110            "batch_id should match the provided value"
111        );
112        pretty_assert_eq!(
113            metadata.input_file_id(),
114            input_id,
115            "input_file_id should match the provided value"
116        );
117        assert!(metadata.output_file_id.is_none(), "output_file_id should be None initially");
118        assert!(metadata.error_file_id.is_none(), "error_file_id should be None initially");
119
120        trace!("===== END TEST: with_input_id_and_batch_id_sets_expected_fields =====");
121    }
122
123    #[traced_test]
124    fn set_output_file_id_and_retrieve_it_successfully() {
125        trace!("===== BEGIN TEST: set_output_file_id_and_retrieve_it_successfully =====");
126        let mut metadata = BatchMetadata::with_input_id_and_batch_id("input_1", "batch_1");
127        let new_output_id = Some("output_file_xyz".to_string());
128        trace!("Assigning output_file_id={:?}", new_output_id);
129        metadata.set_output_file_id(new_output_id);
130
131        match metadata.output_file_id() {
132            Ok(id) => {
133                debug!("Retrieved output_file_id: {}", id);
134                pretty_assert_eq!(id, "output_file_xyz");
135            },
136            Err(_) => {
137                error!("Expected output_file_id to be set, but got an error");
138                panic!("Mismatch in output_file_id retrieval");
139            }
140        }
141        trace!("===== END TEST: set_output_file_id_and_retrieve_it_successfully =====");
142    }
143
144    #[traced_test]
145    fn set_output_file_id_to_none_and_verify_error() {
146        trace!("===== BEGIN TEST: set_output_file_id_to_none_and_verify_error =====");
147        let mut metadata = BatchMetadata::with_input_id_and_batch_id("input_2", "batch_2");
148        metadata.set_output_file_id(None);
149        trace!("Set output_file_id to None");
150
151        let result = metadata.output_file_id();
152        debug!("Attempting to retrieve output_file_id -> {:?}", result);
153        assert!(result.is_err(), "Should fail when output_file_id is None");
154        trace!("===== END TEST: set_output_file_id_to_none_and_verify_error =====");
155    }
156
157    #[traced_test]
158    fn set_error_file_id_and_retrieve_it_successfully() {
159        trace!("===== BEGIN TEST: set_error_file_id_and_retrieve_it_successfully =====");
160        let mut metadata = BatchMetadata::with_input_id_and_batch_id("input_3", "batch_3");
161        let new_error_id = Some("error_file_abc".to_string());
162        trace!("Assigning error_file_id={:?}", new_error_id);
163        metadata.set_error_file_id(new_error_id);
164
165        match metadata.error_file_id() {
166            Ok(id) => {
167                debug!("Retrieved error_file_id: {}", id);
168                pretty_assert_eq!(id, "error_file_abc");
169            },
170            Err(_) => {
171                error!("Expected error_file_id to be set, but got an error");
172                panic!("Mismatch in error_file_id retrieval");
173            }
174        }
175        trace!("===== END TEST: set_error_file_id_and_retrieve_it_successfully =====");
176    }
177
178    #[traced_test]
179    fn set_error_file_id_to_none_and_verify_error() {
180        trace!("===== BEGIN TEST: set_error_file_id_to_none_and_verify_error =====");
181        let mut metadata = BatchMetadata::with_input_id_and_batch_id("input_4", "batch_4");
182        metadata.set_error_file_id(None);
183        trace!("Set error_file_id to None");
184
185        let result = metadata.error_file_id();
186        debug!("Attempting to retrieve error_file_id -> {:?}", result);
187        assert!(result.is_err(), "Should fail when error_file_id is None");
188        trace!("===== END TEST: set_error_file_id_to_none_and_verify_error =====");
189    }
190
191    #[traced_test]
192    async fn save_to_file_and_load_from_file_round_trip() -> Result<(),BatchMetadataError> {
193
194        trace!("===== BEGIN TEST: save_to_file_and_load_from_file_round_trip =====");
195
196        // Arrange
197        let temp_dir = std::env::temp_dir();
198        let filename = temp_dir.join("test_batch_metadata.json");
199        debug!("Temporary file for metadata: {:?}", filename);
200
201        let mut original = BatchMetadata::with_input_id_and_batch_id("in_10", "batch_10");
202        original.set_output_file_id(Some("out_10".into()));
203        original.set_error_file_id(Some("err_10".into()));
204        trace!("Original metadata: {:?}", original);
205
206        // Act: save to file
207        let save_res = original.save_to_file(&filename).await;
208        debug!("save_to_file result: {:?}", save_res);
209        assert!(save_res.is_ok(), "Saving metadata should succeed");
210
211        // Assert: load from file
212        let loaded = BatchMetadata::load_from_file(&filename).await
213            .expect("Loading metadata from file should succeed");
214        debug!("Loaded metadata: {:?}", loaded);
215
216        // Clean up
217        if let Err(e) = fs::remove_file(&filename).await {
218            warn!("Failed to remove temp file: {:?}", e);
219        }
220
221        // Compare fields
222        pretty_assert_eq!(loaded.batch_id(), original.batch_id());
223        pretty_assert_eq!(loaded.input_file_id(), original.input_file_id());
224        // We unwrap or assert on these, because we set them
225        pretty_assert_eq!(loaded.output_file_id().unwrap(), original.output_file_id().unwrap());
226        pretty_assert_eq!(loaded.error_file_id().unwrap(), original.error_file_id().unwrap());
227
228        trace!("===== END TEST: save_to_file_and_load_from_file_round_trip =====");
229        Ok(())
230    }
231}