added recursive file reading using --recursive flag.

added a feature to scan subdirectory and organize the file structure
even further.
This commit is contained in:
2025-12-29 18:47:50 +05:30
parent 09828ee2f5
commit 6df8af91bb
6 changed files with 187 additions and 352 deletions

View File

@@ -2,6 +2,7 @@ use colored::*;
use serde::{Deserialize, Serialize};
use std::io;
use std::{ffi::OsStr, fs, path::Path, path::PathBuf};
use walkdir::WalkDir;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FileCategory {
@@ -21,31 +22,31 @@ pub struct FileBatch {
impl FileBatch {
/// Reads a directory path and populates lists of all files inside it.
/// It skips sub-directories (does not read recursively).
pub fn from_path(root_path: PathBuf) -> Self {
pub fn from_path(root_path: PathBuf, recursive: bool) -> Self {
let mut filenames = Vec::new();
let mut paths = Vec::new();
let entries = match fs::read_dir(&root_path) {
Ok(entries) => entries,
Err(e) => {
eprintln!("Error reading directory {:?}: {}", root_path, e);
return FileBatch {
filenames: Vec::new(),
paths: Vec::new(),
};
}
let walker = if recursive {
WalkDir::new(&root_path).min_depth(1).follow_links(false)
} else {
WalkDir::new(&root_path)
.min_depth(1)
.max_depth(1)
.follow_links(false)
};
for entry in entries.flatten() {
for entry in walker.into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file()
&& let Ok(relative_path) = path.strip_prefix(&root_path)
{
filenames.push(relative_path.to_string_lossy().into_owned());
paths.push(path);
if path.is_file() {
match path.strip_prefix(&root_path) {
Ok(relative_path) => {
filenames.push(relative_path.to_string_lossy().into_owned());
paths.push(path.to_path_buf());
}
Err(e) => {
eprintln!("Error getting relative path for {:?}: {}", path, e);
}
}
}
}
FileBatch { filenames, paths }
}

View File

@@ -35,7 +35,7 @@ fn test_file_batch_from_path() {
File::create(dir_path.join("file2.rs")).unwrap();
fs::create_dir(dir_path.join("subdir")).unwrap();
let batch = FileBatch::from_path(dir_path.to_path_buf());
let batch = FileBatch::from_path(dir_path.to_path_buf(), false);
assert_eq!(batch.count(), 2);
assert!(batch.filenames.contains(&"file1.txt".to_string()));
assert!(batch.filenames.contains(&"file2.rs".to_string()));
@@ -43,10 +43,47 @@ fn test_file_batch_from_path() {
#[test]
fn test_file_batch_from_path_nonexistent() {
let batch = FileBatch::from_path(PathBuf::from("/nonexistent/path"));
let batch = FileBatch::from_path(PathBuf::from("/nonexistent/path"), false);
assert_eq!(batch.count(), 0);
}
#[test]
fn test_file_batch_from_path_non_recursive() {
let temp_dir = tempfile::tempdir().unwrap();
let dir_path = temp_dir.path();
File::create(dir_path.join("file1.txt")).unwrap();
File::create(dir_path.join("file2.rs")).unwrap();
fs::create_dir(dir_path.join("subdir")).unwrap();
File::create(dir_path.join("subdir").join("file3.txt")).unwrap();
let batch = FileBatch::from_path(dir_path.to_path_buf(), false);
assert_eq!(batch.count(), 2);
assert!(batch.filenames.contains(&"file1.txt".to_string()));
assert!(batch.filenames.contains(&"file2.rs".to_string()));
assert!(!batch.filenames.contains(&"subdir/file3.txt".to_string()));
}
#[test]
fn test_file_batch_from_path_recursive() {
let temp_dir = tempfile::tempdir().unwrap();
let dir_path = temp_dir.path();
File::create(dir_path.join("file1.txt")).unwrap();
fs::create_dir(dir_path.join("subdir1")).unwrap();
File::create(dir_path.join("subdir1").join("file2.rs")).unwrap();
fs::create_dir(dir_path.join("subdir1").join("nested")).unwrap();
File::create(dir_path.join("subdir1").join("nested").join("file3.md")).unwrap();
fs::create_dir(dir_path.join("subdir2")).unwrap();
File::create(dir_path.join("subdir2").join("file4.py")).unwrap();
let batch = FileBatch::from_path(dir_path.to_path_buf(), true);
assert_eq!(batch.count(), 4);
assert!(batch.filenames.contains(&"file1.txt".to_string()));
assert!(batch.filenames.contains(&"subdir1/file2.rs".to_string()));
assert!(
batch
.filenames
.contains(&"subdir1/nested/file3.md".to_string())
);
assert!(batch.filenames.contains(&"subdir2/file4.py".to_string()));
}
#[test]
fn test_read_file_sample() {
let temp_dir = tempfile::tempdir().unwrap();

View File

@@ -22,6 +22,8 @@ struct Args {
help = "Maximum concurrent API requests"
)]
max_concurrent: usize,
#[arg(long, help = "Recursively searches files in subdirectory")]
recursive: bool,
}
#[tokio::main]
@@ -37,7 +39,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
cache.cleanup_old_entries(7 * 24 * 60 * 60);
let batch = FileBatch::from_path(download_path.clone());
let batch = FileBatch::from_path(download_path.clone(), args.recursive);
if batch.filenames.is_empty() {
println!("{}", "No files found to organize!".yellow());