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