commit 54d504c3de1e6b9871b17cbd55b6e83e4111c280 Author: Dendy Date: Thu Oct 20 13:23:15 2022 +0200 Initial comit / Pre db simplification diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..403c8ef --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto +*.zig text eol=lf +zigmod.* text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7196971 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +zig-* +.zigmod +deps.zig diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..c5adf0d --- /dev/null +++ b/build.zig @@ -0,0 +1,39 @@ +const std = @import("std"); + +pub fn build(b: *std.build.Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. + const mode = b.standardReleaseOptions(); + + // Import autogenerated zigmod dependencies + const deps = @import("deps.zig"); + + const exe = b.addExecutable("uos", "src/main.zig"); + exe.use_stage1 = true; // for sqlite + deps.addAllTo(exe); // zigmod dependencies + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_tests = b.addTest("src/main.zig"); + exe_tests.setTarget(target); + exe_tests.setBuildMode(mode); + + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&exe_tests.step); +} diff --git a/src/Db.zig b/src/Db.zig new file mode 100644 index 0000000..905baa9 --- /dev/null +++ b/src/Db.zig @@ -0,0 +1,133 @@ +const std = @import("std"); +const sqlite = @import("sqlite"); + +const ObjectError = error{ + IncompatibleObjectType, // No ?u32 "id" field +}; + +pub fn persist(db: *sqlite.Db, obj: anytype) !void { + const T = @TypeOf(obj); + + // Check that object is a valid type for the DB system. + // It should have an "id" field and be of type "?u32" + if (!@hasField(T, "id") or @TypeOf(@field(obj, "id")) != ?u32) { + return ObjectError.IncompatibleObjectType; + } + + // Tried to persist something that is already persisted + if (@field(obj, "id") != null) { + std.debug.print("Can't persist an object with an id.\n", .{}); + return; + } + + const fields = std.meta.fields(T); + + const query_init = "INSTERT INTO " ++ @typeName(T) ++ " ("; + + const sep = ", "; + var buf: [1024]u8 = .{0} ** 1024; + var offset: usize = 0; + + std.mem.copy(u8, buf[offset..], query_init); + offset += query_init.len; + + // Keep track of how many fields to instert + comptime var count: u8 = 0; + inline for (fields) |field_i| { + // Do not input "id" field. + comptime if (std.mem.eql(u8, field_i.name, "id")) { + continue; + }; + + // Do not input array fields (they got their own table) + comptime switch (@typeInfo(field_i.field_type)) { + .Array => continue, + .Pointer => |info| switch (info.size) { + .One => {}, + .Many, .C, .Slice => continue, + }, + else => {}, + }; + + count += 1; + + std.mem.copy(u8, buf[offset..], field_i.name); + offset += field_i.name.len; + std.mem.copy(u8, buf[offset..], sep); + offset += sep.len; + } + offset -= sep.len; + + const query_end = ") VALUES (" ++ ("?, " ** (count - 1)) ++ "?);"; + + std.mem.copy(u8, buf[offset..], query_end); + offset += query_end.len; + + std.debug.print("{s}\n", .{buf[0..offset]}); + + _ = db; + + //const query = "INSERT INTO item (name) VALUES (?);"; + + //try db.exec(query, .{}, .{ .name = name }); +} + +pub fn queryTest(db: *sqlite.Db) ![]const u8 { + const row = try db.one( + struct { + version: [128:0]u8, + }, + "SELECT sqlite_version();", + .{}, + .{}, + ); + + if (row) |row_i| { + return std.mem.sliceTo(&row_i.version, 0); + } + + return "N/A"; +} + +pub fn init() !sqlite.Db { + var db = try sqlite.Db.init(.{ + .mode = .{ .File = "/tmp/data.db" }, + .open_flags = .{ + .write = true, + .create = true, + }, + .threading_mode = .MultiThread, + }); + + try migrate(&db); + + return db; +} + +// TODO: Make a proper migration system. +fn migrate(db: *sqlite.Db) !void { + try db.exec( + \\ CREATE TABLE IF NOT EXISTS item ( + \\ id INTEGER PRIMARY KEY, + \\ name TEXT NOT NULL + \\ ); + \\ + \\ CREATE TABLE IF NOT EXISTS tag ( + \\ id INTEGER PRIMARY KEY, + \\ name TEXT NOT NULL + \\ ); + \\ + \\ CREATE TABLE IF NOT EXISTS item_tag ( + \\ item INTEGER, + \\ tag INTEGER, + \\ + \\ PRIMARY KEY (item, tag), + \\ + \\ FOREIGN KEY (item) REFERENCES item(id) ON DELETE CASCADE, + \\ FOREIGN KEY (tag) REFERENCES tag(id) ON DELETE CASCADE + \\ ); + , + .{}, + .{}, + ); +} diff --git a/src/Item.zig b/src/Item.zig new file mode 100644 index 0000000..6c652d8 --- /dev/null +++ b/src/Item.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const Db = @import("Db.zig"); +const Tag = @import("Tag.zig"); + +id: ?u32, // If null, the object hasn't been persisted +name: []const u8, +tags: ?[]Tag, diff --git a/src/Tag.zig b/src/Tag.zig new file mode 100644 index 0000000..68ebfbd --- /dev/null +++ b/src/Tag.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const Db = @import("Db.zig"); +const Item = @import("Item.zig"); + +id: ?u32, +name: []const u8, +items: ?[]Item, diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..46db93c --- /dev/null +++ b/src/main.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const sqlite = @import("sqlite"); + +const Db = @import("Db.zig"); +const Item = @import("Item.zig"); + +pub fn main() !void { + var db = try Db.init(); + + const item = Item{ .name = "hola", .id = null, .tags = null }; + try Db.persist(&db, item); + + const version = try Db.queryTest(&db); + std.debug.print("Version: {s}\n", .{version}); +} diff --git a/zigmod.lock b/zigmod.lock new file mode 100644 index 0000000..561628a --- /dev/null +++ b/zigmod.lock @@ -0,0 +1,5 @@ +2 +git https://github.com/getty-zig/json commit-0c2e645fc1304ab57f9819445b91ea064da0b27b +git https://github.com/getty-zig/getty commit-102f17a +git https://github.com/ibokuri/concepts commit-05c7368 +git https://github.com/vrischmann/zig-sqlite commit-5b8b472276829f75dced4dc405b66b7b986498b3 diff --git a/zigmod.yml b/zigmod.yml new file mode 100644 index 0000000..1642b96 --- /dev/null +++ b/zigmod.yml @@ -0,0 +1,7 @@ +id: xstmgop7lwvcrkd18q3u95noqfk3oromr3vixj1ud67rhbwl +name: uos +license: AGPLv3 +description: Tagging system with a JSON API +root_dependencies: + - src: git https://github.com/vrischmann/zig-sqlite branch-master + - src: git https://github.com/getty-zig/json