use super::{Vfs, VfsError, VfsResult};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct BindVfs {
host_root: PathBuf,
sandbox_root: PathBuf,
}
impl BindVfs {
pub fn new(host_root: PathBuf, sandbox_root: PathBuf) -> Self {
Self {
host_root,
sandbox_root,
}
}
pub fn host_root(&self) -> &Path {
&self.host_root
}
pub fn sandbox_root(&self) -> &Path {
&self.sandbox_root
}
}
#[async_trait::async_trait]
impl Vfs for BindVfs {
fn translate_path(&self, path: &Path) -> VfsResult<PathBuf> {
let sandbox_str = self
.sandbox_root
.to_str()
.ok_or_else(|| VfsError::InvalidInput("Invalid sandbox path".to_string()))?;
let path_str = path
.to_str()
.ok_or_else(|| VfsError::InvalidInput("Invalid path".to_string()))?;
if path_str == sandbox_str || path_str.starts_with(&format!("{}/", sandbox_str)) {
let relative = path_str
.strip_prefix(sandbox_str)
.unwrap_or("")
.trim_start_matches('/');
let host_path = if relative.is_empty() {
self.host_root.clone()
} else {
self.host_root.join(relative)
};
Ok(host_path)
} else {
Err(VfsError::NotFound)
}
}
fn is_virtual(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_translate_path_exact_match() {
let vfs = BindVfs::new(PathBuf::from("/tmp/agent"), PathBuf::from("/agent"));
let result = vfs.translate_path(Path::new("/agent")).unwrap();
assert_eq!(result, PathBuf::from("/tmp/agent"));
}
#[test]
fn test_translate_path_with_subpath() {
let vfs = BindVfs::new(PathBuf::from("/tmp/agent"), PathBuf::from("/agent"));
let result = vfs
.translate_path(Path::new("/agent/subdir/file.txt"))
.unwrap();
assert_eq!(result, PathBuf::from("/tmp/agent/subdir/file.txt"));
}
#[test]
fn test_translate_path_no_match() {
let vfs = BindVfs::new(PathBuf::from("/tmp/agent"), PathBuf::from("/agent"));
let result = vfs.translate_path(Path::new("/other/path"));
assert!(result.is_err());
}
#[test]
fn test_translate_path_partial_match() {
let vfs = BindVfs::new(PathBuf::from("/tmp/agent"), PathBuf::from("/agent"));
let result = vfs.translate_path(Path::new("/agentfoo"));
assert!(result.is_err());
}
#[test]
fn test_is_not_virtual() {
let vfs = BindVfs::new(PathBuf::from("/tmp/agent"), PathBuf::from("/agent"));
assert!(!vfs.is_virtual());
}
}