feat: add len and getAll to collections

This commit is contained in:
dualfroz committed 2026-09-01 22:50:51 +02:00
1 parent ca9299e7cb
commit 9f0ee59bf0
1 file changed
+152
+152
View File
@@ -322,3 +322,155 @@ pub const Hash = struct {
return true;
}
pub fn len(db: *Db, allocator: Allocator, name: []const u8) !usize {
const p = try prefix(allocator, name);
defer allocator.free(p);
var it = try db.scanPrefix(allocator, p);
defer it.deinit();
var count: usize = 0;
while (try it.next()) |_| count += 1;
return count;
}
/// Returns every field/value pair currently stored under `name`. Caller
/// owns the returned slice and each entry's `field`/`value` buffers.
pub fn getAll(db: *Db, allocator: Allocator, name: []const u8) ![]HashField {
const p = try prefix(allocator, name);
defer allocator.free(p);
var results = std.ArrayList(HashField).init(allocator);
errdefer {
for (results.items) |f| f.free(allocator);
results.deinit();
}
var it = try db.scanPrefix(allocator, p);
defer it.deinit();
while (try it.next()) |entry| {
const field = try allocator.dupe(u8, entry.key[p.len..]);
errdefer allocator.free(field);
const value = try allocator.dupe(u8, entry.value);
try results.append(.{ .field = field, .value = value });
}
return results.toOwnedSlice();
}
};
// ---------------------------------------------------------------------
// Set
// ---------------------------------------------------------------------
pub const Set = struct {
fn prefix(allocator: Allocator, name: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(nsTag);
try appendSegment(&out, "S");
try appendSegment(&out, name);
return out.toOwnedSlice();
}
fn memberKey(allocator: Allocator, name: []const u8, member: []const u8) ![]u8 {
var out = std.ArrayList(u8).init(allocator);
errdefer out.deinit();
try out.append(nsTag);
try appendSegment(&out, "S");
try appendSegment(&out, name);
try out.appendSlice(member);
return out.toOwnedSlice();
}
/// Returns true if the member was newly added (false if already present).
pub fn add(db: *Db, allocator: Allocator, name: []const u8, member: []const u8) !bool {
if (try isMember(db, allocator, name, member)) return false;
const k = try memberKey(allocator, name, member);
defer allocator.free(k);
try db.put(k, &.{});
return true;
}
/// Returns true if the member was present and has been removed.
pub fn remove(db: *Db, allocator: Allocator, name: []const u8, member: []const u8) !bool {
if (!try isMember(db, allocator, name, member)) return false;
const k = try memberKey(allocator, name, member);
defer allocator.free(k);
try db.delete(k);
return true;
}
pub fn isMember(db: *Db, allocator: Allocator, name: []const u8, member: []const u8) !bool {
const k = try memberKey(allocator, name, member);
defer allocator.free(k);
const raw = try db.get(allocator, k) orelse return false;
allocator.free(raw);
return true;
}
pub fn card(db: *Db, allocator: Allocator, name: []const u8) !usize {
const members_ = try members(db, allocator, name);
defer {
for (members_) |m| allocator.free(m);
allocator.free(members_);
}
return members_.len;
}
/// Returns every member currently in the set. Caller owns the slice and
/// each element.
pub fn members(db: *Db, allocator: Allocator, name: []const u8) ![][]u8 {
const p = try prefix(allocator, name);
defer allocator.free(p);
var results = std.ArrayList([]u8).init(allocator);
errdefer {
for (results.items) |m| allocator.free(m);
results.deinit();
}
var it = try db.scanPrefix(allocator, p);
defer it.deinit();
while (try it.next()) |entry| {
try results.append(try allocator.dupe(u8, entry.key[p.len..]));
}
return results.toOwnedSlice();
}
fn containsSorted(haystack: [][]const u8, needle: []const u8) bool {
for (haystack) |h| {
if (std.mem.eql(u8, h, needle)) return true;
}
return false;
}
/// Members present in every one of `names`. Caller owns the result.
pub fn intersect(db: *Db, allocator: Allocator, names: []const []const u8) ![][]u8 {
var results = std.ArrayList([]u8).init(allocator);
errdefer {
for (results.items) |m| allocator.free(m);
results.deinit();
}
if (names.len == 0) return results.toOwnedSlice();
const first = try members(db, allocator, names[0]);
defer {
for (first) |m| allocator.free(m);
allocator.free(first);
}
outer: for (first) |candidate| {
for (names[1..]) |other| {
if (!try isMember(db, allocator, other, candidate)) continue :outer;
}
try results.append(try allocator.dupe(u8, candidate));
}
return results.toOwnedSlice();
}
/// Members present in any of `names`, deduplicated. Caller owns the result.
pub fn unionOf(db: *Db, allocator: Allocator, names: []const []const u8) ![][]u8 {
var results = std.ArrayList([]u8).init(allocator);
errdefer {
for (results.items) |m| allocator.free(m);
results.deinit();
}