Created
April 18, 2025 07:31
-
-
Save raspberrypisig/8fa5f940ffa9907c14b5781aa29f0b04 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
// Import the zf library module we added in build.zig | |
const zf = @import("zf"); | |
// Helper function to recursively find all file paths relative to the start_dir | |
// It appends duplicated paths to the provided ArrayList. | |
fn getAllFilePathsRecursive( | |
allocator: std.mem.Allocator, | |
base_dir: std.fs.Dir, // The directory we are currently iterating in | |
current_rel_path: []const u8, // Path relative to the *initial* start directory (e.g., ".") | |
paths_list: *std.ArrayList([]const u8), // List to append found file paths to | |
) !void { | |
// Use iterate for potentially better performance on some systems | |
var dir_iter = base_dir.iterate(); | |
// Iterate through entries in the current directory | |
while (try dir_iter.next()) |entry| { | |
// Construct the path relative to the *initial* starting directory | |
// Avoid joining if we are in the root (".") to prevent paths like "./filename" | |
const entry_rel_path = if (std.mem.eql(u8, current_rel_path, ".")) | |
entry.name | |
else // Otherwise, join the current relative path with the new entry name | |
try std.fs.path.join(allocator, &.{ current_rel_path, entry.name }); | |
// If std.fs.path.join allocated memory, ensure it's freed after use | |
const free_entry_rel_path = !std.mem.eql(u8, current_rel_path, "."); | |
if (free_entry_rel_path) defer allocator.free(entry_rel_path); | |
switch (entry.kind) { | |
.File => { | |
// If it's a file, duplicate its relative path and add it to our list | |
try paths_list.append(try allocator.dupe(u8, entry_rel_path)); | |
}, | |
.Directory => { | |
// If it's a directory, try to open it and recurse | |
var sub_dir = base_dir.openDir(entry.name, .{ .iterate = true }) catch |err| { | |
// Handle errors like permission denied gracefully | |
std.debug.print("Warning: Could not open directory '{s}': {s}. Skipping.\n", .{ entry_rel_path, @errorName(err) }); | |
// Skip this directory if not accessible | |
continue; | |
}; | |
// Ensure the subdirectory handle is closed even if recursion fails | |
defer sub_dir.close(); | |
// Recurse into the subdirectory, passing the *new relative path* | |
try getAllFilePathsRecursive(allocator, sub_dir, entry_rel_path, paths_list); | |
}, | |
else => { | |
// Ignore other entry types like symlinks, block devices, etc. for this example | |
}, | |
} | |
} | |
} | |
pub fn main() !void { | |
// Standard allocator setup | |
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | |
defer _ = gpa.deinit(); // Ensure cleanup | |
const allocator = gpa.allocator(); | |
// --- Configuration --- | |
// Define the prefix to search for. This should be relative to the current working directory. | |
// Example uses the structure provided in the prompt. | |
const file_prefix = "src/zf"; | |
// IMPORTANT: Assumes Unix-style path separators ('/') based on your example. | |
// For cross-platform compatibility, consider normalizing separators. | |
const cwd_path = try std.fs.cwd().realpathAlloc(allocator, "."); | |
defer allocator.free(cwd_path); | |
std.debug.print("Searching from CWD: {s}\n", .{cwd_path}); | |
std.debug.print("Looking for files starting with relative path: \"{s}\"\n", .{file_prefix}); | |
// --- Step 1: Get all file paths recursively from CWD --- | |
var all_paths = std.ArrayList([]const u8).init(allocator); | |
defer { | |
// Free the duplicated path strings stored in the list | |
for (all_paths.items) |p| allocator.free(p); | |
all_paths.deinit(); // Free the list itself | |
} | |
const cwd = std.fs.cwd(); | |
// Start the recursive search from the current working directory (".") | |
try getAllFilePathsRecursive(allocator, cwd, ".", &all_paths); | |
std.debug.print("\nFound {d} total files recursively.\n", .{all_paths.items.len}); | |
// --- Method 1: Standard Zig Prefix Matching (Exact Match) --- | |
std.debug.print("\n--- Method 1: Files matching prefix (std.mem.startsWith + path check) ---\n", .{}); | |
var found_count_prefix: usize = 0; | |
for (all_paths.items) |path| { | |
// Check if the path string starts with the defined prefix | |
if (std.mem.startsWith(u8, path, file_prefix)) { | |
// Additionally, ensure it's either an exact match for the prefix, | |
// or the character immediately following the prefix is a path separator. | |
// This prevents matching "src/zfolder_extra" if the prefix is "src/zf". | |
if (path.len == file_prefix.len or path[file_prefix.len] == std.fs.path.sep) { | |
std.debug.print(" [Prefix Match] {s}\n", .{path}); | |
found_count_prefix += 1; | |
} | |
} | |
} | |
if (found_count_prefix == 0) { | |
std.debug.print(" (No files found with this exact prefix structure)\n", .{}); | |
} | |
// --- Method 2: Using zf.rank for Filtering (Fuzzy Match) --- | |
// This uses the zf library as requested, but provides fuzzy matching. | |
std.debug.print("\n--- Method 2: Files ranked by zf against prefix query (zf.rank, fuzzy) ---\n", .{}); | |
std.debug.print(" (Lower rank score is better. Shows all files that match the query fuzzily.)\n", .{}); | |
// Treat the prefix as a single query token for zf | |
const query_tokens = [_][]const u8{file_prefix}; | |
var ranked_paths = std.ArrayList(struct { | |
path: []const u8, // Points to memory owned by all_paths | |
rank: f64, | |
}).init(allocator); | |
// No need to free path strings here, as they are owned by `all_paths` | |
defer ranked_paths.deinit(); | |
for (all_paths.items) |path| { | |
// Configure zf.rank: | |
// .plain = false -> Use path-specific ranking logic (favors filename matches, etc.) | |
// .to_lower = true -> Case-insensitive matching (typical for fuzzy finders) | |
const rank_options = zf.RankOptions{ .plain = false, .to_lower = true }; | |
// Call zf.rank. It returns the rank score (f64) if matched, or null otherwise. | |
if (zf.rank(path, &query_tokens, rank_options)) |rank_value| { | |
// A match was found (rank_value is not null) | |
try ranked_paths.append(.{ .path = path, .rank = rank_value }); | |
} | |
} | |
// Sort the fuzzy matches by rank (lower score is better) to show the best matches first | |
std.sort.block(struct { path: []const u8, rank: f64 }, ranked_paths.items, {}, struct { | |
fn lessThan(_: void, a: anytype, b: anytype) bool { | |
// Sort primarily by rank (ascending) | |
if (a.rank < b.rank) return true; | |
if (a.rank > b.rank) return false; | |
// As a tie-breaker, sort by path alphabetically | |
return std.mem.lessThan(u8, a.path, b.path); | |
} | |
}.lessThan); | |
if (ranked_paths.items.len == 0) { | |
std.debug.print(" (No files found matching the query '{s}' using zf.rank)\n", .{file_prefix}); | |
} else { | |
std.debug.print(" (Showing top matches ranked by zf):\n", .{}); | |
// Limit output for demonstration purposes if many files match fuzzily | |
const limit = @min(ranked_paths.items.len, 20); | |
for (ranked_paths.items[0..limit]) |ranked| { | |
// Print the rank score and the path | |
std.debug.print(" [zf Rank: {d:.2f}] {s}\n", .{ ranked.rank, ranked.path }); | |
} | |
if (ranked_paths.items.len > limit) { | |
std.debug.print(" (... and {d} more)\n", .{ranked_paths.items.len - limit}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment