Something went wrong. Try again.
keystore jetzig.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339const Store = @This();
vtable: *const VTable,
pub const VTable = struct { get: *const fn (*Store, Allocator, []const u8) anyerror!?[]const u8 = unimplementedGet, put: *const fn (*Store, []const u8, []const u8) anyerror!void = unimplementedPut, putExpire: *const fn (*Store, []const u8, []const u8, i32) anyerror!void = unimplementedPutExpire, fetchRemove: *const fn (*Store, Allocator, []const u8) anyerror!?[]const u8 = unimplementedFetchRemove, remove: *const fn (*Store, []const u8) anyerror!void = unimplementedRemove, append: *const fn (*Store, []const u8, []const u8) anyerror!void = unimplementedAppend, prepend: *const fn (*Store, []const u8, []const u8) anyerror!void = unimplementedPrepend, pop: *const fn (*Store, Allocator, []const u8) anyerror!?[]const u8 = unimplementedPop, popFirst: *const fn (*Store, Allocator, []const u8) anyerror!?[]const u8 = unimplementedPopFirst,};
pub fn get(self: *Store, allocator: Allocator, key: []const u8) !?[]const u8 { return self.vtable.get(self, allocator, key);}
test get { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { try store.put("foo", "bar"); const foo = try store.get(t.allocator, "foo") orelse return t.expect(false); defer t.allocator.free(foo); try t.expectEqualStrings("bar", foo); const baz = try store.get(t.allocator, "baz"); try t.expectEqual(baz, null); }}
pub fn put(self: *Store, key: []const u8, value: []const u8) !void { return self.vtable.put(self, key, value);}
test put { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { try store.put("foo", "bar"); const foo = try store.get(t.allocator, "foo") orelse return t.expect(false); defer t.allocator.free(foo); try t.expectEqualStrings("bar", foo); const baz = try store.get(t.allocator, "baz"); try t.expectEqual(baz, null); }}
pub fn putExpire(self: *Store, key: []const u8, value: []const u8, expiration: i32) !void { return self.vtable.putExpire(self, key, value, expiration);}
test putExpire { // File does not implement putExpire var m: Memory = .init(t.io, t.allocator); defer m.deinit();
var f: File = try .init(t.io, .{ .path = "/tmp/jetkv_file.db", }); defer f.deinit();
var s: SQLite = try .init(t.io, t.allocator, .{ .path = "/tmp/jetkv_sqlite.db", }); defer s.deinit();
// File backend does not implement putExpire (no TTL support). for ([_]*Store{ &m.store, &s.store }) |store| { try store.putExpire("foo", "bar", 1); const mem_foo = try store.get(t.allocator, "foo") orelse return t.expect(false); defer t.allocator.free(mem_foo); try t.expectEqualStrings("bar", mem_foo);
const timeout: Timeout = .{ .duration = .{ .raw = .fromNanoseconds(1_100_000_000), .clock = .real, }, }; try timeout.sleep(t.io); try t.expect(try store.get(t.allocator, "foo") == null); }
if (!build_options.@"skip-valkey") { var v: Valkey(.{}) = try .init(t.io, t.allocator); defer v.deinit(); const store = &v.store; try store.putExpire("foo", "bar", 1); const mem_foo = try store.get(t.allocator, "foo") orelse return t.expect(false); defer t.allocator.free(mem_foo); try t.expectEqualStrings("bar", mem_foo);
const timeout: Timeout = .{ .duration = .{ .raw = .fromNanoseconds(1_100_000_000), .clock = .real, }, }; try timeout.sleep(t.io); try t.expect(try store.get(t.allocator, "foo") == null); }}
pub fn fetchRemove(self: *Store, allocator: Allocator, key: []const u8) !?[]const u8 { return self.vtable.fetchRemove(self, allocator, key);}
test fetchRemove { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { try store.put("foo", "bar"); const foo = try store.fetchRemove(t.allocator, "foo") orelse return t.expect(false); defer t.allocator.free(foo); try t.expectEqualStrings("bar", foo); try t.expect(try store.get(t.allocator, "foo") == null); }}
pub fn remove(self: *Store, key: []const u8) !void { return self.vtable.remove(self, key);}
test remove { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { try store.put("foo", "bar"); try store.remove("foo"); try t.expect(try store.get(t.allocator, "foo") == null); }}
pub fn append(self: *Store, key: []const u8, value: []const u8) !void { return self.vtable.append(self, key, value);}
test append { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { const items = &[_][]const u8{ "foo", "bar", "baz" }; for (items) |v| try store.append("list", v);
for (items) |e| { const popped = (try store.popFirst(t.allocator, "list")).?; defer t.allocator.free(popped); try t.expectEqualStrings(e, popped); } try t.expect(try store.popFirst(t.allocator, "list") == null); }}
pub fn prepend(self: *Store, key: []const u8, value: []const u8) !void { return self.vtable.prepend(self, key, value);}
test prepend { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { const items = &[_][]const u8{ "foo", "bar", "baz" }; for (items) |v| try store.prepend("list", v);
const expected = &[_][]const u8{ "baz", "bar", "foo" }; for (expected) |e| { const popped = (try store.popFirst(t.allocator, "list")).?; defer t.allocator.free(popped); try t.expectEqualStrings(e, popped); } }}
pub fn pop(self: *Store, allocator: Allocator, key: []const u8) !?[]const u8 { return self.vtable.pop(self, allocator, key);}
test pop { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { const items = &[_][]const u8{ "foo", "bar", "baz" }; for (items) |v| try store.append("list", v);
const expected = &[_][]const u8{ "baz", "bar", "foo" }; for (expected) |e| { const popped = (try store.pop(t.allocator, "list")).?; defer t.allocator.free(popped); try t.expectEqualStrings(e, popped); } try t.expect(try store.pop(t.allocator, "list") == null); }}
pub fn popFirst(self: *Store, allocator: Allocator, key: []const u8) !?[]const u8 { return self.vtable.popFirst(self, allocator, key);}
test popFirst { var backends: Backends = try .init(t.io, t.allocator); defer backends.deinit();
for (backends.stores()) |store| { const items = &[_][]const u8{ "foo", "bar", "baz" }; for (items) |v| try store.prepend("list", v);
const expected = &[_][]const u8{ "baz", "bar", "foo" }; for (expected) |e| { const popped = (try store.popFirst(t.allocator, "list")).?; defer t.allocator.free(popped); try t.expectEqualStrings(e, popped); } }}
fn unimplementedGet(_: *Store, _: Allocator, _: []const u8) !?[]const u8 { return error.Unimplemented;}
fn unimplementedPut(_: *Store, _: []const u8, _: []const u8) !void { return error.Unimplemented;}
fn unimplementedPutExpire(_: *Store, _: []const u8, _: []const u8, _: i32) !void { return error.Unimplemented;}
fn unimplementedFetchRemove(_: *Store, _: Allocator, _: []const u8) !?[]const u8 { return error.Unimplemented;}
fn unimplementedRemove(_: *Store, _: []const u8) !void { return error.Unimplemented;}
fn unimplementedAppend(_: *Store, _: []const u8, _: []const u8) !void { return error.Unimplemented;}
fn unimplementedPrepend(_: *Store, _: []const u8, _: []const u8) !void { return error.Unimplemented;}
fn unimplementedPop(_: *Store, _: Allocator, _: []const u8) !?[]const u8 { return error.Unimplemented;}
fn unimplementedPopFirst(_: *Store, _: Allocator, _: []const u8) !?[]const u8 { return error.Unimplemented;}
const Backends = blk: { if (build_options.@"skip-valkey") break :blk struct { memory: Memory, file: File, sqlite: SQLite,
pub fn init(io: Io, allocator: Allocator) !Backends { return .{ .memory = .init(io, allocator), .file = try .init(io, .{ .path = "/tmp/jetkv_file.db" }), .sqlite = try .init(io, allocator, .{ .path = "/tmp/jetkv_sqlite.db" }), }; }
pub fn deinit(self: *Backends) void { self.memory.deinit(); self.file.deinit(); self.sqlite.deinit(); }
pub fn stores(self: *Backends) [3]*Store { return [_]*Store{ &self.file.store, &self.memory.store, &self.sqlite.store, }; } }; break :blk struct { valkey: Valkey(.{}), memory: Memory, file: File, sqlite: SQLite,
pub fn init(io: Io, allocator: Allocator) !Backends { return .{ .valkey = try .init(io, allocator), .memory = .init(io, allocator), .file = try .init(io, .{ .path = "/tmp/jetkv_file.db" }), .sqlite = try .init(io, allocator, .{ .path = "/tmp/jetkv_sqlite.db" }), }; }
pub fn deinit(self: *Backends) void { self.valkey.deinit(); self.memory.deinit(); self.file.deinit(); self.sqlite.deinit(); }
pub fn stores(self: *Backends) [4]*Store { return [_]*Store{ &self.file.store, &self.memory.store, &self.valkey.store, &self.sqlite.store, }; } };};
const std = @import("std");const Io = std.Io;const Allocator = std.mem.Allocator;const t = std.testing;const IpAddress = Io.net.IpAddress;const Timeout = Io.Timeout;const Memory = @import("Memory.zig");const File = @import("File.zig");const Valkey = @import("valkey.zig").Valkey;const SQLite = @import("SQLite.zig");const build_options = @import("build_options");