From b8908c7b225a3c65cf2b2e5425332672a84efd26 Mon Sep 17 00:00:00 2001 From: Dendy Date: Sat, 22 Oct 2022 05:15:36 +0200 Subject: [PATCH] Item creation & insertion from JSON --- src/Item.zig | 80 ++++++++++++++++++++++++++++++++++------------------ src/json.zig | 4 +++ src/main.zig | 33 ++++++++-------------- 3 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/Item.zig b/src/Item.zig index 4873e13..9ef8f76 100644 --- a/src/Item.zig +++ b/src/Item.zig @@ -6,7 +6,7 @@ const sqlite = @import("sqlite"); const Self = @This(); -pub const Tag = struct{ +pub const Tag = struct { // :0 for C compatility name: [:0]const u8, value: ?[:0]const u8, @@ -14,12 +14,27 @@ pub const Tag = struct{ id: ?i64, // If null, the object hasn't been persisted tags: ?[]Tag, +allocator: ?std.mem.Allocator = null, + +pub fn deinit(self: *Self) void { + // If there's no allocator there has been no allocations + const allocator = self.allocator orelse return; + const tags = self.tags orelse return; + + // Free each tag + for (tags) |*tag| { + allocator.free(tag.name); + + if (tag.value) |value| { + allocator.free(value); + } + } + + // Free the tags buffer + allocator.free(tags); +} pub fn persist(self: *Self, db: *sqlite.Db) !void { - /////////////////////////////////// - // ** Insert item ** - ///////////////////////////////// - // Insert only if there's no ID. if (self.id == null) { const query = "INSERT INTO item VALUES(NULL);"; @@ -29,44 +44,31 @@ pub fn persist(self: *Self, db: *sqlite.Db) !void { self.id = try Db.getLastId(db); } - /////////////////////////////////// - // ** Insert tags ** - ///////////////////////////////// - // If the tags haven't been initialized, don't touch anything if (self.tags) |tags| { - // TODO: Remove unused tag relations - // If the count drops to zero, delete. - // Create new tags if they don't exist + // TODO: Do the "if they don't exist" part. for (tags) |*tags_i| { - try db.exec( - "INSERT INTO tag (name) VALUES (?);", - .{}, - .{ .name = tags_i.name } - ); - - try db.exec( - "INSERT INTO item_tag (item, tag, value) VALUES (?, ?, ?);", - .{}, - .{ .item = self.id, .tag = tags_i.name, .value = tags_i.value } - ); + const name_query = "INSERT INTO tag (name) VALUES (?)"; + try db.exec(name_query, .{}, .{ .name = tags_i.name }); + const rel_query = "INSERT INTO item_tag (item, tag, value) VALUES (?, ?, ?);"; + try db.exec(rel_query, .{}, .{ .item = self.id, .tag = tags_i.name, .value = tags_i.value }); } } - } /// Convert into a JSON object. A call to deinit() is necessary from the caller's side pub fn toJson(self: Self) json.Obj { + // Main object var jobj = json.Obj.newObject(); - jobj.objectAdd("id", &json.Obj.newInt64(self.id.?)); + // Add id (i64|null) + jobj.objectAdd("id", if (self.id) |id| &json.Obj.newInt64(id) else null); var jtags = json.Obj.newObject(); jobj.objectAdd("tags", &jtags); - // If there are tags there's no point on continuing - + // Add tags only if they're initialized if (self.tags) |tags| { for (tags) |tag_i| { const jtag = if (tag_i.value) |value| @@ -80,3 +82,27 @@ pub fn toJson(self: Self) json.Obj { return jobj; } + +pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self { + var jtags = jobj.objectGet("tags"); + defer jtags.deinit(); + + const len = @intCast(usize, jtags.objectLen()); + var tags = try allocator.alloc(Tag, len); + + var iter = jtags.objectGetIterator(); + + var i: usize = 0; + while (iter.next()) |*tag| { + tags[i].name = try allocator.dupeZ(u8, tag.key); + tags[i].value = if (tag.value) |*value| try allocator.dupeZ(u8, value.getString()) else null; + + i += 1; + } + + return Self{ + .id = null, + .tags = tags, + .allocator = allocator, + }; +} diff --git a/src/json.zig b/src/json.zig index 1dbf70a..29f7a56 100644 --- a/src/json.zig +++ b/src/json.zig @@ -60,6 +60,10 @@ pub const Obj = struct { return Obj{ .obj = c.json_object_get(obj).? }; } + pub fn objectLen(self: *const Obj) c_int { + return c.json_object_object_length(self.obj); + } + pub fn objectGetIterator(self: *Obj) ObjectIterator { // TODO: Check errors return ObjectIterator{ diff --git a/src/main.zig b/src/main.zig index 7ced616..1a9fd7c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,15 +9,16 @@ const json = @import("json.zig"); pub fn main() !void { var db = try Db.init(); - //var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - //defer arena.deinit(); - //const alloc = arena.allocator(); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); const jsonText: [:0]const u8 = \\{ \\ "tags": { - \\ "title": "clean room", - \\ "task": null + \\ "title": "Programar sistema de TODO", + \\ "programming" : "zig", + \\ "due" : "2023-01-01" \\ } \\} ; @@ -25,25 +26,13 @@ pub fn main() !void { var jsonItem = json.Obj.newFromString(jsonText); defer jsonItem.deinit(); - _ = db; + var item = try Item.fromJson(jsonItem, allocator); + defer item.deinit(); - //var item = Item.fromJson(jsonItem); + try item.persist(&db); - //try item.persist(&db); - - //var jobj = item.toJson(); - - var jobj = jsonItem.objectGet("tags"); + var jobj = item.toJson(); defer jobj.deinit(); - var iter = jobj.objectGetIterator(); - while(iter.next()) |*tag| { - std.debug.print("{s}", .{tag.key}); - defer std.debug.print("\n", .{}); - - if (tag.value) |*value| { - std.debug.print(" => {s}", .{value.getString()}); - } - } - + std.debug.print("{s}\n", .{jobj.toString()}); }