diff --git a/src/Db.zig b/src/Db.zig index 7b1b77b..80a5c6e 100644 --- a/src/Db.zig +++ b/src/Db.zig @@ -64,7 +64,7 @@ pub fn open(path: [:0]const u8, writable: bool, params: [:0]const u8) Self { }; } -// TODO: Returrn error +// TODO: Return error 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); @@ -72,7 +72,12 @@ pub fn set(self: *Self, key: []const u8, value: []const u8) void { _ = c.tkrzw_dbm_set(self.dbm, key.ptr, key_len, value.ptr, value_len, true); } -// TODO: Returrn error +// TODO: Return error +pub fn remove(self: *Self, key: []const u8) bool { + return c.tkrzw_dbm_remove(self.dbm, key.ptr, @intCast(i32, key.len)); +} + +// TODO: Return error pub fn append(self: *Self, key: []const u8, value: []const u8, delim: []const u8) void { const key_len = @intCast(i32, key.len); const value_len = @intCast(i32, value.len); @@ -126,6 +131,27 @@ pub fn isInList(self: *Self, key: []const u8, needle: []const u8) bool { return false; } +// TODO: Return wether or not the list is void after this +pub fn removeFromList(self: *Self, key: []const u8, needle: []const u8) void { + // TODO: Better error handling, do not fail silently + const haystack = self.get(key) orelse return; + + // If there's no start, somesthing's wrong + const start = std.mem.indexOf(u8, haystack, needle) orelse return; + const end = start + needle.len; + + // Re-set the rest of the IDs + self.set(key, haystack[0..start]); + + // We were deleting the last in the list, no need to append anything + if (end >= haystack.len) return; + + // +1 to remove the space + self.append(key, haystack[(end + 1)..], " "); + + return; +} + /// Generic function to find occurences of a string inside another one with delimiters fn isInStringDelim(haystack: []const u8, needle: []const u8, delim: u8) bool { var spliter = std.mem.split(u8, haystack, &[_]u8{delim}); diff --git a/src/Item.zig b/src/Item.zig index 043c9af..e8d5e4f 100644 --- a/src/Item.zig +++ b/src/Item.zig @@ -110,7 +110,39 @@ pub fn persist(self: *Self, db: *Db, allocator: std.mem.Allocator) !void { } } +// TODO: Return error or something, check for it +pub fn delete(self: Self, db: *Db) !void { + // TODO: Throw proper error + const id = self.id orelse return; + + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + // Delete the id inside the tags + if (self.tags) |tags| { + for (tags) |tag| { + // Get the tag selector: "tag:" + // TODO: Put this inside a function so is is comfier + const tag_sel = try std.mem.concat(allocator, u8, &[_][]const u8{ "tag:", tag }); + defer allocator.free(tag_sel); + + // TODO: See what to do when the tag doesn't exist + // TODO: Grab when it's the last of that tag + db.removeFromList(tag_sel, tag); + } + } + + var item_sel = "item:----".*; + std.mem.copy(u8, item_sel[5..], id[0..]); + + // Finally delete the item per se + // TODO: Return error on error + _ = db.remove(&item_sel); +} + pub fn getById(id: []const u8, db: *Db, allocator: std.mem.Allocator) !?Self { + // TODO: Create a function that returns the selector var item_sel = "item:----".*; std.mem.copy(u8, item_sel[5..], id[0..]); @@ -122,6 +154,7 @@ pub fn getById(id: []const u8, db: *Db, allocator: std.mem.Allocator) !?Self { return Self{ .id = aid, .tags = tags, + .allocator = allocator, }; } diff --git a/src/main.zig b/src/main.zig index 5ec3086..f19a2ad 100644 --- a/src/main.zig +++ b/src/main.zig @@ -10,6 +10,8 @@ pub fn main() !void { var db = Db.open("test.tkh", true, ""); + // ---------------------- ADD --------------------------- + const jsonText: [:0]const u8 = \\{ "add" : [ \\ [ @@ -44,6 +46,19 @@ pub fn main() !void { std.debug.print("\n\n", .{}); + // -------------------- DELETE -------------------------- + + const jsonDelete: [:0]const u8 = + \\{ "delete" : [ "0000", "0003" ] } + ; + + var jdelete = json.Obj.newFromString(jsonDelete); + defer jdelete.deinit(); + + try request.process(&jdelete, &db); + + // -------------------- QUERY --------------------------- + const jsonQuery: [:0]const u8 = \\{ "query" : "fur made_with:krita date:2022", "limit" : 20 } ; diff --git a/src/request.zig b/src/request.zig index 4a1ebc9..c954293 100644 --- a/src/request.zig +++ b/src/request.zig @@ -25,7 +25,12 @@ pub fn process(jobj: *json.Obj, db: *Db) !void { jret.objectAdd("queried", &ret); } - std.debug.print("{s}", .{jret.toString()}); + if (jobj.objectGet("delete") catch null) |*jaction| { + var ret = try delete(jaction, db, allocator); + jret.objectAdd("deleted", &ret); + } + + std.debug.print("{s}\n", .{jret.toString()}); } pub fn add(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { @@ -53,6 +58,8 @@ pub fn add(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { } pub fn query(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { + // TODO: Have into account limits so it is scalable + // TODO: Do not fetch EVERY id at once, iterate where possible const query_str = jobj.getString(); var jret = json.Obj.newArray(); @@ -81,10 +88,31 @@ pub fn query(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { var id_iter = util.intersection(ids.items); while (id_iter.next()) |item_id| { - const item = (try Item.getById(item_id, db, allocator)) orelse continue; + var item = (try Item.getById(item_id, db, allocator)) orelse continue; + defer item.deinit(); jret.arrayAdd(&item.toJson()); } return jret; } + +pub fn delete(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { + var jret = json.Obj.newArray(); + + // Go over each tag + var id_iter = jobj.arrayGetIterator(); + while (id_iter.next()) |*jid| { + const id = jid.getString(); + + // TODO: Return some kind of error or somethign telling that some were not found + var item = (try Item.getById(id, db, allocator)) orelse continue; + defer item.deinit(); + + jret.arrayAdd(&item.toJson()); + + try item.delete(db); + } + + return jret; +}