batch_mode_batch_workspace_interface/
mock.rs

1// ---------------- [ File: batch-mode-batch-workspace-interface/src/mock.rs ]
2crate::ix!();
3
4/// A minimal item that could implement `GetTargetPathForAIExpansion`.
5/// We'll just call it `MockItem` for demonstration.
6#[derive(NamedItem,Debug, Clone)]
7pub struct MockItem {
8    pub name: String,
9}
10
11#[derive(Clone,Getters,Setters,Builder,Debug)]
12#[builder(setter(strip_option))]
13#[getset(get = "pub", set = "pub")]
14pub struct MockBatchWorkspace {
15    /// A temporary directory that is automatically
16    /// cleaned up when `MockBatchWorkspace` is dropped.
17    #[builder(default = "Arc::new(tempfile::tempdir().expect(\"Failed to create temp directory\"))")]
18    ephemeral_dir: Arc<tempfile::TempDir>,
19
20    /// The original user‐supplied subpath for “done” items. 
21    /// We now treat this as a relative subdirectory within `ephemeral_dir`.
22    #[builder(default = "PathBuf::from(\"mock_done_dir\")")]
23    done_dir: PathBuf,
24
25    #[builder(default = "PathBuf::from(\"mock_failed_json_repairs_dir\")")]
26    failed_json_repairs_dir: PathBuf,
27
28    #[builder(default = "PathBuf::from(\"mock_failed_items_dir\")")]
29    failed_items_dir: PathBuf,
30
31    #[builder(default = "PathBuf::from(\"mock_workdir\")")]
32    workdir: PathBuf,
33
34    #[builder(default = "\"text_storage_prefix\".to_string()")]
35    text_storage_prefix: String,
36
37    #[builder(default)]
38    input_ids: Vec<String>,
39
40    #[builder(default)]
41    output_ids: Vec<String>,
42
43    #[builder(default)]
44    error_ids: Vec<String>,
45
46    /// We must store a concrete `PathBuf` for the “done directory” specifically,
47    /// because its trait method returns `&PathBuf`, not `PathBuf`.
48    /// All other paths are returned by value, so they can be constructed on the fly.
49    #[builder(default = "PathBuf::new()")]
50    ephemeral_done_dir: PathBuf,
51}
52
53impl Default for MockBatchWorkspace {
54    fn default() -> Self {
55        let temp = tempfile::tempdir().expect("Could not create temp directory for MockBatchWorkspace");
56        info!("Created ephemeral directory for MockBatchWorkspace at: {:?}", temp.path());
57
58        // Pre‐compute the ephemeral “done” directory
59        // so we can return it by reference in the trait method.
60        let done_dir_path = temp.path().join("mock_done_dir");
61        if let Err(e) = std::fs::create_dir_all(&done_dir_path) {
62            warn!("Could not create ephemeral done directory: {} — continuing anyway", e);
63        }
64
65        Self {
66            ephemeral_dir: temp.into(),
67            done_dir: PathBuf::from("mock_done_dir"),
68            failed_json_repairs_dir: PathBuf::from("mock_failed_json_repairs_dir"),
69            failed_items_dir: PathBuf::from("mock_failed_items_dir"),
70            workdir: PathBuf::from("mock_workdir"),
71            text_storage_prefix: "text_storage_prefix".to_string(),
72            input_ids: vec![],
73            output_ids: vec![],
74            error_ids: vec![],
75            ephemeral_done_dir: done_dir_path,
76        }
77    }
78}
79
80impl GetInputFilenameAtIndex for MockBatchWorkspace {
81    fn input_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
82        let path = self
83            .ephemeral_dir
84            .path()
85            .join(format!("mock_input_{}.json", batch_idx));
86        trace!("Returning ephemeral input filename for batch {:?}: {:?}", batch_idx, path);
87        path
88    }
89}
90
91impl GetOutputFilenameAtIndex for MockBatchWorkspace {
92    fn output_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
93        let path = self
94            .ephemeral_dir
95            .path()
96            .join(format!("mock_output_{}.json", batch_idx));
97        trace!("Returning ephemeral output filename for batch {:?}: {:?}", batch_idx, path);
98        path
99    }
100}
101
102impl GetErrorFilenameAtIndex for MockBatchWorkspace {
103    fn error_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
104        let path = self
105            .ephemeral_dir
106            .path()
107            .join(format!("mock_error_{}.json", batch_idx));
108        trace!("Returning ephemeral error filename for batch {:?}: {:?}", batch_idx, path);
109        path
110    }
111}
112
113impl GetMetadataFilenameAtIndex for MockBatchWorkspace {
114    fn metadata_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
115        let path = self
116            .ephemeral_dir
117            .path()
118            .join(format!("mock_metadata_{}.json", batch_idx));
119        trace!("Returning ephemeral metadata filename for batch {:?}: {:?}", batch_idx, path);
120        path
121    }
122}
123
124impl GetDoneDirectory for MockBatchWorkspace {
125    fn get_done_directory(&self) -> &PathBuf {
126        trace!(
127            "Returning ephemeral done directory for mock workspace: {:?}",
128            self.ephemeral_done_dir
129        );
130        &self.ephemeral_done_dir
131    }
132}
133
134impl GetFailedJsonRepairsDir for MockBatchWorkspace {
135    fn failed_json_repairs_dir(&self) -> PathBuf {
136        let path = self.ephemeral_dir.path().join(&self.failed_json_repairs_dir);
137        trace!("Returning ephemeral failed_json_repairs_dir: {:?}", path);
138        if let Err(e) = std::fs::create_dir_all(&path) {
139            warn!("Could not create ephemeral failed_json_repairs_dir: {} — continuing anyway", e);
140        }
141        path
142    }
143}
144
145impl GetFailedItemsDir for MockBatchWorkspace {
146    fn failed_items_dir(&self) -> PathBuf {
147        let path = self.ephemeral_dir.path().join(&self.failed_items_dir);
148        trace!("Returning ephemeral failed_items_dir: {:?}", path);
149        if let Err(e) = std::fs::create_dir_all(&path) {
150            warn!("Could not create ephemeral failed_items_dir: {} — continuing anyway", e);
151        }
152        path
153    }
154}
155
156impl GetTextStoragePath for MockBatchWorkspace {
157    fn text_storage_path(&self, batch_idx: &BatchIndex) -> PathBuf {
158        let path = if self.text_storage_prefix.is_empty() {
159            self.ephemeral_dir
160                .path()
161                .join(format!("text_storage_{}.txt", batch_idx))
162        } else {
163            self.ephemeral_dir
164                .path()
165                .join(&self.text_storage_prefix)
166                .join(format!("text_storage_{}.txt", batch_idx))
167        };
168        trace!("Returning ephemeral text storage path for batch {:?}: {:?}", batch_idx, path);
169        if let Some(parent) = path.parent() {
170            if let Err(e) = std::fs::create_dir_all(parent) {
171                warn!("Could not create parent dir for text storage: {} — continuing anyway", e);
172            }
173        }
174        path
175    }
176}
177
178impl GetWorkdir for MockBatchWorkspace {
179    fn workdir(&self) -> PathBuf {
180        let path = self.ephemeral_dir.path().join(&self.workdir);
181        trace!("Returning ephemeral workdir: {:?}", path);
182        if let Err(e) = std::fs::create_dir_all(&path) {
183            warn!("Could not create ephemeral workdir: {} — continuing anyway", e);
184        }
185        path
186    }
187}
188
189impl GetTargetPath for MockBatchWorkspace {
190    type Item = Arc<dyn GetTargetPathForAIExpansion + Send + Sync + 'static>;
191
192    fn target_path(
193        &self,
194        item: &Self::Item,
195        expected_content_type: &ExpectedContentType
196    ) -> PathBuf {
197        let subdir = match expected_content_type {
198            ExpectedContentType::Json      => "json_output",
199            ExpectedContentType::PlainText => "text_output",
200            ExpectedContentType::JsonLines => "json_lines_output",
201        };
202        let base = self.workdir().join(subdir);
203        trace!("Constructing ephemeral target path for subdir {:?} and item", base);
204        item.target_path_for_ai_json_expansion(base.as_path(), expected_content_type)
205    }
206}
207
208impl BatchWorkspaceInterface for MockBatchWorkspace {}
209
210impl GetTargetDir for MockBatchWorkspace {
211
212    fn get_target_dir(&self) -> PathBuf {
213        todo!()
214    }
215}
216
217#[cfg(test)]
218mod test_mock_workspace_ephemeral {
219    use super::*;
220
221    #[traced_test]
222    fn test_ephemeral_cleanup() {
223        let ephemeral_done: PathBuf;
224        {
225            let workspace = MockBatchWorkspace::default();
226            ephemeral_done = workspace.get_done_directory().to_path_buf();
227            assert!(
228                !ephemeral_done.as_os_str().is_empty(),
229                "Ephemeral done directory path should not be empty."
230            );
231            // Make sure it exists
232            assert!(
233                ephemeral_done.exists(),
234                "Expected ephemeral directory to exist."
235            );
236        }
237        // After going out of scope, the temp dir should be destroyed.
238        // The done directory path should no longer exist.
239        assert!(
240            !ephemeral_done.exists(),
241            "Ephemeral directory should have been cleaned up."
242        );
243    }
244
245    #[traced_test]
246    fn test_mock_workspace_interface() {
247        let w = MockBatchWorkspace::default();
248        // We only test to the interface:
249        let done = w.get_done_directory();
250        let fail_json = w.failed_json_repairs_dir();
251        let fail_items = w.failed_items_dir();
252        let input = w.input_filename(&BatchIndex::from(0));
253        let output = w.output_filename(&BatchIndex::from(1));
254        let error = w.error_filename(&BatchIndex::from(2));
255        let meta = w.metadata_filename(&BatchIndex::from(3));
256        let text = w.text_storage_path(&BatchIndex::from(4));
257        let wd = w.workdir();
258
259        assert!(!done.as_os_str().is_empty());
260        assert!(!fail_json.as_os_str().is_empty());
261        assert!(!fail_items.as_os_str().is_empty());
262        assert!(!input.as_os_str().is_empty());
263        assert!(!output.as_os_str().is_empty());
264        assert!(!error.as_os_str().is_empty());
265        assert!(!meta.as_os_str().is_empty());
266        assert!(!text.as_os_str().is_empty());
267        assert!(!wd.as_os_str().is_empty());
268    }
269}