Skip to main content

serializer/machine/
blocking.rs

1//! Blocking I/O fallback implementation
2//!
3//! This module provides a standard blocking I/O implementation that works
4//! on all platforms. It's used as a fallback when platform-specific async
5//! I/O is not available.
6
7use std::fs;
8use std::io;
9use std::path::Path;
10
11use super::AsyncFileIO;
12
13/// Blocking I/O implementation using standard library
14///
15/// This is the fallback implementation that works on all platforms.
16/// It uses synchronous file operations from `std::fs`.
17#[derive(Debug, Clone, Copy, Default)]
18pub struct BlockingIO;
19
20impl BlockingIO {
21    /// Create a new blocking I/O instance
22    pub fn new() -> Self {
23        Self
24    }
25}
26
27impl AsyncFileIO for BlockingIO {
28    fn read_sync(&self, path: &Path) -> io::Result<Vec<u8>> {
29        fs::read(path)
30    }
31
32    fn write_sync(&self, path: &Path, data: &[u8]) -> io::Result<()> {
33        // Ensure parent directory exists
34        if let Some(parent) = path.parent() {
35            if !parent.as_os_str().is_empty() && !parent.exists() {
36                fs::create_dir_all(parent)?;
37            }
38        }
39        fs::write(path, data)
40    }
41
42    fn read_batch_sync(&self, paths: &[&Path]) -> io::Result<Vec<io::Result<Vec<u8>>>> {
43        Ok(paths.iter().copied().map(fs::read).collect())
44    }
45
46    fn write_batch_sync(&self, files: &[(&Path, &[u8])]) -> io::Result<Vec<io::Result<()>>> {
47        Ok(files
48            .iter()
49            .map(|(path, data)| {
50                // Ensure parent directory exists
51                if let Some(parent) = path.parent() {
52                    if !parent.as_os_str().is_empty() && !parent.exists() {
53                        fs::create_dir_all(parent)?;
54                    }
55                }
56                fs::write(path, data)
57            })
58            .collect())
59    }
60
61    fn backend_name(&self) -> &'static str {
62        "blocking"
63    }
64
65    fn is_available(&self) -> bool {
66        true // Always available
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    #[cfg(test)]
74    // use std::io::Write;
75    use tempfile::TempDir;
76
77    #[test]
78    fn test_blocking_read_write() {
79        let dir = TempDir::new().unwrap();
80        let path = dir.path().join("test.txt");
81        let io = BlockingIO::new();
82
83        let data = b"Hello, World!";
84        io.write_sync(&path, data).unwrap();
85
86        let read_data = io.read_sync(&path).unwrap();
87        assert_eq!(read_data, data);
88    }
89
90    #[test]
91    fn test_blocking_batch_operations() {
92        let dir = TempDir::new().unwrap();
93        let io = BlockingIO::new();
94
95        // Write batch
96        let files: Vec<_> = (0..3)
97            .map(|i| {
98                let path = dir.path().join(format!("file{}.txt", i));
99                let data = format!("Content {}", i);
100                (path, data)
101            })
102            .collect();
103
104        let write_refs: Vec<_> = files
105            .iter()
106            .map(|(p, d)| (p.as_path(), d.as_bytes()))
107            .collect();
108        let write_results = io.write_batch_sync(&write_refs).unwrap();
109        assert!(write_results.iter().all(|r| r.is_ok()));
110
111        // Read batch
112        let paths: Vec<_> = files.iter().map(|(p, _)| p.as_path()).collect();
113        let read_results = io.read_batch_sync(&paths).unwrap();
114
115        for (i, result) in read_results.iter().enumerate() {
116            let data = result.as_ref().unwrap();
117            assert_eq!(String::from_utf8_lossy(data), format!("Content {}", i));
118        }
119    }
120
121    #[test]
122    fn test_blocking_creates_parent_dirs() {
123        let dir = TempDir::new().unwrap();
124        let path = dir.path().join("nested").join("dir").join("test.txt");
125        let io = BlockingIO::new();
126
127        io.write_sync(&path, b"test").unwrap();
128        assert!(path.exists());
129    }
130
131    #[test]
132    fn test_blocking_backend_name() {
133        let io = BlockingIO::new();
134        assert_eq!(io.backend_name(), "blocking");
135        assert!(io.is_available());
136    }
137}