Skip to content

Commit

Permalink
feat: 增強文件過濾功能並更新測試
Browse files Browse the repository at this point in the history
- ✨ 改進文件過濾邏輯,增加對通配符的支持
- ✅ 更新集成測試以使用新的命令行參數
- ✅ 更新單元測試以使用通配符模式
  • Loading branch information
jeromeleong committed Aug 30, 2024
1 parent 1ba04ca commit 60e35fe
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 41 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ c2p 簡化了 code2prompt 的操作方式,同時也修復了一些在使用時
### 命令行配置的改動
- 增加 `-l / --lang` 配置,設定AI 回答的語言,如`--lang zh-hant`是使用繁體中文回答。如沒有使用`-l`,會自動進入語言選擇介面
- 增加 `--hbs <.hbs path>` 配置,用於使用自定義 Handlebars 模板文件
- 增加 `--in <regex>,<regex>` 配置,用正則表達式代替`--include <String>`原有方法
- 增加 `--in` 配置,同原有的`--include`
- 增加 `--nor` 配置,同原有的`--exclude`
- 增加 `--nor <regex>,<regex>` 配置,用正則表達式代替`--exclude <String>`原有方法
- 修改 `-t / --template <template name>` 配置,專注於使用預定義的 Handlebars 模板文件
- 直接使用`-t`而不填寫`<template name>`,會顯示所有預定義的 Handlebars 模板文件,並提供交互的方式來選擇使用相關 Handlebars 模板。
Expand Down
60 changes: 42 additions & 18 deletions src/filter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
//! This module contains the logic for filtering files based on include and exclude patterns.

