Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix file filtering logic to handle file name patterns like output.txt #64

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "code2prompt"
version = "2.0.0"
version = "2.0.1"
authors = ["Mufeed VH <[email protected]>","Olivier D'Ancona <[email protected]>"]
description = "A command-line (CLI) tool to generate an LLM prompt from codebases of any size, fast."
keywords = ["code", "prompt", "llm", "gpt", "ai"]
Expand Down
35 changes: 21 additions & 14 deletions src/filter.rs
ODAncona marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,22 +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| 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) {
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 60 additions & 0 deletions tests/test_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down