Skip to main content

MountableFs

Struct MountableFs 

Source
pub struct MountableFs { /* private fields */ }
Expand description

Filesystem with Unix-style mount points.

MountableFs allows mounting different filesystem implementations at specific paths, similar to how Unix systems mount devices at directories. This enables complex multi-source filesystem setups.

§Features

  • Multiple mount points: Mount different filesystems at different paths
  • Nested mounts: Mount filesystems within other mounts (longest-prefix matching)
  • Dynamic mounting: Add/remove mounts at runtime
  • Cross-mount operations: Copy/move files between different mounted filesystems

§Use Cases

  • Hybrid storage: Combine in-memory temp storage with persistent data stores
  • Multi-tenant isolation: Mount separate filesystems for different tenants
  • Plugin systems: Each plugin gets its own mounted filesystem
  • Testing: Mount mock filesystems for specific paths

§Example: Basic Mounting

use bashkit::{Bash, FileSystem, InMemoryFs, MountableFs};
use std::path::Path;
use std::sync::Arc;

// Create root and separate data filesystem
let root = Arc::new(InMemoryFs::new());
let data_fs = Arc::new(InMemoryFs::new());

// Pre-populate data filesystem
data_fs.write_file(Path::new("/users.json"), br#"["alice", "bob"]"#).await?;

// Create mountable filesystem
let mountable = MountableFs::new(root.clone());

// Mount data_fs at /mnt/data
mountable.mount("/mnt/data", data_fs.clone())?;

// Use with Bash
let mut bash = Bash::builder().fs(Arc::new(mountable)).build();

// Access mounted filesystem
let result = bash.exec("cat /mnt/data/users.json").await?;
assert!(result.stdout.contains("alice"));

// Access root filesystem
bash.exec("echo hello > /root.txt").await?;

§Example: Nested Mounts

use bashkit::{FileSystem, InMemoryFs, MountableFs};
use std::path::Path;
use std::sync::Arc;

let root = Arc::new(InMemoryFs::new());
let outer = Arc::new(InMemoryFs::new());
let inner = Arc::new(InMemoryFs::new());

outer.write_file(Path::new("/outer.txt"), b"outer").await?;
inner.write_file(Path::new("/inner.txt"), b"inner").await?;

let mountable = MountableFs::new(root);
mountable.mount("/mnt", outer)?;
mountable.mount("/mnt/nested", inner)?;

// Access outer mount
let content = mountable.read_file(Path::new("/mnt/outer.txt")).await?;
assert_eq!(content, b"outer");

// Access nested mount (longest-prefix matching)
let content = mountable.read_file(Path::new("/mnt/nested/inner.txt")).await?;
assert_eq!(content, b"inner");

§Example: Dynamic Mount/Unmount

use bashkit::{FileSystem, InMemoryFs, MountableFs};
use std::path::Path;
use std::sync::Arc;

let root = Arc::new(InMemoryFs::new());
let plugin_fs = Arc::new(InMemoryFs::new());
plugin_fs.write_file(Path::new("/plugin.so"), b"binary").await?;

let mountable = MountableFs::new(root);

// Mount plugin filesystem
mountable.mount("/plugins", plugin_fs)?;
assert!(mountable.exists(Path::new("/plugins/plugin.so")).await?);

// Unmount when done
mountable.unmount("/plugins")?;
assert!(!mountable.exists(Path::new("/plugins/plugin.so")).await?);

§Path Resolution

When resolving a path, MountableFs uses longest-prefix matching to find the appropriate filesystem. For example, with mounts at /mnt and /mnt/data:

  • /mnt/file.txt → resolves to /mnt mount
  • /mnt/data/file.txt → resolves to /mnt/data mount (longer prefix wins)
  • /other/file.txt → resolves to root filesystem

Implementations§

Source§

impl MountableFs

Source

pub fn new(root: Arc<dyn FileSystem>) -> Self

Create a new MountableFs with the given root filesystem.

The root filesystem is used for all paths that don’t match any mount point.

§Example
use bashkit::{FileSystem, InMemoryFs, MountableFs};
use std::path::Path;
use std::sync::Arc;

let root = Arc::new(InMemoryFs::new());
let mountable = MountableFs::new(root);

// Paths not covered by mounts go to root
mountable.write_file(Path::new("/tmp/test.txt"), b"hello").await?;
Source

pub fn mount( &self, path: impl AsRef<Path>, fs: Arc<dyn FileSystem>, ) -> Result<()>

Mount a filesystem at the given path.

After mounting, all operations on paths under the mount point will be directed to the mounted filesystem.

§Arguments
  • path - The mount point (must be an absolute path)
  • fs - The filesystem to mount
§Errors

Returns an error if the path is not absolute.

§Example
use bashkit::{FileSystem, InMemoryFs, MountableFs};
use std::path::Path;
use std::sync::Arc;

let root = Arc::new(InMemoryFs::new());
let data_fs = Arc::new(InMemoryFs::new());
data_fs.write_file(Path::new("/data.txt"), b"data").await?;

let mountable = MountableFs::new(root);
mountable.mount("/data", data_fs)?;

// Access via mount point
let content = mountable.read_file(Path::new("/data/data.txt")).await?;
assert_eq!(content, b"data");
Source

pub fn unmount(&self, path: impl AsRef<Path>) -> Result<()>

Unmount a filesystem at the given path.

After unmounting, paths that previously resolved to the mounted filesystem will fall back to the root filesystem or a shorter mount prefix.

§Errors

Returns an error if no filesystem is mounted at the given path.

§Example
use bashkit::{FileSystem, InMemoryFs, MountableFs};
use std::path::Path;
use std::sync::Arc;

let root = Arc::new(InMemoryFs::new());
let plugin = Arc::new(InMemoryFs::new());
plugin.write_file(Path::new("/lib.so"), b"binary").await?;

let mountable = MountableFs::new(root);
mountable.mount("/plugin", plugin)?;

// File is accessible
assert!(mountable.exists(Path::new("/plugin/lib.so")).await?);

// Unmount
mountable.unmount("/plugin")?;

// No longer accessible
assert!(!mountable.exists(Path::new("/plugin/lib.so")).await?);

Trait Implementations§

Source§

impl FileSystem for MountableFs

Source§

fn read_file<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, ) -> Pin<Box<dyn Future<Output = Result<Vec<u8>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Read a file’s contents as bytes. Read more
Source§

