feat: add cmdCollZSet and cmdHelp to repl
This commit is contained in:
1 parent
573d2c4860
commit
4b78a16183
1 file changed
+166
+166
@@ -937,3 +937,169 @@ pub const Repl = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn cmdCollZSet(self: *Repl, op: []const u8, args: []const []const u8, writer: anytype) !void {
|
||||
const ZSet = collectionsmod.ZSet;
|
||||
if (std.mem.eql(u8, op, "add")) {
|
||||
if (args.len < 3) return ReplError.WrongArgCount;
|
||||
const scoreVal = std.fmt.parseFloat(f64, args[2]) catch return ReplError.InvalidArgument;
|
||||
try ZSet.add(self.db, self.allocator, args[0], args[1], scoreVal);
|
||||
try writer.writeAll("OK\n");
|
||||
} else if (std.mem.eql(u8, op, "score")) {
|
||||
if (args.len < 2) return ReplError.WrongArgCount;
|
||||
if (try ZSet.score(self.db, self.allocator, args[0], args[1])) |s| {
|
||||
try writer.print("{d}\n", .{s});
|
||||
} else {
|
||||
try writer.writeAll("(nil)\n");
|
||||
}
|
||||
} else if (std.mem.eql(u8, op, "incrby")) {
|
||||
if (args.len < 3) return ReplError.WrongArgCount;
|
||||
const delta = std.fmt.parseFloat(f64, args[2]) catch return ReplError.InvalidArgument;
|
||||
const s = try ZSet.incrBy(self.db, self.allocator, args[0], args[1], delta);
|
||||
try writer.print("{d}\n", .{s});
|
||||
} else if (std.mem.eql(u8, op, "rem")) {
|
||||
if (args.len < 2) return ReplError.WrongArgCount;
|
||||
const removed = try ZSet.remove(self.db, self.allocator, args[0], args[1]);
|
||||
try writer.print("{d}\n", .{@intFromBool(removed)});
|
||||
} else if (std.mem.eql(u8, op, "card")) {
|
||||
if (args.len < 1) return ReplError.WrongArgCount;
|
||||
const n = try ZSet.card(self.db, self.allocator, args[0]);
|
||||
try writer.print("{d}\n", .{n});
|
||||
} else if (std.mem.eql(u8, op, "rangebyrank") or std.mem.eql(u8, op, "rangebyscore")) {
|
||||
if (args.len < 3) return ReplError.WrongArgCount;
|
||||
const zms = if (std.mem.eql(u8, op, "rangebyrank")) blk: {
|
||||
const start = std.fmt.parseInt(i64, args[1], 10) catch return ReplError.InvalidArgument;
|
||||
const stop = std.fmt.parseInt(i64, args[2], 10) catch return ReplError.InvalidArgument;
|
||||
break :blk try ZSet.rangeByRank(self.db, self.allocator, args[0], start, stop);
|
||||
} else blk: {
|
||||
const min = std.fmt.parseFloat(f64, args[1]) catch return ReplError.InvalidArgument;
|
||||
const max = std.fmt.parseFloat(f64, args[2]) catch return ReplError.InvalidArgument;
|
||||
break :blk try ZSet.rangeByScore(self.db, self.allocator, args[0], min, max);
|
||||
};
|
||||
defer {
|
||||
for (zms) |zm| zm.free(self.allocator);
|
||||
self.allocator.free(zms);
|
||||
}
|
||||
for (zms) |zm| try writer.print("{s}\t{d}\n", .{ zm.member, zm.score });
|
||||
} else if (std.mem.eql(u8, op, "rank")) {
|
||||
if (args.len < 2) return ReplError.WrongArgCount;
|
||||
if (try ZSet.rank(self.db, self.allocator, args[0], args[1])) |r| {
|
||||
try writer.print("{d}\n", .{r});
|
||||
} else {
|
||||
try writer.writeAll("(nil)\n");
|
||||
}
|
||||
} else {
|
||||
return ReplError.UnknownCollectionOp;
|
||||
}
|
||||
}
|
||||
|
||||
fn cmdHelp(self: *Repl, writer: anytype) !void {
|
||||
_ = self;
|
||||
try writer.writeAll(
|
||||
\\commands:
|
||||
\\ put <key> <value> table put <table> <pk> [field=value ...]
|
||||
\\ get <key> table get <table> <pk>
|
||||
\\ del <key> table del <table> <pk>
|
||||
\\ scan [prefix] begin / commit / rollback
|
||||
\\ stats / flush / compact exit / quit
|
||||
\\ table select <table> [expr...] [--limit N] [--offset N]
|
||||
\\ [--order-by f[:desc][,f[:desc]...]]
|
||||
\\ table join [--left|--right|--full] <lt> <lf> <rt> <rf> [--limit N] [--offset N]
|
||||
\\ table aggregate <table> <groupField|*> <spec>[,<spec>...] [expr...] [--limit N] [--offset N]
|
||||
\\ table window <table> <kind[:field]>[,<kind[:field]>...] [expr...] [partition f[,f...]]
|
||||
\\ [order f[:desc][,f...]] [--limit N] [--offset N] [--range]
|
||||
\\ coll <kind> <op> [args...] kind: counter/hash/set/list/zset
|
||||
\\ expire/persist/ttl <key> coll <kind> expire/persist/ttl <name>
|
||||
\\ expire-sweep coll expire-sweep
|
||||
\\
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const PageFlags = struct { limit: u32 = 0, offset: u32 = 0 };
|
||||
|
||||
/// Pulls `--limit N` / `--offset N` out of a `table select` argument list
|
||||
/// wherever they appear, returning the remaining tokens (destined to be
|
||||
/// joined into the expr text) alongside the parsed page window. Same shape
|
||||
/// as main.zig's identically-named helper for `table select`/`remote-select`
|
||||
/// on the one-shot CLI - kept as its own small copy here rather than shared,
|
||||
/// since main.zig is the binary entry point and not a module repl.zig can
|
||||
/// import. Caller owns and frees `rest`.
|
||||
fn extractPageFlags(allocator: Allocator, args: []const []const u8) !struct { page: PageFlags, rest: [][]const u8 } {
|
||||
var page = PageFlags{};
|
||||
var rest = std.ArrayList([]const u8).init(allocator);
|
||||
errdefer rest.deinit();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < args.len) : (i += 1) {
|
||||
const isLimit = std.mem.eql(u8, args[i], "--limit");
|
||||
const isOffset = std.mem.eql(u8, args[i], "--offset");
|
||||
if (isLimit or isOffset) {
|
||||
i += 1;
|
||||
if (i >= args.len) return error.InvalidPageFlag;
|
||||
const n = std.fmt.parseInt(u32, args[i], 10) catch return error.InvalidPageFlag;
|
||||
if (isLimit) page.limit = n else page.offset = n;
|
||||
} else {
|
||||
try rest.append(args[i]);
|
||||
}
|
||||
}
|
||||
return .{ .page = page, .rest = try rest.toOwnedSlice() };
|
||||
}
|
||||
|
||||
/// Pulls a lone `--range` flag (no value) out of `table window`'s trailing
|
||||
/// args, requesting `window.FrameMode.range` instead of the default
|
||||
/// `.rows` running frame - see `window.zig`'s `compute` doc comment. Same
|
||||
/// shape as main.zig's identically-named helper, kept as its own small copy
|
||||
/// here for the same reason `extractPageFlags` above is. Caller owns and
|
||||
/// frees `rest`.
|
||||
fn extractRangeFlag(allocator: Allocator, args: []const []const u8) !struct { range: bool, rest: [][]const u8 } {
|
||||
var range = false;
|
||||
var rest = std.ArrayList([]const u8).init(allocator);
|
||||
errdefer rest.deinit();
|
||||
for (args) |a| {
|
||||
if (std.mem.eql(u8, a, "--range")) {
|
||||
range = true;
|
||||
} else {
|
||||
try rest.append(a);
|
||||
}
|
||||
}
|
||||
return .{ .range = range, .rest = try rest.toOwnedSlice() };
|
||||
}
|
||||
|
||||
const SelectFlags = struct { page: PageFlags = .{}, orderBy: []const querymod.OrderBy = &.{} };
|
||||
|
||||
/// `table select`-only counterpart of `extractPageFlags` above, mirroring
|
||||
/// main.zig's identically-shaped `extractSelectFlags`/`SelectFlags`: same
|
||||
/// `--limit`/`--offset` handling plus `--order-by
|
||||
/// field[:desc][,field[:desc]...]` (comma-separated, applied left to
|
||||
/// right), kept SEPARATE from `extractPageFlags` rather than folded into
|
||||
/// it because `extractPageFlags` is also used by `table join`/`table
|
||||
/// aggregate` here, neither of which sorts its result - recognizing
|
||||
/// `--order-by` there too would silently consume the flag without
|
||||
/// honoring it. Caller owns and frees `rest` AND `flags.orderBy`.
|
||||
fn extractSelectFlags(allocator: Allocator, args: []const []const u8) !struct { flags: SelectFlags, rest: [][]const u8 } {
|
||||
var flags = SelectFlags{};
|
||||
var rest = std.ArrayList([]const u8).init(allocator);
|
||||
errdefer rest.deinit();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < args.len) : (i += 1) {
|
||||
const isLimit = std.mem.eql(u8, args[i], "--limit");
|
||||
const isOffset = std.mem.eql(u8, args[i], "--offset");
|
||||
const isOrderBy = std.mem.eql(u8, args[i], "--order-by");
|
||||
if (isLimit or isOffset) {
|
||||
i += 1;
|
||||
if (i >= args.len) return error.InvalidPageFlag;
|
||||
const n = std.fmt.parseInt(u32, args[i], 10) catch return error.InvalidPageFlag;
|
||||
if (isLimit) flags.page.limit = n else flags.page.offset = n;
|
||||
} else if (isOrderBy) {
|
||||
i += 1;
|
||||
if (i >= args.len) return error.InvalidPageFlag;
|
||||
allocator.free(flags.orderBy);
|
||||
flags.orderBy = try parseOrderByList(allocator, args[i]);
|
||||
} else {
|
||||
try rest.append(args[i]);
|
||||
}
|
||||
}
|
||||
return .{ .flags = flags, .rest = try rest.toOwnedSlice() };
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user