with some help, i was able to traverse directory and parse it into Collection struct recursively like this
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Collection {
pub relative_path: String,
pub name: String,
pub collections: Vec<Collection>,
pub requests: Vec<Request>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Request {
pub relative_path: String,
pub name: String,
}
fn build_collection_from_path<P: AsRef<Path>>(path: P) -> Collection {
let path = path.as_ref();
let name = path.file_name().unwrap().to_string_lossy().into_owned();
let relative_path = path.to_string_lossy().into_owned();
let mut collection = Collection {
relative_path: relative_path.clone(),
name,
collections: vec![],
requests: vec![],
};
if let Ok(entries) = fs::read_dir(path) {
for entry in entries {
if let Ok(entry) = entry {
let entry_path = entry.path();
if entry_path.is_dir() {
// It's a folder, add as a sub-collection
collection
.collections
.push(build_collection_from_path(entry_path));
} else if entry_path.is_file() {
// It's a file, add as a request
let file_name = entry_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let file_relative_path = entry_path.to_string_lossy().into_owned();
collection.requests.push(Request {
relative_path: file_relative_path,
name: file_name,
});
}
}
}
}
collection
}
// Function to traverse both root folders (collections) and root-level files (requests)
fn bfs_traverse_folders_and_files<P: AsRef<Path>>(root_path: P) -> (Vec<Collection>, Vec<Request>) {
let root_path = root_path.as_ref();
// Initialize the root collections and requests
let mut root_collections: Vec<Collection> = Vec::new();
let mut root_requests: Vec<Request> = Vec::new();
// Traverse the root directory
if let Ok(entries) = fs::read_dir(root_path) {
for entry in entries {
if let Ok(entry) = entry {
let entry_path = entry.path();
if entry_path.is_dir() {
// Create and push the folder as a collection
let collection = build_collection_from_path(entry_path);
root_collections.push(collection);
} else if entry_path.is_file() {
// Push the file as a request
let file_name = entry_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let file_relative_path = entry_path.to_string_lossy().into_owned();
root_requests.push(Request {
relative_path: file_relative_path,
name: file_name,
});
}
}
}
}
(root_collections, root_requests)
}
let (root_collections_recursive, root_requests_recursive) =
bfs_traverse_folders_and_files(root_path);
println!(
"Root Collections Recursive:\n{:#?}",
root_collections_recursive
);
println!("Root Requests Recursive:\n{:#?}", root_requests_recursive);
but i'm unable to do this iteratively and assign collections properly
for iterative approach i was thinking of using a stack vector and then keeping a mutable ref to the collection but i can't get it to compile because i cannot seem to share a mutable reference
if i slap a .clone() to it then it doesn't translate to the struct properly because i copied over the contents early? is there a way to get around it please help.
Edit:
i was able to do this iteratively but i had to use unsafe in order to mutate the parent collection, not sure if it could be done in a better way. i'll look into RefCell as well as suggested by @cafce25
here's the code
// Function to iteratively build the Collection structure
fn build_collection_from_path_iterative<P: AsRef<Path>>(root_path: P) -> Collection {
let root_path = root_path.as_ref();
let root_name = root_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let root_relative_path = root_path.to_string_lossy().into_owned();
let mut root_collection = Collection {
relative_path: root_relative_path.clone(),
name: root_name,
collections: vec![],
requests: vec![],
};
// Stack to simulate DFS, storing (current path, pointer to parent collection)
let mut stack: Vec<(PathBuf, *mut Collection)> = Vec::new();
stack.push((
root_path.to_path_buf(),
&mut root_collection as *mut Collection,
));
while let Some((current_path, parent_collection_ptr)) = stack.pop() {
if let Ok(entries) = fs::read_dir(¤t_path) {
for entry in entries {
if let Ok(entry) = entry {
let entry_path = entry.path();
let parent_collection = unsafe { &mut *parent_collection_ptr }; // Dereference the raw pointer to modify parent
if entry_path.is_dir() {
// Create sub-collection for directory
let sub_collection_name = entry_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let sub_collection = Collection {
relative_path: entry_path.to_string_lossy().into_owned(),
name: sub_collection_name,
collections: vec![],
requests: vec![],
};
// Push the sub-collection into the parent collection's `collections` vector
parent_collection.collections.push(sub_collection);
// Push the new sub-collection onto the stack for further traversal
let sub_collection_ptr =
parent_collection.collections.last_mut().unwrap() as *mut Collection; // Get a pointer to the last added collection
stack.push((entry_path.clone(), sub_collection_ptr));
} else if entry_path.is_file() {
// Add file as a request
let file_name = entry_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let file_relative_path = entry_path.to_string_lossy().into_owned();
parent_collection.requests.push(Request {
relative_path: file_relative_path,
name: file_name,
});
}
}
}
}
}
root_collection
}
RefCell