From e0a612c356689c2e962e689b268bea4b62865bce Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Sat, 11 Jan 2025 11:53:18 -0600 Subject: [PATCH 1/8] build: :heavy_plus_sign: add mvzr --- build.zig | 6 ++++++ build.zig.zon | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 build.zig.zon diff --git a/build.zig b/build.zig index 41101a6..168f680 100644 --- a/build.zig +++ b/build.zig @@ -15,6 +15,10 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); + // Add mvzr dependency and create module. + const mvzr_dep = b.dependency("mvzr", .{ .target = target, .optimize = optimize }); + const mvzr_module = mvzr_dep.module("mvzr"); + const exe = b.addExecutable(.{ .name = "scoop-search", // In this case the main source file is merely a path, however, in more @@ -23,6 +27,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + exe.root_module.addImport("mvzr", mvzr_module); // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default @@ -59,6 +64,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + unit_tests.root_module.addImport("mvzr", mvzr_module); const run_unit_tests = b.addRunArtifact(unit_tests); diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..3e117d9 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,39 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = "scoop-search", + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + .mvzr = .{ + .url = "https://github.com/mnemnion/mvzr/archive/refs/tags/v0.3.2.tar.gz", + .hash = "122084c73b4208fdfb02ee2c839e8e204d4b1d93421fecbf1463df96bb4e8a776491", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} From d396761cbb89d100df713946eb50d7ad69ea8bce Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Sat, 11 Jan 2025 12:08:53 -0600 Subject: [PATCH 2/8] feat: :sparkles: add regex support for package and bin searching --- src/search.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/search.zig b/src/search.zig index a926058..e8b4eb7 100644 --- a/src/search.zig +++ b/src/search.zig @@ -2,6 +2,7 @@ const std = @import("std"); const utils = @import("utils.zig"); const Box = utils.Box; const DebugLogger = utils.DebugLogger; +const mvzr = @import("mvzr"); /// State associated with a worker thread. Stores thread local cache and matches. Has its own allocator. const ThreadPoolState = struct { @@ -162,6 +163,13 @@ fn checkBin(allocator: std.mem.Allocator, bin: []const u8, query: []const u8, st try matches.append(try SearchMatch.init(allocator, stem, version, against.withExt)); return true; } + + const regex = mvzr.compile(query); + if (regex != null and regex.?.isMatch(lowerBinStem)) { + try matches.append(try SearchMatch.init(allocator, stem, version, against.withExt)); + return true; + } + return false; } @@ -197,8 +205,10 @@ fn matchPackageAux(packagesDir: std.fs.Dir, query: []const u8, manifestName: []c const lowerStem = try std.ascii.allocLowerString(allocator, stem); defer allocator.free(lowerStem); + const regex = mvzr.compile(query); + // does the package name match? - if (query.len == 0 or std.mem.containsAtLeast(u8, lowerStem, 1, query)) { + if (query.len == 0 or std.mem.containsAtLeast(u8, lowerStem, 1, query) or (regex != null and regex.?.isMatch(lowerStem))) { try state.matches.append(try SearchMatch.init(allocator, stem, version, null)); } else { // the name did not match, lets see if any binary files do From 1ea920c492d2c39647805dd312de3a5f320609bb Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Sat, 11 Jan 2025 12:11:41 -0600 Subject: [PATCH 3/8] build: :bookmark: add current version tag to build.zig.zon --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index 3e117d9..233b499 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -10,7 +10,7 @@ // This is a [Semantic Version](https://semver.org/). // In a future version of Zig it will be used for package deduplication. - .version = "0.0.0", + .version = "1.5.0", // This field is optional. // This is currently advisory only; Zig does not yet do anything From 3cff76e8d6e4fca0c912291f5bd51a14a4a93906 Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Wed, 15 Jan 2025 12:39:50 -0600 Subject: [PATCH 4/8] chore: :coffin: remove unused comments from build.zig.zon paths --- build.zig.zon | 3 --- 1 file changed, 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 233b499..71572fa 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -32,8 +32,5 @@ "build.zig", "build.zig.zon", "src", - // For example... - //"LICENSE", - //"README.md", }, } From 1f5522afa24a51dd39f76ab95f81abea035c4dc1 Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Wed, 15 Jan 2025 12:53:41 -0600 Subject: [PATCH 5/8] refactor: :zap: default to regex, compile early, bail on bad regex --- src/main.zig | 8 +++++++- src/search.zig | 20 ++++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/main.zig b/src/main.zig index fb2f0c0..3ed9e13 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,6 +5,7 @@ const env = @import("env.zig"); const utils = @import("utils.zig"); const search = @import("search.zig"); const ThreadPool = @import("thread_pool.zig").ThreadPool; +const mvzr = @import("mvzr"); /// Stores results of a search in a single bucket. const SearchResult = struct { @@ -46,6 +47,11 @@ pub fn main() !void { const query = try std.ascii.allocLowerString(allocator, args.query orelse ""); defer allocator.free(query); + const regexQuery = mvzr.compile(query); + if (regexQuery == null) { + return std.io.getStdErr().writer().print("Invalid regular expression: parsing \"{s}\".", .{query}); + } + const scoopHome = env.scoopHomeOwned(allocator, debug) catch |err| switch (err) { error.MissingHomeDir => { return std.io.getStdErr().writer().print("Could not establish scoop home directory. USERPROFILE environment variable is not defined.\n", .{}); @@ -79,7 +85,7 @@ pub fn main() !void { defer allocator.free(bucketBase); try debug.log("Found bucket: {s}\n", .{bucketBase}); - const result = search.searchBucket(allocator, query, bucketBase, debug) catch { + const result = search.searchBucket(allocator, regexQuery.?, bucketBase, debug) catch { try std.io.getStdErr().writer().print("Failed to search through the bucket: {s}.\n", .{f.name}); continue; }; diff --git a/src/search.zig b/src/search.zig index e8b4eb7..b7b1f84 100644 --- a/src/search.zig +++ b/src/search.zig @@ -120,7 +120,7 @@ fn getPackagesDir(allocator: std.mem.Allocator, bucketBase: []const u8) !std.fs. return packages; } -pub fn searchBucket(allocator: std.mem.Allocator, query: []const u8, bucketBase: []const u8, debug: DebugLogger) !SearchResult { +pub fn searchBucket(allocator: std.mem.Allocator, query: mvzr.Regex, bucketBase: []const u8, debug: DebugLogger) !SearchResult { var tp: ThreadPool = undefined; try tp.init(.{ .allocator = allocator }, ThreadPoolState.create); try debug.log("Worker count: {}\n", .{tp.threads.len}); @@ -154,18 +154,12 @@ pub fn searchBucket(allocator: std.mem.Allocator, query: []const u8, bucketBase: } /// If the given binary name matches the query, add it to the matches. -fn checkBin(allocator: std.mem.Allocator, bin: []const u8, query: []const u8, stem: []const u8, version: []const u8, matches: *std.ArrayList(SearchMatch)) !bool { +fn checkBin(allocator: std.mem.Allocator, bin: []const u8, query: mvzr.Regex, stem: []const u8, version: []const u8, matches: *std.ArrayList(SearchMatch)) !bool { const against = utils.basename(bin); const lowerBinStem = try std.ascii.allocLowerString(allocator, against.withoutExt); defer allocator.free(lowerBinStem); - if (std.mem.containsAtLeast(u8, lowerBinStem, 1, query)) { - try matches.append(try SearchMatch.init(allocator, stem, version, against.withExt)); - return true; - } - - const regex = mvzr.compile(query); - if (regex != null and regex.?.isMatch(lowerBinStem)) { + if (query.isMatch(lowerBinStem)) { try matches.append(try SearchMatch.init(allocator, stem, version, against.withExt)); return true; } @@ -173,13 +167,13 @@ fn checkBin(allocator: std.mem.Allocator, bin: []const u8, query: []const u8, st return false; } -fn matchPackage(packagesDir: std.fs.Dir, query: []const u8, manifestName: []const u8, state: *ThreadPoolState) void { +fn matchPackage(packagesDir: std.fs.Dir, query: mvzr.Regex, manifestName: []const u8, state: *ThreadPoolState) void { // ignore failed match matchPackageAux(packagesDir, query, manifestName, state) catch return; } /// A worker function for checking if a given manifest matches the query. -fn matchPackageAux(packagesDir: std.fs.Dir, query: []const u8, manifestName: []const u8, state: *ThreadPoolState) !void { +fn matchPackageAux(packagesDir: std.fs.Dir, query: mvzr.Regex, manifestName: []const u8, state: *ThreadPoolState) !void { const allocator = state.allocator.ptr.allocator(); const extension = comptime ".json"; @@ -205,10 +199,8 @@ fn matchPackageAux(packagesDir: std.fs.Dir, query: []const u8, manifestName: []c const lowerStem = try std.ascii.allocLowerString(allocator, stem); defer allocator.free(lowerStem); - const regex = mvzr.compile(query); - // does the package name match? - if (query.len == 0 or std.mem.containsAtLeast(u8, lowerStem, 1, query) or (regex != null and regex.?.isMatch(lowerStem))) { + if (query.isMatch(lowerStem)) { try state.matches.append(try SearchMatch.init(allocator, stem, version, null)); } else { // the name did not match, lets see if any binary files do From 366a7930fc5d1e1d0993aaa5a0929f44a4a12f01 Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Wed, 15 Jan 2025 14:27:59 -0600 Subject: [PATCH 6/8] refactor: :recycle: clean up failed regex compilation w/ use of orelse --- src/main.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.zig b/src/main.zig index 3ed9e13..e36f477 100644 --- a/src/main.zig +++ b/src/main.zig @@ -47,10 +47,8 @@ pub fn main() !void { const query = try std.ascii.allocLowerString(allocator, args.query orelse ""); defer allocator.free(query); - const regexQuery = mvzr.compile(query); - if (regexQuery == null) { + const regexQuery = mvzr.compile(query) orelse return std.io.getStdErr().writer().print("Invalid regular expression: parsing \"{s}\".", .{query}); - } const scoopHome = env.scoopHomeOwned(allocator, debug) catch |err| switch (err) { error.MissingHomeDir => { From f2f383b2e3aa0b3fdee59c46c4bbed28d1c49fb3 Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Wed, 15 Jan 2025 14:35:26 -0600 Subject: [PATCH 7/8] fix: :green_heart: use LF in build.zig.zon --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index ef01f61..8984289 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.zig text eol=lf +*.zig.zon text eol=lf From ec69c2b501195c1a0076be2e6b9e65bd88658c52 Mon Sep 17 00:00:00 2001 From: Evan Elias Young Date: Wed, 15 Jan 2025 14:41:10 -0600 Subject: [PATCH 8/8] fix: :rotating_light: remove .? from `regexQuery` for `searchBucket` --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index e36f477..10c71ec 100644 --- a/src/main.zig +++ b/src/main.zig @@ -83,7 +83,7 @@ pub fn main() !void { defer allocator.free(bucketBase); try debug.log("Found bucket: {s}\n", .{bucketBase}); - const result = search.searchBucket(allocator, regexQuery.?, bucketBase, debug) catch { + const result = search.searchBucket(allocator, regexQuery, bucketBase, debug) catch { try std.io.getStdErr().writer().print("Failed to search through the bucket: {s}.\n", .{f.name}); continue; };