From 3e85bcde6c8524c7bc54ce26bed228ed4a1b78a8 Mon Sep 17 00:00:00 2001 From: Dendy Faist Date: Fri, 24 Feb 2023 09:38:14 +0100 Subject: [PATCH] Lots of things, idk o_0 --- build.zig | 1 - src/Item.zig | 6 ++-- src/json.zig | 36 +++++++++++++------- src/main.zig | 90 ++++++++++++++++++++++++------------------------- src/request.zig | 40 ++++++++++++---------- src/util.zig | 17 ++++++++-- 6 files changed, 107 insertions(+), 83 deletions(-) diff --git a/build.zig b/build.zig index a505d7c..815fc9f 100644 --- a/build.zig +++ b/build.zig @@ -12,7 +12,6 @@ pub fn build(b: *std.build.Builder) void { const mode = b.standardReleaseOptions(); const exe = b.addExecutable("mgtzm", "src/main.zig"); - exe.use_stage1 = true; // for sqlite // Add dependencies exe.linkSystemLibrary("json-c"); diff --git a/src/Item.zig b/src/Item.zig index 54999a8..af9febc 100644 --- a/src/Item.zig +++ b/src/Item.zig @@ -41,7 +41,7 @@ const SubtagIterator = struct { const opt_i = std.mem.indexOfScalarPos(u8, self.orig_tag, index, ':'); if (opt_i) |i| { - defer self.index = i+1; + defer self.index = i + 1; return self.orig_tag[0..i]; } @@ -87,7 +87,7 @@ pub fn persist(self: *Self, db: *Db, allocator: std.mem.Allocator) !void { var iter = SubtagIterator.init(tag); while (iter.next()) |subtag| { // allocate "tag:" - var tagkey = try std.mem.concat(allocator, u8, &[_][]const u8{"tag:", subtag}); + var tagkey = try std.mem.concat(allocator, u8, &[_][]const u8{ "tag:", subtag }); defer allocator.free(tagkey); // Append the tag to the total of tags if it doesn't exist @@ -215,7 +215,7 @@ pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self { }; } -pub fn tagsFromJson(jobj: *json.Obj, allocator: std.mem.Allocator) ![]Tag { +pub fn tagsFromJson(jobj: *const json.Obj, allocator: std.mem.Allocator) ![]Tag { // Reserve space for slice of tags const len = @intCast(usize, jobj.arrayLen()); var tags = try allocator.alloc(Tag, len); diff --git a/src/json.zig b/src/json.zig index d661e3b..243e2de 100644 --- a/src/json.zig +++ b/src/json.zig @@ -4,7 +4,7 @@ pub const c = @cImport({ const std = @import("std"); const Type = enum(c.json_type) { - @"null" = c.json_type_null, + null = c.json_type_null, boolean = c.json_type_boolean, double = c.json_type_double, int = c.json_type_int, @@ -36,8 +36,14 @@ pub const Obj = struct { pub fn newInt64(i: i64) Obj { return Obj{ .obj = c.json_object_new_int64(i).? }; } - pub fn newFromString(s: [*c]const u8) Obj { - return Obj{ .obj = c.json_tokener_parse(s).? }; + pub fn newFromString(s: [*c]const u8) !Obj { + const FromStringError = error{ + MalformedString, + }; + + return Obj{ + .obj = c.json_tokener_parse(s) orelse return FromStringError.MalformedString, + }; } pub fn deinit(self: *Obj) void { @@ -48,7 +54,7 @@ pub const Obj = struct { // ** General functions ** ///////////////////////////////// - const TypeError = error { + const TypeError = error{ TypeMismatch, // Tried to use a function on the wrong JSON format }; @@ -58,7 +64,7 @@ pub const Obj = struct { } // Not *const Obj because the json_object retains ownership of the string - pub fn toString(self: *Obj) []const u8 { + pub fn toString(self: *const 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); } @@ -68,7 +74,7 @@ pub const Obj = struct { ///////////////////////////////// // Not *const Obj because the json_object retains ownership of the string - pub fn getString(self: *Obj) []const u8 { + pub fn getString(self: *const Obj) []const u8 { // TODO: Check type to not allow other types return std.mem.sliceTo(c.json_object_get_string(self.obj), 0); } @@ -94,17 +100,17 @@ pub const Obj = struct { // Everything went alright if (obj) |ret| { return Obj{ .obj = c.json_object_get(ret).? }; - } else if (obj == null and succ != 0){ + } else if (obj == null and succ != 0) { // The actual value is just null return Obj{ .obj = null }; - } else if (self.getType() != Type.object){ + } else if (self.getType() != Type.object) { return GetError.TypeMismatch; } else { return GetError.KeyNotFound; } } - pub fn objectAdd(self: *Obj, key: [*c]const u8, value: ?*Obj) void { + pub fn objectAdd(self: *Obj, key: [*c]const u8, value: ?*const Obj) void { // TODO: Check type and error return // We need the json-c object or null, not the zig object @@ -113,6 +119,10 @@ pub const Obj = struct { _ = c.json_object_object_add(self.obj, key, o); } + pub fn objectAddString(self: *Obj, key: [*c]const u8, value: [*c]const u8) void { + _ = c.json_object_object_add(self.obj, key, newString(value).obj); + } + pub fn objectLen(self: *const Obj) c_int { return c.json_object_object_length(self.obj); } @@ -161,7 +171,7 @@ pub const Obj = struct { // ** Array functions ** ///////////////////////////////// - pub fn arrayAdd(self: *Obj, value: ?*Obj) void { + pub fn arrayAdd(self: *Obj, value: ?*const Obj) void { // TODO: Check type and error return // We need the json-c object or null, not the zig object @@ -173,7 +183,7 @@ pub const Obj = struct { return c.json_object_array_length(self.obj); } - pub fn arrayGet(self: *Obj, idx: usize) ?Obj { + pub fn arrayGet(self: *const 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).? }; @@ -182,12 +192,12 @@ pub const Obj = struct { } } - pub fn arrayGetIterator (self: *Obj) ArrayIterator { + pub fn arrayGetIterator(self: *const Obj) ArrayIterator { return ArrayIterator{ .obj = self, .idx = 0 }; } pub const ArrayIterator = struct { - obj: *Obj, + obj: *const Obj, idx: usize = 0, pub fn next(self: *ArrayIterator) ?Obj { diff --git a/src/main.zig b/src/main.zig index 90d8d00..a543a15 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,66 +5,64 @@ const Item = @import("Item.zig"); const Tag = @import("Tag.zig"); const json = @import("json.zig"); const request = @import("request.zig"); +const util = @import("util.zig"); pub fn main() !void { - var db = Db.open("test.tkh", true, ""); + const sock_path = "/tmp/mgtzm.sock"; - // ---------------------- ADD --------------------------- + // Remove the socket file if it exists + std.fs.deleteFileAbsolute(sock_path) catch |err| { + switch (err) { + // Do not error out if the file doesn't exist + error.FileNotFound => {}, + else => unreachable, + } + }; - const jsonText: [:0]const u8 = - \\{ "add" : [ - \\ [ - \\ "task:uned:led", - \\ "fur:dusk", - \\ "made_with:krita", - \\ "date:2022:10:01", - \\ "alunya" - \\ ],[ - \\ "fur:lara", - \\ "made_with:krita", - \\ "date:2022:04:08", - \\ "cell-shading" - \\ ],[ - \\ "made_with:ballpoint_pen", - \\ "date:2022:11:04", - \\ "practice", - \\ "pose_practice" - \\ ],[ - \\ "fur:lidiarock1", - \\ "made_with:krita", - \\ "date:2022:10:15", - \\ "niko_(oneshot)" - \\ ] - \\] } - ; + var server = std.net.StreamServer.init(.{}); + defer server.deinit(); + const addr = try std.net.Address.initUnix(sock_path); - var jobj = json.Obj.newFromString(jsonText); - defer jobj.deinit(); + try server.listen(addr); - try request.process(&jobj, &db); + while (true) { - std.debug.print("\n\n", .{}); + // Initialization + // ---------------- - // -------------------- DELETE -------------------------- + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); - const jsonDelete: [:0]const u8 = - \\{ "delete" : [ "0000", "0003" ] } - ; + const connection = try server.accept(); + defer connection.stream.close(); - var jdelete = json.Obj.newFromString(jsonDelete); - defer jdelete.deinit(); + // Receive and process message + // ---------------- - try request.process(&jdelete, &db); + const read = connection.stream.reader(); - // -------------------- QUERY --------------------------- + var msg = util.readUntilDelimiterAllocZ(read, allocator, 0, 1024) catch |err| { + std.debug.print("Failed to read from connection: {any}\n", .{err}); + continue; + }; + defer allocator.free(msg); - const jsonQuery: [:0]const u8 = - \\{ "query" : "" } - ; + var jobj = json.Obj.newFromString(msg) catch { + var errorObj = json.Obj.newObject(); + errorObj.objectAddString("error", "Invalid JSON request."); - var jquery = json.Obj.newFromString(jsonQuery); - defer jquery.deinit(); + _ = connection.stream.write(errorObj.toString()) catch {}; + continue; + }; + defer jobj.deinit(); - try request.process(&jquery, &db); + const answer = try request.process(&jobj, &db, allocator); + defer allocator.free(answer); + + // Reply + // ---------------- + _ = try connection.stream.write(answer); + } } diff --git a/src/request.zig b/src/request.zig index 82dfe88..8e76b5b 100644 --- a/src/request.zig +++ b/src/request.zig @@ -6,34 +6,38 @@ const Tag = @import("Tag.zig"); const json = @import("json.zig"); const util = @import("util.zig"); -pub fn process(jobj: *json.Obj, db: *Db) !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - +pub fn process(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) ![]const u8 { var jret = json.Obj.newObject(); defer jret.deinit(); // Test the action to carry and pass the object - if (jobj.objectGet("add") catch null) |*jaction| { + // TODO: Change to { "type" : "", "data" : } + // TODO: Automatize this, give a list of actions and autobuild the checks in comptime + if (jobj.objectGet("add")) |*jaction| { var ret = try add(jaction, db, allocator); - jret.objectAdd("added", &ret); - } + // TODO: Maybe change all of these to "affected"? + jret.objectAddString("type", "added"); + jret.objectAdd("data", &ret); + } else |_| {} - if (jobj.objectGet("query") catch null) |*jaction| { + if (jobj.objectGet("query")) |*jaction| { var ret = try query(jaction, db, allocator); - jret.objectAdd("queried", &ret); - } + jret.objectAddString("type", "queried"); + jret.objectAdd("data", &ret); + } else |_| {} - if (jobj.objectGet("delete") catch null) |*jaction| { + if (jobj.objectGet("delete")) |*jaction| { var ret = try delete(jaction, db, allocator); - jret.objectAdd("deleted", &ret); - } + jret.objectAddString("type", "deleted"); + jret.objectAdd("data", &ret); + } else |_| {} - std.debug.print("{s}\n", .{jret.toString()}); + const ret_str = std.mem.sliceTo(jret.toString(), 0); + + return allocator.dupe(u8, ret_str); } -pub fn add(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { +pub fn add(jobj: *const json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { // TODO: Maybe return error when no items in the array? // Freed by the caller @@ -57,7 +61,7 @@ pub fn add(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { return jret; } -pub fn query(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { +pub fn query(jobj: *const 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(); @@ -100,7 +104,7 @@ pub fn query(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { return jret; } -pub fn delete(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { +pub fn delete(jobj: *const json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj { var jret = json.Obj.newArray(); // Go over each tag diff --git a/src/util.zig b/src/util.zig index d4c7f53..fe26090 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,12 +1,11 @@ const std = @import("std"); - pub const IntersectionIterator = struct { ref: std.mem.TokenIterator(u8), rest: []const []const u8, /// Returns the next occurrence or null. Returned value is owned by originally passed slice - pub fn next(self: *IntersectionIterator) ?[]const u8{ + pub fn next(self: *IntersectionIterator) ?[]const u8 { // If this returns null, there's no more items to check. We're finished. const needle = self.ref.next() orelse return null; @@ -40,3 +39,17 @@ pub fn intersection(slices: []const []const u8) IntersectionIterator { .rest = slices[1..], }; } + +pub fn readUntilDelimiterAllocZ( + reader: anytype, // TODO: Fix this and set the correct type, I got no clue rn + allocator: std.mem.Allocator, + delimiter: u8, + max_size: usize, +) ![:0]u8 { + var array_list = std.ArrayList(u8).init(allocator); + defer array_list.deinit(); + + try reader.readUntilDelimiterArrayList(&array_list, delimiter, max_size); + + return array_list.toOwnedSliceSentinel(0); +}