From c135f407c7727c1b76a326f00e7ba7c4afa076e0 Mon Sep 17 00:00:00 2001 From: Ted Slusser Date: Sat, 18 Jan 2025 11:55:59 -0600 Subject: [PATCH 1/2] will exclude based on simple filename.ext --- src/filter.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/filter.rs b/src/filter.rs index 739ebe4..2e56ac4 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -40,7 +40,10 @@ pub fn should_include_file( .any(|pattern| Pattern::new(pattern).unwrap().matches(path_str)); let excluded = exclude_patterns .iter() - .any(|pattern| Pattern::new(pattern).unwrap().matches(path_str)); + .any(|pattern| { + let file_name = path.file_name().and_then(|name| name.to_str()).unwrap_or(""); + pattern == file_name || Pattern::new(pattern).unwrap().matches(path_str) + }); // ~~~ Decision ~~~ let result = match (included, excluded) { From 24b3c1af961b34bf0ea6db9f67b17e9877e84401 Mon Sep 17 00:00:00 2001 From: Ted Slusser Date: Sat, 18 Jan 2025 12:28:52 -0600 Subject: [PATCH 2/2] Update version and enhance file filtering logic Bumped the version to 2.0.1 and added new utility tests for file inclusion/exclusion functionality. Refactored filtering logic by centralizing pattern matching into a helper function, improving code readability and robustness. --- Cargo.toml | 2 +- src/filter.rs | 38 +++++++++++++++------------- src/main.rs | 2 +- tests/test_filter.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1e9531..ac1a4b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code2prompt" -version = "2.0.0" +version = "2.0.1" authors = ["Mufeed VH ","Olivier D'Ancona "] description = "A command-line (CLI) tool to generate an LLM prompt from codebases of any size, fast." keywords = ["code", "prompt", "llm", "gpt", "ai"] diff --git a/src/filter.rs b/src/filter.rs index 2e56ac4..e505ab5 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -6,6 +6,20 @@ use log::{debug, error}; use std::fs; use std::path::Path; +/// Helper function to check if a path matches any pattern +fn matches_any_pattern(path: &Path, patterns: &[String]) -> bool { + let path_str = path.to_str().unwrap_or(""); + + patterns.iter().any(|pattern| { + // Extract the file name + let file_name = path.file_name().and_then(|name| name.to_str()).unwrap_or(""); + // Normalize the pattern to an absolute path + let absolute_pattern = fs::canonicalize(Path::new(pattern)).unwrap_or_else(|_| Path::new(pattern).to_path_buf()); + // Match either the file name or the full path + pattern == file_name || Pattern::new(absolute_pattern.to_str().unwrap_or(pattern)).unwrap().matches(path_str) + }) +} + /// Determines whether a file should be included based on include and exclude patterns. /// /// # Arguments @@ -25,25 +39,15 @@ pub fn should_include_file( include_priority: bool, ) -> bool { // ~~~ Clean path ~~~ - let canonical_path = match fs::canonicalize(path) { - Ok(path) => path, - Err(e) => { - error!("Failed to canonicalize path: {}", e); - return false; - } - }; - let path_str = canonical_path.to_str().unwrap(); + let canonical_path = fs::canonicalize(path).unwrap_or_else(|e| { + error!("Failed to canonicalize path {:?}: {}", path, e); + path.to_path_buf() // Fall back to the original path if canonicalization fails + }); + let path_str = canonical_path.to_str().unwrap_or(""); // ~~~ Check glob patterns ~~~ - let included = include_patterns - .iter() - .any(|pattern| Pattern::new(pattern).unwrap().matches(path_str)); - let excluded = exclude_patterns - .iter() - .any(|pattern| { - let file_name = path.file_name().and_then(|name| name.to_str()).unwrap_or(""); - pattern == file_name || Pattern::new(pattern).unwrap().matches(path_str) - }); + let included = matches_any_pattern(&canonical_path, include_patterns); + let excluded = matches_any_pattern(&canonical_path, exclude_patterns); // ~~~ Decision ~~~ let result = match (included, excluded) { diff --git a/src/main.rs b/src/main.rs index de5064e..d548280 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,7 @@ const CUSTOM_TEMPLATE_NAME: &str = "custom"; // CLI Arguments #[derive(Parser)] -#[clap(name = "code2prompt", version = "2.0.0", author = "Mufeed VH")] +#[clap(name = "code2prompt", version = "2.0.1", author = "Mufeed VH")] #[command(arg_required_else_help = true)] struct Cli { /// Path to the codebase directory diff --git a/tests/test_filter.rs b/tests/test_filter.rs index 76719a5..223ab29 100644 --- a/tests/test_filter.rs +++ b/tests/test_filter.rs @@ -63,6 +63,66 @@ mod tests { use super::*; // ¬Include && ¬Exclude + #[test] + fn test_exclude_by_file_name() { + let path = Path::new("/Users/test/code2prompt/output.txt"); + let include_patterns = vec![]; + let exclude_patterns = vec!["output.txt".to_string()]; + let include_priority = false; + + assert!(!should_include_file( + &path, + &include_patterns, + &exclude_patterns, + include_priority + )); + } + + #[test] + fn test_exclude_by_glob_pattern() { + let path = Path::new("/Users/test/code2prompt/output.txt"); + let include_patterns = vec![]; + let exclude_patterns = vec!["**/output.txt".to_string()]; + let include_priority = false; + + assert!(!should_include_file( + &path, + &include_patterns, + &exclude_patterns, + include_priority + )); + } + + #[test] + fn test_include_by_file_name() { + let path = Path::new("/Users/test/code2prompt/output.txt"); + let include_patterns = vec!["output.txt".to_string()]; + let exclude_patterns = vec![]; + let include_priority = true; + + assert!(should_include_file( + &path, + &include_patterns, + &exclude_patterns, + include_priority + )); + } + + #[test] + fn test_include_and_exclude_conflict() { + let path = Path::new("/Users/test/code2prompt/output.txt"); + let include_patterns = vec!["output.txt".to_string()]; + let exclude_patterns = vec!["**/output.txt".to_string()]; + let include_priority = true; + + assert!(should_include_file( + &path, + &include_patterns, + &exclude_patterns, + include_priority + )); + } + #[test] fn test_no_include_no_exclude_path() { let path = Path::new("src/main.rs");