wip: "full" findr

Creating direct equivilant of fd for performance testing, before reducing
scope to needed features.
This commit is contained in:
2026-06-17 10:18:50 -04:00
parent dd522de02c
commit 54c92ed1fc
4 changed files with 57 additions and 26 deletions

View File

@@ -38,16 +38,17 @@ echo
# --- file counts ---
echo "=== File counts ==="
printf " findr --ignored : %8d\n" "$("$FINDR" --ignored "$TARGET" 2>/dev/null | wc -l)"
printf " fd -a . : %8d\n" "$(fd -a . "$TARGET" 2>/dev/null | wc -l)"
printf " findr : %8d\n" "$("$FINDR" "$TARGET" 2>/dev/null | wc -l)"
echo
printf " fd . -t f --exclude .git : %8d\n" "$(fd . -t f --exclude .git "$TARGET" 2>/dev/null | wc -l)"
printf " findr --no-hidden : %8d\n" "$("$FINDR" --no-hidden "$TARGET" 2>/dev/null | wc -l)"
printf " fd -a -H . : %8d\n" "$(fd -a -H . "$TARGET" 2>/dev/null | wc -l)"
printf " findr -H : %8d\n" "$("$FINDR" -H "$TARGET" 2>/dev/null | wc -l)"
echo
printf " fd . -H -t f --exclude .git : %8d\n" "$(fd . -H -t f --exclude .git "$TARGET" 2>/dev/null | wc -l)"
printf " findr (respect) : %8d\n" "$("$FINDR" "$TARGET" 2>/dev/null | wc -l)"
printf " fd -a -HI . : %8d\n" "$(fd -a -HI . "$TARGET" 2>/dev/null | wc -l)"
printf " findr -HI : %8d\n" "$("$FINDR" -HI "$TARGET" 2>/dev/null | wc -l)"
echo
printf " fd . -HI -t f --exclude .git: %8d\n" "$(fd . -HI -t f --exclude .git "$TARGET" 2>/dev/null | wc -l)"
printf " findr -I (all) : %8d\n" "$("$FINDR" -I "$TARGET" 2>/dev/null | wc -l)"
printf " fd -a -E .git . : %8d\n" "$(fd -a -E .git . "$TARGET" 2>/dev/null | wc -l)"
printf " findr -E .git : %8d\n" "$("$FINDR" -E .git "$TARGET" 2>/dev/null | wc -l)"
echo
# --- benchmarks ---
@@ -57,13 +58,14 @@ hyperfine \
--warmup 2 \
--runs 5 \
--export-markdown "$RESULTS_FILE" \
"$FINDR --ignored \"$TARGET\" > /dev/null" \
"fd . -t f --exclude .git \"$TARGET\" > /dev/null" \
"$FINDR --no-hidden \"$TARGET\" > /dev/null" \
"fd . -H -t f --exclude .git \"$TARGET\" > /dev/null" \
"fd -a . \"$TARGET\" > /dev/null" \
"$FINDR \"$TARGET\" > /dev/null" \
"fd . -HI -t f --exclude .git \"$TARGET\" > /dev/null" \
"$FINDR -I \"$TARGET\" > /dev/null"
"fd -a -H . \"$TARGET\" > /dev/null" \
"$FINDR -H \"$TARGET\" > /dev/null" \
"fd -a -HI . \"$TARGET\" > /dev/null" \
"$FINDR -HI \"$TARGET\" > /dev/null" \
"fd -a -E .git . \"$TARGET\" > /dev/null" \
"$FINDR -E .git \"$TARGET\" > /dev/null"
echo
echo "=== Results written to $RESULTS_FILE ==="

View File

