feat: extend main with boolFlag and runColl
This commit is contained in:
1 parent
02c5e6da3a
commit
bdeec4bfdf
1 file changed
+173
+173
@@ -832,6 +832,179 @@ fn fail(stderr: anytype, message: []const u8) !void {
|
||||
std.process.exit(2);
|
||||
}
|
||||
|
||||
fn boolFlag(v: bool) u8 {
|
||||
return if (v) 1 else 0;
|
||||
}
|
||||
|
||||
/// Dispatches `coll <kind> <op> [args...]` onto the Redis-like typed
|
||||
/// collections in collections.zig (Counter/Hash/Set/List/ZSet), which until
|
||||
/// now were only reachable through the library API, never a CLI.
|
||||
fn runColl(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, db: *cobalt.Db, rest: []const []const u8) !void {
|
||||
if (rest.len >= 1 and std.mem.eql(u8, rest[0], "expire-sweep")) {
|
||||
const reaped = try cobalt.collttl.sweepExpired(db, allocator, std.time.nanoTimestamp());
|
||||
try stdout.print("reaped: {d}\n", .{reaped});
|
||||
return;
|
||||
}
|
||||
if (rest.len < 2) return fail(stderr, "coll needs <kind> <op> ...");
|
||||
const kind = rest[0];
|
||||
const op = rest[1];
|
||||
const args = rest[2..];
|
||||
|
||||
if (std.mem.eql(u8, op, "expire") or std.mem.eql(u8, op, "ttl") or std.mem.eql(u8, op, "persist")) {
|
||||
const k = cobalt.CollectionKind.parse(kind) orelse return fail(stderr, "unknown collection kind (expected counter/hash/set/list/zset)");
|
||||
return runCollTtl(allocator, stdout, stderr, db, k, op, args);
|
||||
}
|
||||
|
||||
// Lazy expiry on the read/write path: every remaining op below takes the
|
||||
// collection name as args[0], so this is what keeps a bare
|
||||
// `coll hash get <name> ...` from ever reading a collection that is
|
||||
// logically already gone. `set inter|union|diff` accept further names
|
||||
// past args[0] - those get their own expireIfDueMany call right before
|
||||
// dispatch, so this single-name check only needs to cover args[0].
|
||||
if (args.len >= 1) {
|
||||
if (cobalt.CollectionKind.parse(kind)) |k| {
|
||||
_ = try cobalt.collttl.expireIfDue(db, allocator, k, args[0], std.time.nanoTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, kind, "counter")) {
|
||||
if (std.mem.eql(u8, op, "get")) {
|
||||
if (args.len < 1) return fail(stderr, "counter get needs <name>");
|
||||
const v = try cobalt.Counter.get(db, allocator, args[0]);
|
||||
try stdout.print("{d}\n", .{v});
|
||||
} else if (std.mem.eql(u8, op, "set")) {
|
||||
if (args.len < 2) return fail(stderr, "counter set needs <name> <value>");
|
||||
const v = std.fmt.parseInt(i64, args[1], 10) catch return fail(stderr, "invalid integer value");
|
||||
try cobalt.Counter.set(db, allocator, args[0], v);
|
||||
try stdout.writeAll("OK\n");
|
||||
} else if (std.mem.eql(u8, op, "incrby")) {
|
||||
if (args.len < 2) return fail(stderr, "counter incrby needs <name> <delta>");
|
||||
const delta = std.fmt.parseInt(i64, args[1], 10) catch return fail(stderr, "invalid integer delta");
|
||||
const v = try cobalt.Counter.incrBy(db, allocator, args[0], delta);
|
||||
try stdout.print("{d}\n", .{v});
|
||||
} else {
|
||||
return fail(stderr, "unknown counter operation");
|
||||
}
|
||||
} else if (std.mem.eql(u8, kind, "hash")) {
|
||||
if (std.mem.eql(u8, op, "set")) {
|
||||
if (args.len < 3) return fail(stderr, "hash set needs <name> <field> <value>");
|
||||
const created = try cobalt.Hash.set(db, allocator, args[0], args[1], args[2]);
|
||||
try stdout.print("{s}\n", .{if (created) "created" else "updated"});
|
||||
} else if (std.mem.eql(u8, op, "get")) {
|
||||
if (args.len < 2) return fail(stderr, "hash get needs <name> <field>");
|
||||
if (try cobalt.Hash.get(db, allocator, args[0], args[1])) |v| {
|
||||
defer allocator.free(v);
|
||||
try stdout.print("{s}\n", .{v});
|
||||
} else {
|
||||
try stdout.writeAll("(nil)\n");
|
||||
}
|
||||
} else if (std.mem.eql(u8, op, "del")) {
|
||||
if (args.len < 2) return fail(stderr, "hash del needs <name> <field>");
|
||||
const existed = try cobalt.Hash.del(db, allocator, args[0], args[1]);
|
||||
try stdout.print("{d}\n", .{boolFlag(existed)});
|
||||
} else if (std.mem.eql(u8, op, "len")) {
|
||||
if (args.len < 1) return fail(stderr, "hash len needs <name>");
|
||||
const n = try cobalt.Hash.len(db, allocator, args[0]);
|
||||
try stdout.print("{d}\n", .{n});
|
||||
} else if (std.mem.eql(u8, op, "getall")) {
|
||||
if (args.len < 1) return fail(stderr, "hash getall needs <name>");
|
||||
const fields = try cobalt.Hash.getAll(db, allocator, args[0]);
|
||||
defer {
|
||||
for (fields) |f| f.free(allocator);
|
||||
allocator.free(fields);
|
||||
}
|
||||
for (fields) |f| try stdout.print("{s}\t{s}\n", .{ f.field, f.value });
|
||||
} else {
|
||||
return fail(stderr, "unknown hash operation");
|
||||
}
|
||||
} else if (std.mem.eql(u8, kind, "set")) {
|
||||
if (std.mem.eql(u8, op, "add")) {
|
||||
if (args.len < 2) return fail(stderr, "set add needs <name> <member>");
|
||||
const added = try cobalt.Set.add(db, allocator, args[0], args[1]);
|
||||
try stdout.print("{d}\n", .{boolFlag(added)});
|
||||
} else if (std.mem.eql(u8, op, "rem")) {
|
||||
if (args.len < 2) return fail(stderr, "set rem needs <name> <member>");
|
||||
const removed = try cobalt.Set.remove(db, allocator, args[0], args[1]);
|
||||
try stdout.print("{d}\n", .{boolFlag(removed)});
|
||||
} else if (std.mem.eql(u8, op, "ismember")) {
|
||||
if (args.len < 2) return fail(stderr, "set ismember needs <name> <member>");
|
||||
const is = try cobalt.Set.isMember(db, allocator, args[0], args[1]);
|
||||
try stdout.print("{d}\n", .{boolFlag(is)});
|
||||
} else if (std.mem.eql(u8, op, "card")) {
|
||||
if (args.len < 1) return fail(stderr, "set card needs <name>");
|
||||
const n = try cobalt.Set.card(db, allocator, args[0]);
|
||||
try stdout.print("{d}\n", .{n});
|
||||
} else if (std.mem.eql(u8, op, "members")) {
|
||||
if (args.len < 1) return fail(stderr, "set members needs <name>");
|
||||
const ms = try cobalt.Set.members(db, allocator, args[0]);
|
||||
defer {
|
||||
for (ms) |m| allocator.free(m);
|
||||
allocator.free(ms);
|
||||
}
|
||||
for (ms) |m| try stdout.print("{s}\n", .{m});
|
||||
} else if (std.mem.eql(u8, op, "inter")) {
|
||||
if (args.len < 1) return fail(stderr, "set inter needs at least one <name>");
|
||||
// The blanket lazy check above only covered args[0]; args[1..]
|
||||
// are additional set names this multi-set op reads directly.
|
||||
_ = try cobalt.collttl.expireIfDueMany(db, allocator, .set, args[1..], std.time.nanoTimestamp());
|
||||
const ms = try cobalt.Set.intersect(db, allocator, args);
|
||||
defer {
|
||||
for (ms) |m| allocator.free(m);
|
||||
allocator.free(ms);
|
||||
}
|
||||
for (ms) |m| try stdout.print("{s}\n", .{m});
|
||||
} else if (std.mem.eql(u8, op, "union")) {
|
||||
if (args.len < 1) return fail(stderr, "set union needs at least one <name>");
|
||||
_ = try cobalt.collttl.expireIfDueMany(db, allocator, .set, args[1..], std.time.nanoTimestamp());
|
||||
const ms = try cobalt.Set.unionOf(db, allocator, args);
|
||||
defer {
|
||||
for (ms) |m| allocator.free(m);
|
||||
allocator.free(ms);
|
||||
}
|
||||
for (ms) |m| try stdout.print("{s}\n", .{m});
|
||||
} else if (std.mem.eql(u8, op, "diff")) {
|
||||
if (args.len < 2) return fail(stderr, "set diff needs <name> <other...>");
|
||||
_ = try cobalt.collttl.expireIfDueMany(db, allocator, .set, args[1..], std.time.nanoTimestamp());
|
||||
const ms = try cobalt.Set.difference(db, allocator, args[0], args[1..]);
|
||||
defer {
|
||||
for (ms) |m| allocator.free(m);
|
||||
allocator.free(ms);
|
||||
}
|
||||
for (ms) |m| try stdout.print("{s}\n", .{m});
|
||||
} else {
|
||||
return fail(stderr, "unknown set operation");
|
||||
}
|
||||
} else if (std.mem.eql(u8, kind, "list")) {
|
||||
if (std.mem.eql(u8, op, "lpush")) {
|
||||
if (args.len < 2) return fail(stderr, "list lpush needs <name> <value>");
|
||||
try cobalt.List.pushLeft(db, allocator, args[0], args[1]);
|
||||
try stdout.writeAll("OK\n");
|
||||
} else if (std.mem.eql(u8, op, "rpush")) {
|
||||
if (args.len < 2) return fail(stderr, "list rpush needs <name> <value>");
|
||||
try cobalt.List.pushRight(db, allocator, args[0], args[1]);
|
||||
try stdout.writeAll("OK\n");
|
||||
} else if (std.mem.eql(u8, op, "lpop")) {
|
||||
if (args.len < 1) return fail(stderr, "list lpop needs <name>");
|
||||
if (try cobalt.List.popLeft(db, allocator, args[0])) |v| {
|
||||
defer allocator.free(v);
|
||||
try stdout.print("{s}\n", .{v});
|
||||
} else {
|
||||
try stdout.writeAll("(nil)\n");
|
||||
}
|
||||
} else if (std.mem.eql(u8, op, "rpop")) {
|
||||
if (args.len < 1) return fail(stderr, "list rpop needs <name>");
|
||||
if (try cobalt.List.popRight(db, allocator, args[0])) |v| {
|
||||
defer allocator.free(v);
|
||||
try stdout.print("{s}\n", .{v});
|
||||
} else {
|
||||
try stdout.writeAll("(nil)\n");
|
||||
}
|
||||
} else if (std.mem.eql(u8, op, "len")) {
|
||||
if (args.len < 1) return fail(stderr, "list len needs <name>");
|
||||
const n = try cobalt.List.len(db, allocator, args[0]);
|
||||
try stdout.print("{d}\n", .{n});
|
||||
}
|
||||
|
||||
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
||||
std.fs.cwd().deleteTree(dir) catch {};
|
||||
|
||||
|
||||
Reference in new issue
Block a user