Initial comit / Pre db simplification

This commit is contained in:
Dendy 2022-10-20 13:23:15 +02:00
commit 54d504c3de
9 changed files with 219 additions and 0 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
* text=auto
*.zig text eol=lf
zigmod.* text eol=lf

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
zig-*
.zigmod
deps.zig

39
build.zig Normal file
View File

@ -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);
}

133
src/Db.zig Normal file
View File

@ -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
\\ );
,
.{},
.{},
);
}

7
src/Item.zig Normal file
View File

@ -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,

7
src/Tag.zig Normal file
View File

@ -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,

15
src/main.zig Normal file
View File

@ -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});
}

5
zigmod.lock Normal file
View File

@ -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

7
zigmod.yml Normal file
View File

@ -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