@@ -8,7 +8,7 @@ main :: proc() {
args := os.args
opts: WalkOptions
opts.include_hidden = true
opts.include_hidden = false
opts.ignore_mode = .Respected
excludes := make([dynamic]string)
@@ -22,12 +22,8 @@ main :: proc() {
for i < len(args) {
arg := args[i]
switch {
case arg == "-I":
opts.ignore_mode = .All
case arg == "--ignored":
opts.ignore_mode = .Ignored
case arg == "--no-hidden":
opts.include_hidden = false
case arg == "-E":
i += 1
if i < len(args) {
@@ -35,8 +31,17 @@ main :: proc() {
}
case strings.has_prefix(arg, "-E"):
append(&excludes, arg[2:])
case len(arg) > 0 && arg[0] == '-':
// unknown flag, skip
case len(arg) > 1 && arg[0] == '-':
for c, j in arg[1:] {
switch c {
case 'H':
opts.include_hidden = true
case 'I':
opts.ignore_mode = .All
case 'a':
// no-op: accepted for fd compatibility
}
}
case:
if pattern == "" {
pattern = arg

View File

@@ -63,7 +63,9 @@ test_dir_only_pattern :: proc(t: ^testing.T) {
create_dir(env, "repo/ignored_dir")
create_file(env, "repo/.gitignore", "ignored_dir/\n")
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Ignored}, {})
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Ignored}, {
"repo/ignored_dir/",
})
}
@(test)
@@ -158,7 +160,7 @@ test_nested_gitignore_respected_mode :: proc(t: ^testing.T) {
// important.log: un-ignored by nested negation → emitted
// debug.log: ignored by root → skipped
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Respected}, {
"repo/.gitignore", "repo/sub/.gitignore", "repo/sub/important.log",
"repo/", "repo/.gitignore", "repo/sub/", "repo/sub/.gitignore", "repo/sub/important.log",
})
}
@@ -253,7 +255,7 @@ test_all_mode_emits_all_files :: proc(t: ^testing.T) {
create_file(env, "repo/normal.txt")
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .All}, {
"repo/.env", "repo/.gitignore", "repo/secrets.env", "repo/normal.txt",
"repo/", "repo/.env", "repo/.gitignore", "repo/secrets.env", "repo/normal.txt",
})
}
@@ -268,7 +270,7 @@ test_all_mode_descends_everywhere :: proc(t: ^testing.T) {
create_file(env, "repo/build/output.txt")
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .All}, {
"repo/.gitignore", "repo/build/output.txt",
"repo/", "repo/.gitignore", "repo/build/", "repo/build/output.txt",
})
}
@@ -288,7 +290,7 @@ test_respected_mode_skips_gitignored :: proc(t: ^testing.T) {
create_file(env, "repo/normal.txt")
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Respected}, {
"repo/.gitignore", "repo/normal.txt",
"repo/", "repo/.gitignore", "repo/normal.txt",
})
}
@@ -304,7 +306,7 @@ test_respected_mode_prunes_ignored_dirs :: proc(t: ^testing.T) {
create_file(env, "repo/main.txt")
assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Respected}, {
"repo/.gitignore", "repo/main.txt",
"repo/", "repo/.gitignore", "repo/main.txt",
})
}

View File

@@ -230,6 +230,12 @@ process_dir :: proc(pool: ^WalkerPool, item: WorkItem) {
}
if is_dir {
if should_emit && matches_pattern(pool, entry.name) {
dir_path_out := join_path_dir(dir_path, entry.name)
sync.mutex_lock(&pool.results_mutex)
append(pool.results, dir_path_out)
sync.mutex_unlock(&pool.results_mutex)
}
if !ignored {
child_rel, _ := strings.clone(entry_rel)
child_path := join_path(dir_path, entry.name)
@@ -367,3 +373,19 @@ join_path :: proc(parent, child: string) -> string {
result, _ := strings.clone(s)
return result
}
join_path_dir :: proc(parent, child: string) -> string {
b: strings.Builder
strings.builder_init(&b)
defer strings.builder_destroy(&b)
fmt.sbprintf(&b, "%s", parent)
if len(parent) == 0 || parent[len(parent) - 1] != '/' {
fmt.sbprintf(&b, "/")
}
fmt.sbprintf(&b, "%s/", child)
s := strings.to_string(b)
result, _ := strings.clone(s)
return result
}