Item creation & insertion from JSON

This commit is contained in:
Dendy 2022-10-22 05:15:36 +02:00
parent c5a124b100
commit b8908c7b22
3 changed files with 68 additions and 49 deletions

View File

@ -6,7 +6,7 @@ const sqlite = @import("sqlite");
const Self = @This(); const Self = @This();
pub const Tag = struct{ pub const Tag = struct {
// :0 for C compatility // :0 for C compatility
name: [:0]const u8, name: [:0]const u8,
value: ?[: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 id: ?i64, // If null, the object hasn't been persisted
tags: ?[]Tag, 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 { pub fn persist(self: *Self, db: *sqlite.Db) !void {
///////////////////////////////////
// ** Insert item **
/////////////////////////////////
// Insert only if there's no ID. // Insert only if there's no ID.
if (self.id == null) { if (self.id == null) {
const query = "INSERT INTO item VALUES(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); self.id = try Db.getLastId(db);
} }
///////////////////////////////////
// ** Insert tags **
/////////////////////////////////
// If the tags haven't been initialized, don't touch anything // If the tags haven't been initialized, don't touch anything
if (self.tags) |tags| { if (self.tags) |tags| {
// TODO: Remove unused tag relations
// If the count drops to zero, delete.
// Create new tags if they don't exist // Create new tags if they don't exist
// TODO: Do the "if they don't exist" part.
for (tags) |*tags_i| { for (tags) |*tags_i| {
try db.exec( const name_query = "INSERT INTO tag (name) VALUES (?)";
"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 (?, ?, ?);";
.{ .name = tags_i.name } try db.exec(rel_query, .{}, .{ .item = self.id, .tag = tags_i.name, .value = tags_i.value });
);
try db.exec(
"INSERT INTO item_tag (item, tag, value) VALUES (?, ?, ?);",
.{},
.{ .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 /// Convert into a JSON object. A call to deinit() is necessary from the caller's side
pub fn toJson(self: Self) json.Obj { pub fn toJson(self: Self) json.Obj {
// Main object
var jobj = json.Obj.newObject(); 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(); var jtags = json.Obj.newObject();
jobj.objectAdd("tags", &jtags); 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| { if (self.tags) |tags| {
for (tags) |tag_i| { for (tags) |tag_i| {
const jtag = if (tag_i.value) |value| const jtag = if (tag_i.value) |value|
@ -80,3 +82,27 @@ pub fn toJson(self: Self) json.Obj {
return jobj; 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,
};
}

View File

@ -60,6 +60,10 @@ pub const Obj = struct {
return Obj{ .obj = c.json_object_get(obj).? }; 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 { pub fn objectGetIterator(self: *Obj) ObjectIterator {
// TODO: Check errors // TODO: Check errors
return ObjectIterator{ return ObjectIterator{

View File

@ -9,15 +9,16 @@ const json = @import("json.zig");
pub fn main() !void { pub fn main() !void {
var db = try Db.init(); var db = try Db.init();
//var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
//defer arena.deinit(); defer arena.deinit();
//const alloc = arena.allocator(); const allocator = arena.allocator();
const jsonText: [:0]const u8 = const jsonText: [:0]const u8 =
\\{ \\{
\\ "tags": { \\ "tags": {
\\ "title": "clean room", \\ "title": "Programar sistema de TODO",
\\ "task": null \\ "programming" : "zig",
\\ "due" : "2023-01-01"
\\ } \\ }
\\} \\}
; ;
@ -25,25 +26,13 @@ pub fn main() !void {
var jsonItem = json.Obj.newFromString(jsonText); var jsonItem = json.Obj.newFromString(jsonText);
defer jsonItem.deinit(); 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 = item.toJson();
var jobj = jsonItem.objectGet("tags");
defer jobj.deinit(); defer jobj.deinit();
var iter = jobj.objectGetIterator(); std.debug.print("{s}\n", .{jobj.toString()});
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()});
}
}
} }