From 45747f606938dd968aaa037b4b3664a0049fe037 Mon Sep 17 00:00:00 2001 From: Dendy Date: Thu, 27 Oct 2022 02:54:00 +0200 Subject: [PATCH] Implement tag creation --- src/Item.zig | 40 +++++++++++++--------- src/json.zig | 88 ++++++++++++++++++++++++++++++++----------------- src/main.zig | 61 ++++++++++++++-------------------- src/request.zig | 34 +++++++++++++++++++ 4 files changed, 141 insertions(+), 82 deletions(-) create mode 100644 src/request.zig diff --git a/src/Item.zig b/src/Item.zig index 9b67186..7c62e3e 100644 --- a/src/Item.zig +++ b/src/Item.zig @@ -23,8 +23,10 @@ pub fn deinit(self: *Self) void { // Free each tag for (tags) |*tag| { + // Free name allocator.free(tag.name); + // Free value if (tag.value) |value| { allocator.free(value); } @@ -49,7 +51,8 @@ pub fn persist(self: *Self, db: *sqlite.Db) !void { // Create new tags if they don't exist // TODO: Do the "if they don't exist" part. for (tags) |*tags_i| { - const name_query = "INSERT INTO tag (name) VALUES (?)"; + const name_query = "INSERT OR IGNORE INTO tag (name) VALUES (?)"; + // TODO: This does work but doesn't prevent SQLite3 from crying on stdout 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 }); @@ -110,21 +113,7 @@ pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self { if (jobj.objectGet("tags") catch null) |*jtags| { defer jtags.deinit(); - - // Reserve space for slice of tags - const len = @intCast(usize, jtags.objectLen()); - tags = try allocator.alloc(Tag, len); - - var iter = jtags.objectGetIterator(); - - var i: usize = 0; - while (iter.next()) |*tag| { - // Whe know it's not null at this point - 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; - } + tags = try tagsFromJson(jobj, allocator); } var id: ?i64 = null; @@ -144,3 +133,22 @@ pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self { .allocator = allocator, }; } + +pub fn tagsFromJson(jobj: *json.Obj, allocator: std.mem.Allocator) ![]Tag { + // Reserve space for slice of tags + const len = @intCast(usize, jobj.objectLen()); + var tags = try allocator.alloc(Tag, len); + + var iter = jobj.objectGetIterator(); + + var i: usize = 0; + while (iter.next()) |*tag| { + // Whe know it's not null at this point + 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 tags; +} diff --git a/src/json.zig b/src/json.zig index 72d6dfb..d661e3b 100644 --- a/src/json.zig +++ b/src/json.zig @@ -17,10 +17,8 @@ pub const Obj = struct { // Option because a null value is treated as the C value NULL in json-c obj: ?*c.json_object, - // TODO: Port type attribute to a zig enum and assign it upon creation - /////////////////////////////////// - // ** General functions ** + // ** Object creation ** ///////////////////////////////// pub fn newObject() Obj { @@ -42,6 +40,14 @@ pub const Obj = struct { return Obj{ .obj = c.json_tokener_parse(s).? }; } + pub fn deinit(self: *Obj) void { + _ = c.json_object_put(self.obj); + } + + /////////////////////////////////// + // ** General functions ** + ///////////////////////////////// + const TypeError = error { TypeMismatch, // Tried to use a function on the wrong JSON format }; @@ -51,6 +57,16 @@ pub const Obj = struct { return @intToEnum(Type, c.json_object_get_type(self.obj)); } + // Not *const Obj because the json_object retains ownership of the string + pub fn toString(self: *Obj) []const u8 { + // TODO: Allow passing of flags as an enum like the SDL2 binding + return std.mem.sliceTo(c.json_object_to_json_string(self.obj), 0); + } + + /////////////////////////////////// + // ** Getters ** + ///////////////////////////////// + // Not *const Obj because the json_object retains ownership of the string pub fn getString(self: *Obj) []const u8 { // TODO: Check type to not allow other types @@ -61,16 +77,6 @@ pub const Obj = struct { return c.json_object_get_int64(self.obj); } - pub fn deinit(self: *Obj) void { - _ = c.json_object_put(self.obj); - } - - // Not *const Obj because the json_object retains ownership of the string - pub fn toString(self: *Obj) []const u8 { - // TODO: Allow passing of flags as an enum like the SDL2 binding - return std.mem.sliceTo(c.json_object_to_json_string(self.obj), 0); - } - /////////////////////////////////// // ** Object functions ** ///////////////////////////////// @@ -98,18 +104,6 @@ pub const Obj = struct { } } - 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{ - .it = c.json_object_iter_begin(self.obj), - .end = c.json_object_iter_end(self.obj), - }; - } - pub fn objectAdd(self: *Obj, key: [*c]const u8, value: ?*Obj) void { // TODO: Check type and error return @@ -119,6 +113,18 @@ pub const Obj = struct { _ = c.json_object_object_add(self.obj, key, o); } + 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 and check it is a JSON Object + return ObjectIterator{ + .it = c.json_object_iter_begin(self.obj), + .end = c.json_object_iter_end(self.obj), + }; + } + pub const ObjectIterator = struct { it: c.json_object_iterator, end: c.json_object_iterator, @@ -157,14 +163,36 @@ pub const Obj = struct { pub fn arrayAdd(self: *Obj, value: ?*Obj) void { // TODO: Check type and error return - // TODO: Check if it can accept a null value // We need the json-c object or null, not the zig object const o = if (value) |i| i.obj else null; - _ = c.json_object_array_add(self.obj, o); } -}; -// TODO: Create wrapper types that statically check and know there -// won't be any problems + pub fn arrayLen(self: Obj) usize { + return c.json_object_array_length(self.obj); + } + + pub fn arrayGet(self: *Obj, idx: usize) ?Obj { + // Sould the .? be there? + if (c.json_object_array_get_idx(self.obj, idx)) |obj| { + return Obj{ .obj = c.json_object_get(obj).? }; + } else { + return null; + } + } + + pub fn arrayGetIterator (self: *Obj) ArrayIterator { + return ArrayIterator{ .obj = self, .idx = 0 }; + } + + pub const ArrayIterator = struct { + obj: *Obj, + idx: usize = 0, + + pub fn next(self: *ArrayIterator) ?Obj { + defer self.idx += 1; + return self.obj.arrayGet(self.idx); + } + }; +}; diff --git a/src/main.zig b/src/main.zig index a065774..9ef2114 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,49 +5,38 @@ 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(); - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - const jsonText: [:0]const u8 = - \\{ - \\ "tags": { - \\ "title": "Programar sistema de TODO", - \\ "programming" : "zig", - \\ "due" : "2023-01-01" + \\{ "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 jsonItem = json.Obj.newFromString(jsonText); - defer jsonItem.deinit(); + var jobj = json.Obj.newFromString(jsonText); - var item = try Item.fromJson(jsonItem, allocator); - defer item.deinit(); - - try item.persist(&db); - - // ---------- - - const jsonText2: [:0]const u8 = - \\{ "id" : 1 } - ; - - var jsonItem2 = json.Obj.newFromString(jsonText2); - defer jsonItem2.deinit(); - - var item2 = try Item.fromJson(jsonItem2, allocator); - defer item2.deinit(); - - var jobj = item2.toJson(); - defer jobj.deinit(); - - const tags = try Item.getTagsById(item2.id.?, &db, allocator); - std.debug.print("{any}\n", .{tags}); - - std.debug.print("{s}\n", .{jobj.toString()}); + try request.process(&jobj, &db); } diff --git a/src/request.zig b/src/request.zig new file mode 100644 index 0000000..b2a9a73 --- /dev/null +++ b/src/request.zig @@ -0,0 +1,34 @@ +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"); + +pub fn process(jobj: *json.Obj, db: *sqlite.Db) !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + // Test the action to carry and pass the object + if (jobj.objectGet("add") catch null) |*jadd| { + try add(jadd, db, allocator); + } +} + +pub fn add(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !void { + // TODO: Maybe return error when no items in the array? + + var iter = jobj.arrayGetIterator(); + while(iter.next()) |*jtags| { + var item = Item { + .id = null, + .tags = try Item.tagsFromJson(jtags, allocator), + }; + defer item.deinit(); + + // TODO: Return the things that have been added with ID + try item.persist(db); + } +}