use {
super::*,
crate::{
Uri,
fs::errors::{
FsError,
Result,
},
},
error_stack::Report,
std::{
collections::BTreeMap,
io::{
Cursor,
Error,
Read,
Seek,
SeekFrom,
Write,
},
sync::{
Arc,
Mutex,
RwLock,
},
time::SystemTime,
},
};
type NodeId = usize;
#[derive(Default, Clone)]
struct Arena {
nodes: im::Vector<Node>,
}
impl Arena {
fn new() -> Self {
Self {
nodes: im::Vector::new(),
}
}
fn alloc(&mut self, node: Node) -> NodeId {
let id = self.nodes.len();
self.nodes.push_back(node);
id
}
fn get(&self, id: NodeId) -> Option<&Node> {
self.nodes.get(id)
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut Node> {
self.nodes.get_mut(id)
}
fn iter(&self) -> impl Iterator<Item = (NodeId, &Node)> {
self.nodes.iter().enumerate()
}
}
#[derive(Clone)]
struct Node {
content: im::Vector<u8>,
metadata: Metadata,
children: BTreeMap<String, NodeId>,
}
impl Node {
fn new_file(content: im::Vector<u8>) -> Self {
Node {
metadata: Metadata {
is_dir: false,
len: content.len() as u64,
modified: SystemTime::now(),
created: SystemTime::now(),
readonly: false,
},
content,
children: BTreeMap::new(),
}
}
fn new_dir() -> Self {
Node {
metadata: Metadata {
is_dir: true,
len: 0,
modified: SystemTime::now(),
created: SystemTime::now(),
readonly: false,
},
content: im::Vector::new(),
children: BTreeMap::new(),
}
}
}
#[derive(Clone)]
pub struct MemoryFileSystem {
arena: Arc<RwLock<Arena>>,
root: Uri,
root_id: NodeId,
}
impl std::fmt::Debug for MemoryFileSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
{
let arena = match self.arena.read() {
| Ok(arena) => arena,
| Err(_) => return write!(f, "MemoryFileSystem (lock error)"),
};
fn print_tree(
f: &mut std::fmt::Formatter<'_>,
arena: &Arena,
node_id: NodeId,
prefix: &str,
) -> std::fmt::Result {
if let Some(node) = arena.get(node_id) {
let mut children: im::Vector<_> = node.children.iter().collect();
children.sort_by(|(a, _), (b, _)| a.cmp(b));
for (idx, (name, child_id)) in children.iter().enumerate() {
let is_last = idx == children.len() - 1;
let connector = if is_last { "└── " } else { "├── " };
if let Some(child) = arena.get(**child_id) {
if child.metadata.is_dir {
writeln!(f, "{prefix}{connector}{name}/")?;
let new_prefix = if is_last {
format!("{prefix} ")
} else {
format!("{prefix}│ ")
};
print_tree(f, arena, **child_id, &new_prefix)?;
} else {
writeln!(
f,
"{}{}{} [{}b]",
prefix, connector, name, child.metadata.len
)?;
}
}
}
}
Ok(())
}
writeln!(f, "{}", self.uri())?;
print_tree(f, &arena, self.root_id, "")
}
}
}
impl std::hash::Hash for MemoryFileSystem {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.root.hash(state);
}
}
#[derive(Clone)]
struct MemoryDirEntry {
path: Uri,
node_id: NodeId,
arena: Arc<RwLock<Arena>>,
}
impl std::fmt::Debug for MemoryDirEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MemoryDirEntry({})", self.path)
}
}
struct MemoryFileHandle {
cursor: Mutex<Cursor<Vec<u8>>>,
node_id: NodeId,
arena: Arc<RwLock<Arena>>,
}
impl MemoryFileSystem {
#[allow(clippy::new_ret_no_self)]
pub fn new() -> FS {
let mut arena = Arena::new();
let root = arena.alloc(Node::new_dir());
static DEFAULT_ROOT_URI: std::sync::LazyLock<Uri> = std::sync::LazyLock::new(|| {
Uri::parse("file://").unwrap_or_else(|_| {
Uri::from_file_path("/").unwrap_or_else(|| {
Uri::from_file_path(".").unwrap_or_else(|| {
std::process::abort()
})
})
})
});
FS::from(MemoryFileSystem {
arena: Arc::new(RwLock::new(arena)),
root_id: root,
root: DEFAULT_ROOT_URI.clone(),
})
}
pub fn new_with_root_uri(root_uri: Uri) -> FS {
let mut arena = Arena::new();
let root = arena.alloc(Node::new_dir());
FS::from(MemoryFileSystem {
arena: Arc::new(RwLock::new(arena)),
root_id: root,
root: root_uri,
})
}
pub fn from_folder(
folder_path: &std::path::Path,
root_uri: Uri,
) -> Result<FS> {
if !folder_path.exists() {
return Err(Report::new(FsError::filesystem_error(format!(
"Folder does not exist: {}",
folder_path.display()
))));
}
if !folder_path.is_dir() {
return Err(Report::new(FsError::filesystem_error(format!(
"Path is not a directory: {}",
folder_path.display()
))));
}
let fs = Self::new_with_root_uri(root_uri.clone());
fn walk_dir(
fs: &FS,
dir_path: &std::path::Path,
base_path: &std::path::Path,
root_uri: &Uri,
) -> Result<()> {
let entries = std::fs::read_dir(dir_path).map_err(|e| {
Report::new(FsError::io_error(format!(
"Failed to read directory {}: {e}",
dir_path.display()
)))
})?;
for entry in entries {
let entry = entry.map_err(|e| {
Report::new(FsError::io_error(format!(
"Failed to read directory entry: {e}"
)))
})?;
let path = entry.path();
let relative_path = path
.strip_prefix(base_path)
.map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to get relative path: {e}"
)))
})?
.to_string_lossy();
if path.is_dir() {
walk_dir(fs, &path, base_path, root_uri)?;
} else {
let content = std::fs::read(&path).map_err(|e| {
Report::new(FsError::io_error(format!(
"Failed to read file {}: {e}",
path.display()
)))
})?;
let file_uri = root_uri.join(&relative_path).ok_or_else(|| {
Report::new(FsError::uri_error(format!(
"Failed to join URI with path: {relative_path}"
)))
})?;
fs.write(&file_uri, &content)?;
}
}
Ok(())
}
walk_dir(&fs, folder_path, folder_path, &root_uri)?;
Ok(fs)
}
pub fn debug_list_files(&self) -> Result<String> {
let mut output = String::new();
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
})?;
fn get_path(arena: &Arena, id: NodeId, parent_path: String) -> String {
if id == 0 {
return "/".to_string();
}
for (parent_id, parent) in arena.iter() {
for (name, &child_id) in &parent.children {
if child_id == id {
let parent_full_path =
get_path(arena, parent_id, parent_path.clone());
return if parent_full_path == "/" {
format!("/{name}")
} else {
format!("{parent_full_path}/{name}")
};
}
}
}
parent_path
}
for (id, node) in arena.iter() {
if node.metadata.is_dir {
output.push_str(&format!("{}\n", get_path(&arena, id, String::new())));
} else {
output.push_str(&format!(
"{} [{} bytes]\n",
get_path(&arena, id, String::new()),
node.metadata.len
));
}
}
Ok(output)
}
fn reconstruct_path_for_node(
&self,
target_id: NodeId,
arena: &Arena,
) -> Result<Uri> {
fn get_path_components(arena: &Arena, id: NodeId) -> Vec<String> {
if id == 0 {
return vec![];
}
for (parent_id, parent) in arena.iter() {
for (name, &child_id) in &parent.children {
if child_id == id {
let mut parent_components = get_path_components(arena, parent_id);
parent_components.push(name.clone());
return parent_components;
}
}
}
vec![]
}
let components = get_path_components(arena, target_id);
let path_str = if components.is_empty() {
"/".to_string()
} else {
format!("/{}", components.join("/"))
};
self.root.join(&path_str).ok_or_else(|| {
Report::new(FsError::uri_error("Failed to reconstruct Uri"))
})
}
fn resolve_path(&self, path: &Uri) -> Result<NodeId> {
if *path == self.root {
return Ok(self.root_id);
}
let root_path = self.root.path_str().trim_end_matches('/');
let target_path = path.path_str().trim_end_matches('/');
if target_path == root_path {
return Ok(self.root_id);
}
if !target_path.starts_with(root_path) {
return Err(Report::new(FsError::filesystem_error(
"Path not within filesystem root",
)));
}
let relative_path = if root_path.is_empty() {
target_path.trim_start_matches('/')
} else {
target_path
.strip_prefix(root_path)
.and_then(|p| p.strip_prefix('/'))
.unwrap_or("")
};
if relative_path.is_empty() {
return Ok(self.root_id);
}
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
})?;
let mut current = self.root_id;
for component in relative_path.split('/') {
if component.is_empty() {
continue;
}
if let Some(node) = arena.get(current) {
if let Some(&child_id) = node.children.get(component) {
current = child_id;
} else {
return Err(Report::new(FsError::filesystem_error("Path not found")));
}
}
}
Ok(current)
}
fn create_path(&self, uri: &Uri, is_dir: bool) -> Result<NodeId> {
let root_path = self.root.path_str().trim_end_matches('/');
let target_path = uri.path_str().trim_end_matches('/');
if target_path == root_path {
return Ok(self.root_id);
}
if !target_path.starts_with(root_path) {
return Err(Report::new(FsError::filesystem_error(
"Path not within filesystem root",
)));
}
let relative_path = if root_path.is_empty() {
target_path.trim_start_matches('/')
} else {
target_path
.strip_prefix(root_path)
.and_then(|p| p.strip_prefix('/'))
.unwrap_or("")
};
if relative_path.is_empty() {
return Ok(self.root_id);
}
let components: im::Vector<&str> = relative_path.split('/').collect();
let mut arena = self.arena.write().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire write lock: {e}"
)))
})?;
let mut current = self.root_id;
for (i, &component) in components.iter().enumerate() {
let is_last = i == components.len() - 1;
if let Some(node) = arena.get(current)
&& let Some(&child_id) = node.children.get::<str>(component)
{
if is_last
&& let Some(child_node) = arena.get(child_id)
&& is_dir != child_node.metadata.is_dir
{
return Err(Report::new(FsError::invalid_operation(
"Path exists with different type",
)));
}
current = child_id;
continue;
}
let new_node = if is_last && !is_dir {
Node::new_file(im::Vector::new())
} else {
Node::new_dir()
};
let new_id = arena.alloc(new_node);
if let Some(parent) = arena.get_mut(current) {
parent.children.insert(component.to_string(), new_id);
}
current = new_id;
}
Ok(current)
}
}
impl FileSystem for MemoryFileSystem {
fn uri(&self) -> Uri {
self.root.clone()
}
fn open(
&self,
path: &Uri,
options: OpenOptions,
) -> Result<Box<dyn FileHandle>> {
let node_id = if options.create {
self.create_path(path, false)?
} else {
self.resolve_path(path)?
};
let content = {
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
.attach_printable(format!("Path: {path}"))
})?;
let node = arena.get(node_id).ok_or_else(|| {
Report::new(FsError::filesystem_error("Node not found"))
.attach_printable(format!("Path: {path}"))
})?;
if node.metadata.is_dir {
return Err(
Report::new(FsError::invalid_operation(
"Cannot open directory as file",
))
.attach_printable(format!("Path: {path}")),
);
}
node.content.clone()
};
Ok(Box::new(MemoryFileHandle {
cursor: Mutex::new(Cursor::new(content.iter().copied().collect())),
node_id,
arena: self.arena.clone(),
}))
}
fn read(&self, path: &Uri) -> Result<Vec<u8>> {
let node_id = self.resolve_path(path)?;
let arena = self.arena.read().map_err(|_| {
Report::new(FsError::filesystem_error("Failed to acquire read lock"))
.attach_printable(format!("Path: {path}"))
})?;
let node = arena.get(node_id).ok_or_else(|| {
Report::new(FsError::filesystem_error("Node not found"))
.attach_printable(format!("Path: {path}"))
})?;
Ok(node.content.iter().copied().collect())
}
fn read_to_new_rope(&self, path: &Uri) -> Result<ropey::Rope> {
let node_id = self.resolve_path(path)?;
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
})?;
let node = arena.get(node_id).ok_or_else(|| {
Report::new(FsError::filesystem_error("Node not found"))
.attach_printable(format!("Path: {path}"))
})?;
let reader = Cursor::new(node.content.iter().copied().collect::<Vec<_>>());
ropey::Rope::from_reader(reader).map_err(|e| {
Report::new(FsError::io_error(format!("Failed to read rope: {e}")))
.attach_printable(format!("Path: {path}"))
})
}
fn write(&self, path: &Uri, data: &[u8]) -> Result<()> {
let node_id = self.create_path(path, false)?;
let mut arena = self.arena.write().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire write lock: {e}"
)))
})?;
if let Some(node) = arena.get_mut(node_id) {
node.content = data.iter().copied().collect();
node.metadata.len = data.len() as u64;
node.metadata.modified = SystemTime::now();
}
Ok(())
}
fn write_str(&self, path: &Uri, data: &str) -> Result<()> {
self.write(path, data.as_bytes())
}
fn append(&self, path: &Uri, data: &[u8]) -> Result<()> {
let node_id = self.create_path(path, false)?;
let mut arena = self.arena.write().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire write lock: {e}"
)))
})?;
if let Some(node) = arena.get_mut(node_id) {
node.content.extend(data.iter().cloned());
node.metadata.len = node.content.len() as u64;
node.metadata.modified = SystemTime::now();
}
Ok(())
}
fn delete(&self, path: &Uri) -> Result<()> {
let path_str = path.path_str().trim_start_matches('/');
let components: im::Vector<&str> = path_str.split('/').collect();
let Some(to_delete) = components.last() else {
return Err(Report::new(FsError::invalid_operation(
"Cannot delete root",
)));
};
let parent_uri = if components.len() == 1 {
Uri::parse(&format!("{}:///", path.scheme())).map_err(|e| {
Report::new(FsError::uri_error(format!("Invalid root uri: {e}")))
})?
} else {
let parent_path: String = components
.iter()
.take(components.len() - 1)
.copied()
.collect::<im::Vector<&str>>()
.iter()
.copied()
.collect::<Vec<&str>>()
.join("/");
Uri::parse(&format!("{}:///{}", path.scheme(), parent_path)).map_err(
|e| {
Report::new(FsError::uri_error(format!("Invalid parent path: {e}")))
},
)?
};
let parent_id = self.resolve_path(&parent_uri)?;
let mut arena = self.arena.write().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire write lock: {e}"
)))
})?;
if let Some(parent) = arena.get_mut(parent_id)
&& parent.children.remove(*to_delete).is_none()
{
return Err(
Report::new(FsError::filesystem_error("Path not found"))
.attach_printable(format!("Path: {path}")),
);
}
Ok(())
}
fn metadata(&self, path: &Uri) -> Result<Metadata> {
let node_id = self.resolve_path(path)?;
let arena = self.arena.read().map_err(|_| {
Report::new(FsError::filesystem_error("Failed to acquire read lock"))
.attach_printable(format!("Path: {path}"))
})?;
let node = arena.get(node_id).ok_or_else(|| {
Report::new(FsError::filesystem_error("Node not found"))
.attach_printable(format!("Path: {path}"))
})?;
Ok(node.metadata.clone())
}
fn root(&self) -> Result<Box<dyn DirEntry>> {
Ok(Box::new(MemoryDirEntry {
path: self.root.clone(),
node_id: self.root_id,
arena: self.arena.clone(),
}))
}
fn dir(&self, path: &Uri) -> Result<Box<dyn DirEntry>> {
let node_id = self.create_path(path, true)?;
Ok(Box::new(MemoryDirEntry {
path: path.clone(),
node_id,
arena: self.arena.clone(),
}))
}
fn find(
&self,
path: &Uri,
globs: &[String],
) -> Result<im::Vector<Arc<dyn DirEntry>>> {
let glob = crate::fs::filesystem::build_glob_set(globs)?;
let mut results = im::Vector::new();
let base_id = self.resolve_path(path)?;
fn traverse(
arena: &Arena,
node_id: NodeId,
base_uri: &Uri,
current_path: &str,
glob: &globset::GlobSet,
results: &mut im::Vector<Arc<dyn DirEntry>>,
arena_ref: Arc<RwLock<Arena>>,
) -> Result<()> {
if let Some(node) = arena.get(node_id) {
if !current_path.is_empty()
&& !node.metadata.is_dir
&& glob.is_match(current_path)
{
let base_uri_str = if base_uri.as_str().ends_with('/') {
base_uri.as_str().to_string()
} else {
format!("{}/", base_uri.as_str())
};
let base_uri_for_join = Uri::parse(&base_uri_str).map_err(|e| {
Report::new(FsError::uri_error(format!("Invalid base uri: {e}")))
})?;
let uri = base_uri_for_join
.join(current_path)
.ok_or_else(|| Report::new(FsError::uri_error("Invalid path")))?;
results.push_back(Arc::new(MemoryDirEntry {
path: uri,
node_id,
arena: arena_ref.clone(),
}) as Arc<dyn DirEntry>);
}
for (name, &child_id) in &node.children {
let next_path = if current_path.is_empty() {
name.clone()
} else {
format!("{current_path}/{name}")
};
traverse(
arena,
child_id,
base_uri,
&next_path,
glob,
results,
arena_ref.clone(),
)?;
}
}
Ok(())
}
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
})?;
traverse(
&arena,
base_id,
path,
"",
&glob,
&mut results,
self.arena.clone(),
)?;
Ok(results)
}
fn iter_files(
&self,
) -> Result<Box<dyn Iterator<Item = (Uri, Vec<u8>)> + '_>> {
let arena = self.arena.read().map_err(|_| {
Report::new(FsError::filesystem_error("Failed to acquire read lock"))
})?;
let mut files = Vec::new();
for (node_id, node) in arena.iter() {
if !node.metadata.is_dir
&& let Ok(uri) = self.reconstruct_path_for_node(node_id, &arena)
{
let content: Vec<u8> = node.content.iter().copied().collect();
files.push((uri, content));
}
}
Ok(Box::new(files.into_iter()))
}
}
impl DirEntry for MemoryDirEntry {
fn path(&self) -> Uri {
self.path.clone()
}
fn file_type(&self) -> FileType {
let Ok(arena) = self.arena.read() else {
return FileType::File;
};
if let Some(node) = arena.get(self.node_id) {
if node.metadata.is_dir {
FileType::Directory
} else {
FileType::File
}
} else {
FileType::File }
}
fn metadata(&self) -> Result<Metadata> {
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
.attach_printable(format!("Path: {}", self.path))
})?;
let node = arena.get(self.node_id).ok_or_else(|| {
Report::new(FsError::filesystem_error("Node not found"))
.attach_printable(format!("Path: {}", self.path))
})?;
Ok(node.metadata.clone())
}
fn entry(&mut self, _entry: Box<dyn DirEntry>) {
}
fn write(&mut self, path: &Uri, data: &[u8]) -> Result<()> {
let fs = MemoryFileSystem {
arena: self.arena.clone(),
root_id: self.node_id,
root: self.path.clone(),
};
fs.write(path, data)
}
}
impl FileHandle for MemoryFileHandle {
fn metadata(&self) -> Result<Metadata> {
let arena = self.arena.read().map_err(|e| {
Report::new(FsError::filesystem_error(format!(
"Failed to acquire read lock: {e}"
)))
.attach_printable("Failed to read file handle metadata")
})?;
let node = arena.get(self.node_id).ok_or_else(|| {
Report::new(FsError::filesystem_error("Node not found"))
.attach_printable("Node for file handle not found")
})?;
Ok(node.metadata.clone())
}
}
impl Read for MemoryFileHandle {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let mut cursor = self
.cursor
.lock()
.map_err(|e| Error::other(format!("Failed to lock cursor: {e}")))?;
cursor.read(buf)
}
}
impl Write for MemoryFileHandle {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let mut cursor = self
.cursor
.lock()
.map_err(|e| Error::other(format!("Failed to lock cursor: {e}")))?;
let position = cursor.position();
cursor.seek(SeekFrom::Start(position))?;
let bytes_written = cursor.write(buf)?;
if let Ok(mut arena) = self.arena.write()
&& let Some(node) = arena.get_mut(self.node_id)
{
node.content = cursor.get_ref().clone().into();
node.metadata.len = node.content.len() as u64;
node.metadata.modified = SystemTime::now();
}
Ok(bytes_written)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::Uri,
};
#[test]
fn test_resolve_path_root_access() {
let root_uri = Uri::parse("file://example.com/root").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
let root_entry = fs.root().unwrap();
assert_eq!(root_entry.path(), root_uri);
}
#[test]
fn test_resolve_path_subdirectory_creation_and_access() {
let root_uri = Uri::parse("file://example.com/root").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
let file_uri =
Uri::parse("file://example.com/root/subdir/test.txt").unwrap();
fs.write_str(&file_uri, "test content").unwrap();
let content = fs.read_to_new_rope(&file_uri).unwrap();
assert_eq!(content.to_string(), "test content");
let dir_uri = Uri::parse("file://example.com/root/subdir").unwrap();
let metadata = fs.metadata(&dir_uri).unwrap();
assert!(metadata.is_dir);
}
#[test]
fn test_resolve_path_find_with_glob() {
let root_uri = Uri::parse("file://example.com/root").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
fs.write_str(
&Uri::parse("file://example.com/root/file1.txt").unwrap(),
"content1",
)
.unwrap();
fs.write_str(
&Uri::parse("file://example.com/root/subdir/file2.txt").unwrap(),
"content2",
)
.unwrap();
let results = fs.find(&root_uri, &["**/*.txt".to_string()]).unwrap();
assert_eq!(results.len(), 2);
}
#[test]
fn test_resolve_path_outside_root_fails() {
let root_uri = Uri::parse("file://example.com/root").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
let outside_uri = Uri::parse("file://example.com/other/file.txt").unwrap();
let result = fs.write_str(&outside_uri, "content");
assert!(result.is_err());
}
#[test]
fn test_resolve_path_complex_root() {
let root_uri =
Uri::parse("file://example.org/editions/2026/core").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
let root_entry = fs.root().unwrap();
assert_eq!(root_entry.path(), root_uri);
let packages_file = Uri::parse(
"file://example.org/editions/2026/core/packages/test.bld",
)
.unwrap();
fs.write_str(&packages_file, "test content").unwrap();
let content = fs.read_to_new_rope(&packages_file).unwrap();
assert_eq!(content.to_string(), "test content");
let results = fs.find(&root_uri, &["packages/*".to_string()]).unwrap();
assert_eq!(results.len(), 1);
}
#[test]
fn test_resolve_path_relative_glob_matching() {
let root_uri =
Uri::parse("file://example.org/editions/2026/core").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
fs.write_str(
&Uri::parse(
"file://example.org/editions/2026/core/packages/ops/package.bld",
)
.unwrap(),
"package config",
)
.unwrap();
fs.write_str(
&Uri::parse("file://example.org/editions/2026/core/packages/prelude/package.bld").unwrap(),
"package config"
).unwrap();
let results = fs
.find(&root_uri, &["packages/**/package.bld".to_string()])
.unwrap();
assert_eq!(results.len(), 2);
for entry in results {
let path_uri = entry.path();
let path_str = path_uri.path_str();
assert!(path_str.contains("/packages/"));
assert!(path_str.starts_with("/editions/2026/core/packages/"));
}
}
#[test]
fn test_create_path_functionality() {
let root_uri =
Uri::parse("file://example.org/editions/2026/core").unwrap();
let fs_enum = MemoryFileSystem::new_with_root_uri(root_uri.clone());
let memory_fs = match fs_enum {
| crate::fs::FS::Memory(fs) => fs,
| _ => panic!("Expected Memory filesystem"),
};
let packages_uri =
Uri::parse("file://example.org/editions/2026/core/packages")
.unwrap();
let packages_id = memory_fs.create_path(&packages_uri, true).unwrap();
assert_ne!(packages_id, memory_fs.root_id);
let resolved_id = memory_fs.resolve_path(&packages_uri).unwrap();
assert_eq!(packages_id, resolved_id);
let file_uri = Uri::parse(
"file://example.org/editions/2026/core/packages/test.bld",
)
.unwrap();
let file_id = memory_fs.create_path(&file_uri, false).unwrap();
assert_ne!(file_id, memory_fs.root_id);
assert_ne!(file_id, packages_id);
let resolved_file_id = memory_fs.resolve_path(&file_uri).unwrap();
assert_eq!(file_id, resolved_file_id);
}
#[test]
fn test_issue_fix_relative_globs_with_complex_base_uri() {
let root_uri =
Uri::parse("file://example.org/editions/2026/core").unwrap();
let fs = MemoryFileSystem::new_with_root_uri(root_uri.clone());
fs.write_str(
&Uri::parse(
"file://example.org/editions/2026/core/packages/ops/package.bld",
)
.unwrap(),
"name = \"ops\"\nversion = \"1.0.0\"",
)
.unwrap();
fs.write_str(
&Uri::parse("file://example.org/editions/2026/core/packages/prelude/package.bld").unwrap(),
"name = \"prelude\"\nversion = \"1.0.0\""
).unwrap();
fs.write_str(
&Uri::parse("file://example.org/editions/2026/core/workspace.bld")
.unwrap(),
"name = \"workspace\"",
)
.unwrap();
let results = fs
.find(&root_uri, &["packages/**/package.bld".to_string()])
.unwrap();
assert_eq!(results.len(), 2, "Should find both package.bld files");
let paths: Vec<String> = results
.iter()
.map(|entry| entry.path().path().to_string())
.collect();
assert!(
paths
.contains(&"/editions/2026/core/packages/ops/package.bld".to_string())
);
assert!(paths.contains(
&"/editions/2026/core/packages/prelude/package.bld".to_string()
));
let workspace_results =
fs.find(&root_uri, &["workspace.bld".to_string()]).unwrap();
assert_eq!(
workspace_results.len(),
1,
"Should find workspace.bld at root"
);
}
}