Skip to main content

akar_common/
file_system.rs

1//! File system abstraction layer.
2//!
3//! Provides a unified interface for reading/writing data across
4//! local filesystem, HTTP, and other backends.
5
6use std::io::{Read, Seek, Write};
7use std::path::Path;
8
9/// A generic file system interface.
10pub trait FileSystem: Send + Sync {
11    /// Check if this file system can handle the given path/URL.
12    fn can_handle(&self, path: &str) -> bool;
13
14    /// Open a file for reading.
15    fn open_read(&self, path: &str) -> std::io::Result<Box<dyn FileRead>>;
16
17    /// Open a file for writing (creates or truncates).
18    fn open_write(&self, path: &str) -> std::io::Result<Box<dyn FileWrite>>;
19
20    /// Check if a path exists.
21    fn exists(&self, path: &str) -> bool;
22
23    /// Remove a file.
24    fn remove(&self, path: &str) -> std::io::Result<()>;
25
26    /// Create a directory and all parents.
27    fn create_dir_all(&self, path: &str) -> std::io::Result<()>;
28}
29
30/// A readable file handle.
31pub trait FileRead: Read + Seek + Send {}
32
33/// A writable file handle.
34pub trait FileWrite: Write + Seek + Send {}
35
36/// Local filesystem implementation.
37#[derive(Default)]
38pub struct LocalFileSystem;
39
40impl FileSystem for LocalFileSystem {
41    fn can_handle(&self, _path: &str) -> bool {
42        // LocalFileSystem is the fallback/default for non-URL paths.
43        true
44    }
45
46    fn open_read(&self, path: &str) -> std::io::Result<Box<dyn FileRead>> {
47        Ok(Box::new(std::fs::File::open(Path::new(path))?))
48    }
49
50    fn open_write(&self, path: &str) -> std::io::Result<Box<dyn FileWrite>> {
51        Ok(Box::new(std::fs::File::create(Path::new(path))?))
52    }
53
54    fn exists(&self, path: &str) -> bool {
55        Path::new(path).exists()
56    }
57
58    fn remove(&self, path: &str) -> std::io::Result<()> {
59        std::fs::remove_file(Path::new(path))
60    }
61
62    fn create_dir_all(&self, path: &str) -> std::io::Result<()> {
63        std::fs::create_dir_all(Path::new(path))
64    }
65}
66
67impl FileRead for std::fs::File {}
68impl FileWrite for std::fs::File {}
69
70use std::sync::RwLock;
71
72/// Registry for virtual file systems.
73pub struct VirtualFileSystemRegistry {
74    systems: RwLock<Vec<Box<dyn FileSystem>>>,
75    default_fs: LocalFileSystem,
76}
77
78impl Default for VirtualFileSystemRegistry {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84impl VirtualFileSystemRegistry {
85    pub fn new() -> Self {
86        Self {
87            systems: RwLock::new(Vec::new()),
88            default_fs: LocalFileSystem,
89        }
90    }
91
92    pub fn register_file_system(&self, fs: Box<dyn FileSystem>) {
93        let mut systems = self.systems.write().unwrap();
94        systems.push(fs);
95    }
96
97    pub fn open_read(&self, path: &str) -> std::io::Result<Box<dyn FileRead>> {
98        let systems = self.systems.read().unwrap();
99        for fs in systems.iter() {
100            if fs.can_handle(path) {
101                return fs.open_read(path);
102            }
103        }
104        self.default_fs.open_read(path)
105    }
106
107    pub fn open_write(&self, path: &str) -> std::io::Result<Box<dyn FileWrite>> {
108        let systems = self.systems.read().unwrap();
109        for fs in systems.iter() {
110            if fs.can_handle(path) {
111                return fs.open_write(path);
112            }
113        }
114        self.default_fs.open_write(path)
115    }
116
117    pub fn exists(&self, path: &str) -> bool {
118        let systems = self.systems.read().unwrap();
119        for fs in systems.iter() {
120            if fs.can_handle(path) {
121                return fs.exists(path);
122            }
123        }
124        self.default_fs.exists(path)
125    }
126
127    pub fn remove(&self, path: &str) -> std::io::Result<()> {
128        let systems = self.systems.read().unwrap();
129        for fs in systems.iter() {
130            if fs.can_handle(path) {
131                return fs.remove(path);
132            }
133        }
134        self.default_fs.remove(path)
135    }
136
137    pub fn create_dir_all(&self, path: &str) -> std::io::Result<()> {
138        let systems = self.systems.read().unwrap();
139        for fs in systems.iter() {
140            if fs.can_handle(path) {
141                return fs.create_dir_all(path);
142            }
143        }
144        self.default_fs.create_dir_all(path)
145    }
146}