feat: add fromByte and kind to protocol
This commit is contained in:
1 parent
fc90b6c8a3
commit
eb441341cb
1 file changed
+160
@@ -489,3 +489,163 @@ pub const CollAction = enum(u8) {
|
|||||||
zset_rangebyscore = 28,
|
zset_rangebyscore = 28,
|
||||||
zset_rank = 29,
|
zset_rank = 29,
|
||||||
|
|
||||||
|
pub fn fromByte(b: u8) !CollAction {
|
||||||
|
return std.meta.intToEnum(CollAction, b) catch error.Malformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Which `collections.Kind` namespace this action's lazy TTL check
|
||||||
|
/// (`collttl.expireIfDue`, called on `name` before every dispatch in
|
||||||
|
/// `handleCollOp`) and `requiredRole` gating both need - grouped by the
|
||||||
|
/// action's numeric range rather than a second explicit table, since
|
||||||
|
/// the enum above is itself laid out kind-by-kind.
|
||||||
|
pub fn kind(self: CollAction) collmod.Kind {
|
||||||
|
return switch (self) {
|
||||||
|
.counter_get, .counter_set, .counter_incrby => .counter,
|
||||||
|
.hash_set, .hash_get, .hash_del, .hash_len, .hash_getall => .hash,
|
||||||
|
.set_add, .set_rem, .set_ismember, .set_card, .set_members, .set_inter, .set_union, .set_diff => .set,
|
||||||
|
.list_lpush, .list_rpush, .list_lpop, .list_rpop, .list_len, .list_range => .list,
|
||||||
|
.zset_add, .zset_score, .zset_incrby, .zset_rem, .zset_card, .zset_rangebyrank, .zset_rangebyscore, .zset_rank => .zset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True for actions that only read - see `server.zig`'s `requiredRole`,
|
||||||
|
/// which needs this to decide `readonly` vs `readwrite` from the action
|
||||||
|
/// byte alone, the same action-byte peek `ttl_op`/`coll_ttl_op` already
|
||||||
|
/// use for their own bundled actions.
|
||||||
|
pub fn isReadOnly(self: CollAction) bool {
|
||||||
|
return switch (self) {
|
||||||
|
.counter_get, .hash_get, .hash_len, .hash_getall, .set_ismember, .set_card, .set_members, .set_inter, .set_union, .set_diff, .list_len, .list_range, .zset_score, .zset_card, .zset_rangebyrank, .zset_rangebyscore, .zset_rank => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn encodeCollOp(allocator: Allocator, action: CollAction, name: []const u8, args: []const []const u8) ![]u8 {
|
||||||
|
var buf = std.ArrayList(u8).init(allocator);
|
||||||
|
errdefer buf.deinit();
|
||||||
|
const w = encode.ByteWriter.init(&buf);
|
||||||
|
try w.byte(@intFromEnum(action));
|
||||||
|
try putLenPrefixed(w, name);
|
||||||
|
try w.putU32(@intCast(args.len));
|
||||||
|
for (args) |a| try putLenPrefixed(w, a);
|
||||||
|
return buf.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CollOpArgs = struct {
|
||||||
|
action: CollAction,
|
||||||
|
name: []const u8,
|
||||||
|
args: [][]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `name` and every entry of `args` are borrowed slices into `payload`,
|
||||||
|
/// valid only as long as the frame that carried them is alive - the same
|
||||||
|
/// convention `decodeBatch`/`decodeTableSelect` use. Only the `args`
|
||||||
|
/// pointer array itself is owned by the caller (free with
|
||||||
|
/// `allocator.free`); never its contents.
|
||||||
|
pub fn decodeCollOp(allocator: Allocator, payload: []const u8) !CollOpArgs {
|
||||||
|
var c = Cursor{ .data = payload };
|
||||||
|
const action = try CollAction.fromByte(try c.takeByte());
|
||||||
|
const name = try c.takeLenPrefixed();
|
||||||
|
const count = try c.takeU32();
|
||||||
|
var list = try std.ArrayList([]const u8).initCapacity(allocator, count);
|
||||||
|
errdefer list.deinit();
|
||||||
|
var i: u32 = 0;
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
list.appendAssumeCapacity(try c.takeLenPrefixed());
|
||||||
|
}
|
||||||
|
return .{ .action = action, .name = name, .args = try list.toOwnedSlice() };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One reply shape covers every `coll_op` action, tagged by the first byte -
|
||||||
|
/// the response-side counterpart of the request-side `CollAction`. `.nil`
|
||||||
|
/// is the wire form of the CLI's `(nil)` (a hash field/list pop/zset score
|
||||||
|
/// that does not exist); `.ok` is a bare acknowledgement (counter set,
|
||||||
|
/// hash set/del as a flag would double up with `.int`, so those still use
|
||||||
|
/// `.int` with 0/1 - see `encodeCollReply`).
|
||||||
|
pub const CollReplyTag = enum(u8) {
|
||||||
|
nil = 0,
|
||||||
|
ok = 1,
|
||||||
|
int = 2,
|
||||||
|
real = 3,
|
||||||
|
bytes = 4,
|
||||||
|
stringList = 5,
|
||||||
|
pairList = 6,
|
||||||
|
scorePairList = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CollPair = struct { field: []const u8, value: []const u8 };
|
||||||
|
pub const CollScorePair = struct { member: []const u8, score: f64 };
|
||||||
|
|
||||||
|
pub const CollReply = union(enum) {
|
||||||
|
nil,
|
||||||
|
ok,
|
||||||
|
int: i64,
|
||||||
|
real: f64,
|
||||||
|
bytes: []const u8,
|
||||||
|
stringList: []const []const u8,
|
||||||
|
pairList: []const CollPair,
|
||||||
|
scorePairList: []const CollScorePair,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn encodeCollReply(allocator: Allocator, reply: CollReply) ![]u8 {
|
||||||
|
var buf = std.ArrayList(u8).init(allocator);
|
||||||
|
errdefer buf.deinit();
|
||||||
|
const w = encode.ByteWriter.init(&buf);
|
||||||
|
switch (reply) {
|
||||||
|
.nil => try w.byte(@intFromEnum(CollReplyTag.nil)),
|
||||||
|
.ok => try w.byte(@intFromEnum(CollReplyTag.ok)),
|
||||||
|
.int => |v| {
|
||||||
|
try w.byte(@intFromEnum(CollReplyTag.int));
|
||||||
|
try w.putU64(@bitCast(v));
|
||||||
|
},
|
||||||
|
.real => |v| {
|
||||||
|
try w.byte(@intFromEnum(CollReplyTag.real));
|
||||||
|
try w.putU64(@bitCast(v));
|
||||||
|
},
|
||||||
|
.bytes => |v| {
|
||||||
|
try w.byte(@intFromEnum(CollReplyTag.bytes));
|
||||||
|
try w.slice(v);
|
||||||
|
},
|
||||||
|
.stringList => |items| {
|
||||||
|
try w.byte(@intFromEnum(CollReplyTag.stringList));
|
||||||
|
try w.putU32(@intCast(items.len));
|
||||||
|
for (items) |it| try putLenPrefixed(w, it);
|
||||||
|
},
|
||||||
|
.pairList => |items| {
|
||||||
|
try w.byte(@intFromEnum(CollReplyTag.pairList));
|
||||||
|
try w.putU32(@intCast(items.len));
|
||||||
|
for (items) |p| {
|
||||||
|
try putLenPrefixed(w, p.field);
|
||||||
|
try putLenPrefixed(w, p.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.scorePairList => |items| {
|
||||||
|
try w.byte(@intFromEnum(CollReplyTag.scorePairList));
|
||||||
|
try w.putU32(@intCast(items.len));
|
||||||
|
for (items) |p| {
|
||||||
|
try putLenPrefixed(w, p.member);
|
||||||
|
try w.putU64(@bitCast(p.score));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return buf.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const OwnedCollPair = struct { field: []u8, value: []u8 };
|
||||||
|
pub const OwnedCollScorePair = struct { member: []u8, score: f64 };
|
||||||
|
|
||||||
|
/// Decoded, independently owned counterpart of `CollReply` - every byte
|
||||||
|
/// slice is a fresh allocation the caller must free via `deinit`, unlike
|
||||||
|
/// `CollOpArgs` above which stays borrowed (that side is consumed
|
||||||
|
/// immediately inside `dispatch` before the frame is freed; this side
|
||||||
|
/// crosses back out to a `Client` caller that keeps it around).
|
||||||
|
pub const OwnedCollReply = union(enum) {
|
||||||
|
nil,
|
||||||
|
ok,
|
||||||
|
int: i64,
|
||||||
|
real: f64,
|
||||||
|
bytes: []u8,
|
||||||
|
stringList: [][]u8,
|
||||||
|
pairList: []OwnedCollPair,
|
||||||
|
scorePairList: []OwnedCollScorePair,
|
||||||
|
|
||||||
Reference in new issue
Block a user