feat: extend main with printCollReply and runRemoteColl
This commit is contained in:
1 parent
ef1d0f4c06
commit
0b6c99591e
1 file changed
+173
+173
@@ -3252,6 +3252,179 @@ fn runRemoteCollTtl(allocator: std.mem.Allocator, stdout: anytype, stderr: anyty
|
|||||||
defer allocator.free(authFlags.rest);
|
defer allocator.free(authFlags.rest);
|
||||||
|
|
||||||
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
|
|
||||||
|
const op = rest[1];
|
||||||
|
const args = authFlags.rest;
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, op, "expire")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "remote-coll-ttl <kind> expire needs <name> <seconds>");
|
||||||
|
const seconds = std.fmt.parseInt(u64, args[1], 10) catch return fail(stderr, "expire needs a whole number of seconds");
|
||||||
|
client.collTtlExpire(kind, args[0], seconds) catch |err| {
|
||||||
|
if (err == error.ServerCollectionNotFound) return fail(stderr, "no such collection");
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
} else if (std.mem.eql(u8, op, "persist")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "remote-coll-ttl <kind> persist needs <name>");
|
||||||
|
client.collTtlPersist(kind, args[0]) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
} else if (std.mem.eql(u8, op, "ttl")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "remote-coll-ttl <kind> ttl needs <name>");
|
||||||
|
const result = client.collTtlQuery(kind, args[0]) catch |err| {
|
||||||
|
try stderr.print("error: {s}\n", .{@errorName(err)});
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
switch (result) {
|
||||||
|
.noKey => try stdout.writeAll("-2\n"),
|
||||||
|
.noExpiry => try stdout.writeAll("-1\n"),
|
||||||
|
.seconds => |s| try stdout.print("{d}\n", .{s}),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fail(stderr, "unknown remote-coll-ttl operation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints an `OwnedCollReply` the same way `runColl`'s local dispatch
|
||||||
|
/// prints each op's own return value - one shape per reply variant, not
|
||||||
|
/// one shape per op, since `client.collOp`'s reply already carries enough
|
||||||
|
/// structure to tell them apart (an int reply from `set_ismember` prints
|
||||||
|
/// exactly like one from `hash_del`, both booleans-as-0/1, matching
|
||||||
|
/// `runColl`'s own `boolFlag` convention).
|
||||||
|
fn printCollReply(stdout: anytype, reply: cobalt.protocol.OwnedCollReply) !void {
|
||||||
|
switch (reply) {
|
||||||
|
.nil => try stdout.writeAll("(nil)\n"),
|
||||||
|
.ok => try stdout.writeAll("OK\n"),
|
||||||
|
.int => |v| try stdout.print("{d}\n", .{v}),
|
||||||
|
.real => |v| try stdout.print("{d}\n", .{v}),
|
||||||
|
.bytes => |v| try stdout.print("{s}\n", .{v}),
|
||||||
|
.stringList => |items| for (items) |it| try stdout.print("{s}\n", .{it}),
|
||||||
|
.pairList => |items| for (items) |p| try stdout.print("{s}\t{s}\n", .{ p.field, p.value }),
|
||||||
|
.scorePairList => |items| for (items) |p| try stdout.print("{s}\t{d}\n", .{ p.member, p.score }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The network counterpart of `runColl`'s local `coll <kind> <op> ...`
|
||||||
|
/// (backed by `collections.zig`): parses the exact same
|
||||||
|
/// `<kind> <op> [args...]` shape `runColl` does, translates it to a
|
||||||
|
/// `protocol.CollAction` plus a name and string-arg list, and sends it
|
||||||
|
/// through `client.collOp` instead of calling `collections.zig` directly -
|
||||||
|
/// the network counterpart `remote-coll-ttl` already is for `coll <kind>
|
||||||
|
/// expire|ttl|persist`. There is no `remote-coll expire-sweep` counterpart:
|
||||||
|
/// unlike TTL sweeping (a background maintenance action against the whole
|
||||||
|
/// store), the read/write collection operations here always target one
|
||||||
|
/// named collection, so there is nothing store-wide to bundle behind this
|
||||||
|
/// opcode the way `coll_ttl_op`'s `sweep` action does.
|
||||||
|
fn runRemoteColl(allocator: std.mem.Allocator, stdout: anytype, stderr: anytype, addressSpec: []const u8, rest: []const []const u8) !void {
|
||||||
|
if (rest.len < 2) return fail(stderr, "remote-coll needs <kind> <op> ...");
|
||||||
|
const address = try parseHostPort(stdout, addressSpec);
|
||||||
|
|
||||||
|
var client = cobalt.Client.connect(allocator, address) catch |err| {
|
||||||
|
try stderr.print("error: could not connect to {s}: {s}\n", .{ addressSpec, @errorName(err) });
|
||||||
|
std.process.exit(1);
|
||||||
|
};
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
const authFlags = extractAuthFlags(allocator, rest[2..]) catch return fail(stderr, "invalid --user/--pass/--token value");
|
||||||
|
defer allocator.free(authFlags.rest);
|
||||||
|
try authenticateIfRequested(&client, stderr, authFlags.auth);
|
||||||
|
|
||||||
|
const kind = rest[0];
|
||||||
|
const op = rest[1];
|
||||||
|
const args = authFlags.rest;
|
||||||
|
|
||||||
|
const Call = struct { action: cobalt.protocol.CollAction, name: []const u8, callArgs: []const []const u8 };
|
||||||
|
|
||||||
|
const call: Call = blk: {
|
||||||
|
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>");
|
||||||
|
break :blk .{ .action = .counter_get, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "set")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "counter set needs <name> <value>");
|
||||||
|
break :blk .{ .action = .counter_set, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "incrby")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "counter incrby needs <name> <delta>");
|
||||||
|
break :blk .{ .action = .counter_incrby, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
}
|
||||||
|
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>");
|
||||||
|
break :blk .{ .action = .hash_set, .name = args[0], .callArgs = args[1..3] };
|
||||||
|
} else if (std.mem.eql(u8, op, "get")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "hash get needs <name> <field>");
|
||||||
|
break :blk .{ .action = .hash_get, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "del")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "hash del needs <name> <field>");
|
||||||
|
break :blk .{ .action = .hash_del, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "len")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "hash len needs <name>");
|
||||||
|
break :blk .{ .action = .hash_len, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "getall")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "hash getall needs <name>");
|
||||||
|
break :blk .{ .action = .hash_getall, .name = args[0], .callArgs = &.{} };
|
||||||
|
}
|
||||||
|
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>");
|
||||||
|
break :blk .{ .action = .set_add, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "rem")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "set rem needs <name> <member>");
|
||||||
|
break :blk .{ .action = .set_rem, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "ismember")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "set ismember needs <name> <member>");
|
||||||
|
break :blk .{ .action = .set_ismember, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "card")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "set card needs <name>");
|
||||||
|
break :blk .{ .action = .set_card, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "members")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "set members needs <name>");
|
||||||
|
break :blk .{ .action = .set_members, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "inter")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "set inter needs at least one <name>");
|
||||||
|
break :blk .{ .action = .set_inter, .name = args[0], .callArgs = args[1..] };
|
||||||
|
} else if (std.mem.eql(u8, op, "union")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "set union needs at least one <name>");
|
||||||
|
break :blk .{ .action = .set_union, .name = args[0], .callArgs = args[1..] };
|
||||||
|
} else if (std.mem.eql(u8, op, "diff")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "set diff needs <name> <other...>");
|
||||||
|
break :blk .{ .action = .set_diff, .name = args[0], .callArgs = args[1..] };
|
||||||
|
}
|
||||||
|
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>");
|
||||||
|
break :blk .{ .action = .list_lpush, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "rpush")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "list rpush needs <name> <value>");
|
||||||
|
break :blk .{ .action = .list_rpush, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "lpop")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "list lpop needs <name>");
|
||||||
|
break :blk .{ .action = .list_lpop, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "rpop")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "list rpop needs <name>");
|
||||||
|
break :blk .{ .action = .list_rpop, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "len")) {
|
||||||
|
if (args.len < 1) return fail(stderr, "list len needs <name>");
|
||||||
|
break :blk .{ .action = .list_len, .name = args[0], .callArgs = &.{} };
|
||||||
|
} else if (std.mem.eql(u8, op, "range")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "list range needs <name> <start> <stop>");
|
||||||
|
break :blk .{ .action = .list_range, .name = args[0], .callArgs = args[1..3] };
|
||||||
|
}
|
||||||
|
return fail(stderr, "unknown list operation");
|
||||||
|
} else if (std.mem.eql(u8, kind, "zset")) {
|
||||||
|
if (std.mem.eql(u8, op, "add")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "zset add needs <name> <member> <score>");
|
||||||
|
break :blk .{ .action = .zset_add, .name = args[0], .callArgs = args[1..3] };
|
||||||
|
} else if (std.mem.eql(u8, op, "score")) {
|
||||||
|
if (args.len < 2) return fail(stderr, "zset score needs <name> <member>");
|
||||||
|
break :blk .{ .action = .zset_score, .name = args[0], .callArgs = args[1..2] };
|
||||||
|
} else if (std.mem.eql(u8, op, "incrby")) {
|
||||||
|
if (args.len < 3) return fail(stderr, "zset incrby needs <name> <member> <delta>");
|
||||||
|
break :blk .{ .action = .zset_incrby, .name = args[0], .callArgs = args[1..3] };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
fn runVerify(allocator: std.mem.Allocator, stdout: anytype, dir: []const u8, count: usize) !void {
|
||||||
|
|||||||
Reference in new issue
Block a user