Skip to main content

akar_common/
gzip_file_system.rs

1use crate::file_system::{FileRead, FileSystem, FileWrite};
2use flate2::Compression;
3use flate2::read::GzDecoder;
4use flate2::write::GzEncoder;
5use std::io::{Read, Seek, Write};
6
7/// A virtual file system that transparently compresses/decompresses
8/// files whose path ends in `.gz`.
9///
10/// Delegates actual I/O to an inner `FileSystem` (typically
11/// [`LocalFileSystem`](crate::file_system::LocalFileSystem)) and wraps
12/// the resulting reader/writer with gzip streaming.
13///
14/// # Example
15///
16/// ```no_run
17/// use akar_common::file_system::{LocalFileSystem, VirtualFileSystemRegistry};
18/// use akar_common::gzip_file_system::GzipFileSystem;
19///
20/// let vfs = VirtualFileSystemRegistry::new();
21/// vfs.register_file_system(Box::new(GzipFileSystem::new(Box::new(LocalFileSystem))));
22/// ```
23pub struct GzipFileSystem {
24    inner: Box<dyn FileSystem>,
25}
26
27impl GzipFileSystem {
28    /// Create a gzip-wrapping file system over the given inner FS.
29    pub fn new(inner: Box<dyn FileSystem>) -> Self {
30        Self { inner }
31    }
32}
33
34impl FileSystem for GzipFileSystem {
35    fn can_handle(&self, path: &str) -> bool {
36        path.ends_with(".gz")
37    }
38
39    fn open_read(&self, path: &str) -> std::io::Result<Box<dyn FileRead>> {
40        let inner_reader = self.inner.open_read(path)?;
41        let decoder = GzDecoder::new(inner_reader);
42        Ok(Box::new(GzipFileRead(decoder)))
43    }
44
45    fn open_write(&self, path: &str) -> std::io::Result<Box<dyn FileWrite>> {
46        let inner_writer = self.inner.open_write(path)?;
47        let encoder = GzEncoder::new(inner_writer, Compression::default());
48        Ok(Box::new(GzipFileWrite(encoder)))
49    }
50
51    fn exists(&self, path: &str) -> bool {
52        self.inner.exists(path)
53    }
54
55    fn remove(&self, path: &str) -> std::io::Result<()> {
56        self.inner.remove(path)
57    }
58
59    fn create_dir_all(&self, path: &str) -> std::io::Result<()> {
60        self.inner.create_dir_all(path)
61    }
62}
63
64/// Wraps a `GzDecoder` so it implements `FileRead`.
65struct GzipFileRead(GzDecoder<Box<dyn FileRead>>);
66
67impl Read for GzipFileRead {
68    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
69        self.0.read(buf)
70    }
71}
72
73impl Seek for GzipFileRead {
74    fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
75        Err(std::io::Error::new(
76            std::io::ErrorKind::Unsupported,
77            "gzip does not support seeking",
78        ))
79    }
80}
81
82impl FileRead for GzipFileRead {}
83
84/// Wraps a `GzEncoder` so it implements `FileWrite`.
85struct GzipFileWrite(GzEncoder<Box<dyn FileWrite>>);
86
87impl Write for GzipFileWrite {
88    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
89        self.0.write(buf)
90    }
91    fn flush(&mut self) -> std::io::Result<()> {
92        self.0.flush()
93    }
94}
95
96impl Seek for GzipFileWrite {
97    fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
98        Err(std::io::Error::new(
99            std::io::ErrorKind::Unsupported,
100            "gzip does not support seeking",
101        ))
102    }
103}
104
105impl FileWrite for GzipFileWrite {}