diff --git a/.gitignore b/.gitignore index 7196971..5f65b96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ zig-* .zigmod deps.zig +*.tkh diff --git a/build.zig b/build.zig index 271f5fc..a505d7c 100644 --- a/build.zig +++ b/build.zig @@ -11,13 +11,13 @@ pub fn build(b: *std.build.Builder) void { // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); - // Import autogenerated zigmod dependencies - const deps = @import("deps.zig"); - const exe = b.addExecutable("mgtzm", "src/main.zig"); exe.use_stage1 = true; // for sqlite - deps.addAllTo(exe); // zigmod dependencies + + // Add dependencies exe.linkSystemLibrary("json-c"); + exe.linkSystemLibrary("tkrzw"); // TODO: Build statically? + exe.setTarget(target); exe.setBuildMode(mode); exe.install(); diff --git a/src/Db.zig b/src/Db.zig index 8b3a6e3..c3e6175 100644 --- a/src/Db.zig +++ b/src/Db.zig @@ -1,78 +1,80 @@ +const c = @cImport({ + @cInclude("tkrzw_langc.h"); +}); + +const Self = @This(); + const std = @import("std"); -const sqlite = @import("sqlite"); -// TODO: Make DB an object so we can just do Self. -// TODO: Wrap into transactions with a .flush() method +pub const free = c.free; -const ObjectError = error{ - IncompatibleObjectType, // No ?u32 "id" field +dbm: *c.TkrzwDBM, + +const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const encode_len = 4; + +pub fn numEncode(num: usize) ![encode_len]u8 { + return uintEncode(num, 4, chars); +} + +pub fn numDecode(str: []const u8) usize { + return uintDecode(str, chars); +} + +const EncodeError = error{ + NumberTooBig, }; -pub fn getLastId(db: *sqlite.Db) !i64 { - const row = try db.one( - struct { - rowid: i64, - }, - "SELECT LAST_INSERT_ROWID();", - .{}, - .{}, - ); +fn uintEncode(num: usize, comptime bufflen: usize, comptime charset: []const u8) ![bufflen]u8 { + // Check if the number is bigger than the maximum possible number + var pow = comptime std.math.pow(usize, charset.len, bufflen); + if (num >= pow) return EncodeError.NumberTooBig; - if (row) |row_i| { - return row_i.rowid; + var buff = [_]u8{charset[0]} ** bufflen; + + var num_i: usize = num; + for (buff) |*pos| { + pow /= charset.len; + pos.* = charset[@divTrunc(num_i, pow)]; + + num_i %= pow; } - return 0; + return buff; } -pub fn init() !sqlite.Db { - var db = try sqlite.Db.init(.{ - .mode = .{ .File = "/tmp/data.db" }, - .open_flags = .{ - .write = true, - .create = true, - }, - .threading_mode = .MultiThread, - }); +fn uintDecode(str: []const u8, comptime charset: []const u8) usize { + var ret: usize = 0; - try migrate(&db); + for (str) |needle, i| { + for (charset) |blade, j| { + if (needle == blade) { + ret += std.math.pow(usize, charset.len, str.len - i - 1) * j; + break; + } + } + } - return db; + return ret; } -// TODO: Make a proper migration system. -fn migrate(db: *sqlite.Db) !void { - try db.exec( - \\CREATE TABLE IF NOT EXISTS item ( - \\ id INTEGER PRIMARY KEY - \\); - , - .{}, - .{}, - ); - - try db.exec( - \\CREATE TABLE IF NOT EXISTS tag ( - \\ name TEXT PRIMARY KEY - \\); - , - .{}, - .{}, - ); - - try db.exec( - \\CREATE TABLE IF NOT EXISTS item_tag ( - \\ item INTEGER, - \\ tag TEXT, - \\ value TEXT, - \\ - \\ PRIMARY KEY (item, tag), - \\ - \\ FOREIGN KEY (item) REFERENCES item(id) ON DELETE CASCADE, - \\ FOREIGN KEY (tag) REFERENCES tag(name) ON DELETE CASCADE ON UPDATE CASCADE - \\); - , - .{}, - .{}, - ); +pub fn open(path: [:0]const u8, writable: bool, params: [:0]const u8) Self { + return Self{ + .dbm = c.tkrzw_dbm_open(path, writable, params), + }; +} + +pub fn set(self: *Self, key: []const u8, value: []const u8) void { + const key_len = @intCast(i32, key.len); + const value_len = @intCast(i32, value.len); + + _ = c.tkrzw_dbm_set(self.dbm, key.ptr, key_len, value.ptr, value_len, true); +} + +pub fn get(self: *Self, key: []const u8) []u8 { + const key_len = @intCast(i32, key.len); + + var len: i32 = 0; + var ret = c.tkrzw_dbm_get(self.dbm, key.ptr, key_len, &len); + return ret[0..@intCast(usize, len)]; } diff --git a/src/main.zig b/src/main.zig index f235783..9ad5a25 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,43 +1,68 @@ const std = @import("std"); -const sqlite = @import("sqlite"); - const Db = @import("Db.zig"); + const Item = @import("Item.zig"); const Tag = @import("Tag.zig"); const json = @import("json.zig"); const request = @import("request.zig"); pub fn main() !void { - var db = try Db.init(); - const jsonText: [:0]const u8 = - \\{ "add" : [ - \\ { - \\ "fur": "dusk", - \\ "made_with": "krita", - \\ "date": "2022-10-01", - \\ "alunya": null - \\ },{ - \\ "fur": "lara", - \\ "made_with": "krita", - \\ "date": "2022-04-08", - \\ "cell-shading": null - \\ },{ - \\ "made_with": "ballpoint_pen", - \\ "date": "2022-11-04", - \\ "practice": "", - \\ "pose_practice": null - \\ },{ - \\ "fur": "lidiarock1", - \\ "made_with": "krita", - \\ "date": "2022-02-15", - \\ "niko_(oneshot)": null - \\ } - \\] } - ; + var db = Db.open("test.tkh", true, ""); + db.set("test", "lol"); + db.set("kaka", "jeje"); + db.set("kaka", "poop"); - var jobj = json.Obj.newFromString(jsonText); - defer jobj.deinit(); + var str = db.get("test"); + defer Db.free(str.ptr); + std.debug.print("{s}\n", .{str}); - try request.process(&jobj, &db); + const num = 69420; + const numch = try Db.numEncode(num); + std.debug.print("{}\n", .{Db.numDecode(numch[0..])}); + std.debug.print("{}\n", .{num}); + + //var db = try Db.init(); +// + //const jsonText: [:0]const u8 = + //\\{ "add" : [ + //\\ { + //\\ "fur": "dusk", + //\\ "made_with": "krita", + //\\ "date": "2022-10-01", + //\\ "alunya": null + //\\ },{ + //\\ "fur": "lara", + //\\ "made_with": "krita", + //\\ "date": "2022-04-08", + //\\ "cell-shading": null + //\\ },{ + //\\ "made_with": "ballpoint_pen", + //\\ "date": "2022-11-04", + //\\ "practice": "", + //\\ "pose_practice": null + //\\ },{ + //\\ "fur": "lidiarock1", + //\\ "made_with": "krita", + //\\ "date": "2022-02-15", + //\\ "niko_(oneshot)": null + //\\ } + //\\] } + //; +// + //var jobj = json.Obj.newFromString(jsonText); + //defer jobj.deinit(); +// + //try request.process(&jobj, &db); +// + //std.debug.print("\n\n", .{}); +// + //const jsonQuery: [:0]const u8 = + //\\{ "query" : "niko_(oneshot) made_with:krita -fur:dusk" } + //; +// + //var jquery = json.Obj.newFromString(jsonQuery); + //defer jquery.deinit(); +// + //try request.process(&jquery, &db); } diff --git a/src/request.zig b/src/request.zig index 8765298..08137d3 100644 --- a/src/request.zig +++ b/src/request.zig @@ -15,11 +15,16 @@ pub fn process(jobj: *json.Obj, db: *sqlite.Db) !void { defer jret.deinit(); // Test the action to carry and pass the object - if (jobj.objectGet("add") catch null) |*jadd| { - var ret = try add(jadd, db, allocator); + if (jobj.objectGet("add") catch null) |*jaction| { + var ret = try add(jaction, db, allocator); jret.objectAdd("added", &ret); } + if (jobj.objectGet("query") catch null) |*jaction| { + var ret = try query(jaction, db, allocator); + jret.objectAdd("queried", &ret); + } + std.debug.print("{s}", .{ jret.toString() }); } @@ -46,3 +51,44 @@ pub fn add(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !json. return jret; } + +pub fn query(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !json.Obj { + _ = db; + var jret = json.Obj.newArray(); + + const query_string = jobj.getString(); + + var keywords = std.ArrayList([]const u8).init(allocator); + var sql = std.ArrayList(u8).init(allocator); + try sql.appendSlice("SELECT * FROM item_tag WHERE"); + + var iter = std.mem.split(u8, query_string, " "); + + var i: usize = 0; + while(iter.next()) |tag| { + defer i += 1; + + try keywords.append(tag); + + // Just put AND between keywords + if (i > 0) { + try sql.appendSlice(" AND"); + } + // Exclude tags that start with ! + + try sql.appendSlice(" tag "); + + if (tag[0] == '-') { + try sql.append('!'); + } + try sql.appendSlice("= ?"); + + } + + std.debug.print("{s}\n", .{sql.items}); + + //var stmt = try db.prepareDynamic(); + //defer stmt.deinit(); +// + return jret; +} diff --git a/zigmod.lock b/zigmod.lock deleted file mode 100644 index 237665e..0000000 --- a/zigmod.lock +++ /dev/null @@ -1,2 +0,0 @@ -2 -git https://github.com/vrischmann/zig-sqlite commit-624bbf60e79c2b6c838e952aa0c8c4cc35128044 diff --git a/zigmod.yml b/zigmod.yml deleted file mode 100644 index e4bceff..0000000 --- a/zigmod.yml +++ /dev/null @@ -1,6 +0,0 @@ -id: xstmgop7lwvcrkd18q3u95noqfk3oromr3vixj1ud67rhbwl -name: uos -license: AGPLv3 -description: Tagging system with a JSON API -root_dependencies: - - src: git https://github.com/vrischmann/zig-sqlite branch-master