Skip to main content

extendable_assets/filesystem/
native.rs

1use std::path::{Path, PathBuf};
2
3use crate::filesystem::{Filesystem, FilesystemError};
4
5use async_trait::async_trait;
6
7/// A filesystem implementation that reads from the native OS filesystem.
8///
9/// This implementation provides async access to files on the local filesystem,
10/// with all asset paths resolved relative to a configured root directory.
11/// Currently uses blocking I/O operations wrapped in async functions.
12pub struct NativeFilesystem {
13    /// Root directory where all asset paths are resolved relative to
14    root_dir: PathBuf,
15}
16impl NativeFilesystem {
17    /// Creates a new native filesystem with the specified root directory.
18    ///
19    /// # Arguments
20    ///
21    /// * `root_dir` - The root directory for resolving asset paths
22    pub fn new<P: AsRef<Path>>(root_dir: P) -> Self {
23        Self {
24            root_dir: PathBuf::from(root_dir.as_ref()),
25        }
26    }
27
28    /// Returns a reference to the root directory path.
29    ///
30    /// This is the directory that all asset paths are resolved relative to.
31    pub fn root_dir(&self) -> &Path {
32        &self.root_dir
33    }
34}
35#[async_trait]
36impl Filesystem for NativeFilesystem {
37    async fn read_bytes(&self, asset_path: &str) -> Result<Vec<u8>, FilesystemError> {
38        // Resolve the asset path relative to our root directory
39        let path = self.root_dir.join(asset_path);
40
41        // Check if the file exists before attempting to read
42        if !path.is_file() {
43            return Err(FilesystemError::NotFound(asset_path.to_string()));
44        }
45
46        // Read the entire file into memory asynchronously
47        // Note: Currently using blocking I/O - could be improved with tokio::fs for true async I/O
48        let bytes = std::fs::read(path).map_err(FilesystemError::from)?;
49        Ok(bytes)
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use super::*;
56    use std::sync::Arc;
57
58    /// Tests that the native filesystem can successfully read a test file asynchronously.
59    ///
60    /// Uses pollster to block on the async operation for testing purposes.
61    #[test]
62    fn read_bytes() {
63        let tests_dir = Path::new(&env!("CARGO_MANIFEST_DIR")).join("tests");
64
65        // Create a filesystem instance rooted at the "tests" directory
66        let fs: Arc<dyn Filesystem> = Arc::new(NativeFilesystem::new(tests_dir));
67
68        // Read a test file asynchronously and verify its contents
69        // Using pollster::block_on to wait for the async operation in a sync test
70        let greeting = pollster::block_on(fs.read_bytes("test_data_0/hello.txt")).unwrap();
71        assert_eq!(greeting, b"Hello world\n");
72    }
73}