use log::error;
use log::{debug, error, warn};
use regex::Regex;
use std::fs;
use std::path::Path;
Expand All @@ -23,22 +21,40 @@ pub fn should_include_file(
exclude_patterns: &[String],
include_priority: bool,
) -> bool {
let canonical_path = match fs::canonicalize(path) {
Ok(path) => path,
let path_str = match fs::canonicalize(path) {
Ok(canonical_path) => canonical_path.to_string_lossy().into_owned(),
Err(e) => {
error!("無法正規化路徑: {}", e);
return false;
warn!("無法正規化路徑: {}, 使用原始路徑", e);
path.to_string_lossy().into_owned()
}
};
let path_str = canonical_path.to_str().unwrap_or("");

let included = include_patterns.is_empty()
|| include_patterns
.iter()
.any(|pattern| matches_regex(path_str, pattern));
let excluded = exclude_patterns
.iter()
.any(|pattern| matches_regex(path_str, pattern));
let included = if include_patterns.is_empty() {
true // 如果沒有包含模式,默認包含所有文件
} else {
include_patterns.iter().any(|pattern| {
let matches = matches_pattern(&path_str, pattern);
debug!(
"Include pattern '{}' matches path '{}': {}",
pattern, path_str, matches
);
matches
})
};

let excluded = exclude_patterns.iter().any(|pattern| {
let matches = matches_pattern(&path_str, pattern);
debug!(
"Exclude pattern '{}' matches path '{}': {}",
pattern, path_str, matches
);
matches
});

debug!(
"Path: {}, Included: {}, Excluded: {}, Include Priority: {}",
path_str, included, excluded, include_priority
);

match (included, excluded) {
(true, true) => include_priority,
Expand All @@ -48,11 +64,19 @@ pub fn should_include_file(
}
}

fn matches_regex(path: &str, pattern: &str) -> bool {
Regex::new(pattern)
fn matches_pattern(path: &str, pattern: &str) -> bool {
let regex_pattern = convert_wildcard_to_regex(pattern);
Regex::new(&regex_pattern)
.map(|re| re.is_match(path))
.unwrap_or_else(|e| {
error!("無效的正則表達式 '{}': {}", pattern, e);
error!("無效的正則表達式 '{}': {}", regex_pattern, e);
false
})
}

fn convert_wildcard_to_regex(pattern: &str) -> String {
pattern
.replace(".", r"\.")
.replace("*", ".*")
.replace("?", ".")
}
27 changes: 14 additions & 13 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,12 @@ mod tests {
}

fn command(&self) -> Command {
let mut cmd =
Command::cargo_bin("code2prompt").expect("Failed to find code2prompt binary");
cmd.arg(&self.dir.path().to_str().unwrap())
let mut cmd = Command::cargo_bin("c2p").expect("Failed to find code2prompt binary");
cmd.arg("path")
.arg(&self.dir.path().to_str().unwrap())
.arg("--output")
.arg(&self.output_file)
.arg("--lang=en")
.arg("--no-clipboard");
cmd
}
Expand All @@ -107,7 +108,7 @@ mod tests {
fn test_include_extensions() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--include=*.py").assert().success();
cmd.arg("--in=*.py").assert().success();

let output = env.read_output();
debug!("Test include extensions output:\n{}", output);
Expand All @@ -122,7 +123,7 @@ mod tests {
fn test_exclude_extensions() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--exclude=*.txt").assert().success();
cmd.arg("--nor=*.txt").assert().success();

let output = env.read_output();
debug!("Test exclude files output:\n{}", output);
Expand All @@ -138,7 +139,7 @@ mod tests {
fn test_include_files() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--include=**/foo.py,**/bar.py").assert().success();
cmd.arg("--in=**/foo.py,**/bar.py").assert().success();

let output = env.read_output();
debug!("Test include files output:\n{}", output);
Expand All @@ -154,7 +155,7 @@ mod tests {
fn test_include_folders() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--include=**/lowercase/**").assert().success();
cmd.arg("--in=**/lowercase/**").assert().success();

let output = env.read_output();
debug!("Test include folders output:\n{}", output);
Expand All @@ -169,7 +170,7 @@ mod tests {
fn test_exclude_files() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--exclude=**/foo.py,**/bar.py").assert().success();
cmd.arg("--nor=**/foo.py,**/bar.py").assert().success();

let output = env.read_output();
debug!("Test exclude files output:\n{}", output);
Expand All @@ -185,7 +186,7 @@ mod tests {
fn test_exclude_folders() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--exclude=**/uppercase/**").assert().success();
cmd.arg("--nor=**/uppercase/**").assert().success();

let output = env.read_output();
debug!("Test exclude folders output:\n{}", output);
Expand All @@ -200,8 +201,8 @@ mod tests {
fn test_include_exclude_with_include_priority() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--include=*.py,**/lowercase/**")
.arg("--exclude=**/foo.py,**/uppercase/**")
cmd.arg("--in=*.py,**/lowercase/**")
.arg("--nor=**/foo.py,**/uppercase/**")
.arg("--include-priority")
.assert()
.success();
Expand All @@ -220,8 +221,8 @@ mod tests {
fn test_include_exclude_with_exclude_priority() {
let env = TestEnv::new();
let mut cmd = env.command();
cmd.arg("--include=*.py,**/lowercase/**")
.arg("--exclude=**/foo.py,**/uppercase/**")
cmd.arg("--in=*.py,**/lowercase/**")
.arg("--nor=**/foo.py,**/uppercase/**")
.assert()
.success();

Expand Down
18 changes: 9 additions & 9 deletions tests/test_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ mod tests {
fn test_include_patterns() {
let base_path = TEST_DIR.path();

let include_patterns = vec![r".*\.py$".to_string()];
let include_patterns = vec!["*.py".to_string()];
let exclude_patterns = vec![];
let include_priority = false;

Expand Down Expand Up @@ -139,7 +139,7 @@ mod tests {
let base_path = TEST_DIR.path();

let include_patterns = vec![];
let exclude_patterns = vec![r".*\.txt$".to_string()];
let exclude_patterns = vec!["*.txt".to_string()];
let include_priority = false;

for file in [
Expand Down Expand Up @@ -181,7 +181,7 @@ mod tests {
fn test_include_files() {
let base_path = TEST_DIR.path();

let include_patterns = vec![r".*/foo\.py$".to_string(), r".*/bar\.py$".to_string()];
let include_patterns = vec!["*/foo.py".to_string(), r"*/bar.py".to_string()];
let exclude_patterns = vec![];
let include_priority = false;

Expand Down Expand Up @@ -222,7 +222,7 @@ mod tests {
let base_path = TEST_DIR.path();

let include_patterns = vec![];
let exclude_patterns = vec![r".*/foo\.py$".to_string(), r".*/bar\.py$".to_string()];
let exclude_patterns = vec!["*/foo.py".to_string(), "*/bar.py".to_string()];
let include_priority = false;

for file in ["lowercase/foo.py", "lowercase/bar.py"] {
Expand Down Expand Up @@ -261,8 +261,8 @@ mod tests {
fn test_include_exclude_conflict_file() {
let base_path = TEST_DIR.path();

let include_patterns = vec![r".*/foo\.py$".to_string()];
let exclude_patterns = vec![r".*/foo\.py$".to_string()];
let include_patterns = vec!["*/foo.py".to_string()];
let exclude_patterns = vec!["*/foo.py".to_string()];
let include_priority = true;

for file in ["lowercase/foo.py"] {
Expand Down Expand Up @@ -302,8 +302,8 @@ mod tests {
fn test_include_exclude_conflict_extension() {
let base_path = TEST_DIR.path();

let include_patterns = vec![r".*\.py$".to_string()];
let exclude_patterns = vec![r".*\.py$".to_string()];
let include_patterns = vec!["*.py".to_string()];
let exclude_patterns = vec!["*.py".to_string()];
let include_priority = true;

for file in [
Expand Down Expand Up @@ -359,7 +359,7 @@ mod tests {
fn test_should_exclude_file_with_patterns() {
let path = Path::new("src/main.rs");
let include_patterns: Vec<String> = vec![];
let exclude_patterns: Vec<String> = vec![r".*\.rs$".to_string()];
let exclude_patterns: Vec<String> = vec!["*.rs".to_string()];
let include_priority = false;
assert!(!should_include_file(
&path,
Expand Down

0 comments on commit 60e35fe

Please sign in to comment.