fn write_file<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, path: &'life1 Path, content: &'life2 [u8], ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Write contents to a file, creating it if necessary. Read more
Source§

fn append_file<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, path: &'life1 Path, content: &'life2 [u8], ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Append contents to a file, creating it if necessary. Read more
Source§

fn mkdir<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, recursive: bool, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Create a directory. Read more
Source§

fn remove<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, recursive: bool, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Remove a file or directory. Read more
Source§

fn stat<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, ) -> Pin<Box<dyn Future<Output = Result<Metadata>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get file or directory metadata. Read more
Source§

fn read_dir<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, ) -> Pin<Box<dyn Future<Output = Result<Vec<DirEntry>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

List directory contents. Read more
Source§

fn exists<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, ) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Check if a path exists. Read more
Source§

fn rename<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, from: &'life1 Path, to: &'life2 Path, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Rename or move a file or directory. Read more
Source§

fn copy<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, from: &'life1 Path, to: &'life2 Path, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Copy a file. Read more
Create a symbolic link. Read more
Read a symbolic link’s target. Read more
Source§

fn chmod<'life0, 'life1, 'async_trait>( &'life0 self, path: &'life1 Path, mode: u32, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Change file permissions. Read more
Source§

fn usage(&self) -> FsUsage

Get current filesystem usage statistics. Read more
Source§

fn limits(&self) -> FsLimits

Get filesystem limits. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.