diff options
| author | aura <nw@moneybot.cc> | 2026-02-23 03:51:28 +0100 |
|---|---|---|
| committer | aura <nw@moneybot.cc> | 2026-02-23 03:51:28 +0100 |
| commit | f251ddc8f0bba98a51be9b6e59ac4c5d9b24c3d5 (patch) | |
| tree | e799284935a9012db280192375e9107575e2a216 /private/src | |
| parent | bedf43af45d97a10a6f62b4f1bb21cd66fda1d71 (diff) | |
zig project startmaster
Diffstat (limited to 'private/src')
| -rw-r--r-- | private/src/db.zig | 151 | ||||
| -rw-r--r-- | private/src/main.zig | 5 | ||||
| -rw-r--r-- | private/src/util.zig | 4 |
3 files changed, 160 insertions, 0 deletions
diff --git a/private/src/db.zig b/private/src/db.zig new file mode 100644 index 0000000..ee8908b --- /dev/null +++ b/private/src/db.zig @@ -0,0 +1,151 @@ +const z = @import( "std" ); +const sql = @import( "sqlite" ); + +const u = @import( "util.zig" ); + +const alloc = u.alloc; + +const ArenaAlloc = z.heap.ArenaAllocator; +const Mutex = z.Thread.Mutex; +const Db = sql.Db; + +pub threadlocal var dbi: Interface = undefined; +pub threadlocal var dbi_init: bool = false; + +var db_mutex = Mutex{}; + +pub fn create( dbfile: [:0]const u8 ) !void { + dbiCheck() catch try init( dbfile ); + + const create_users_q = + \\CREATE TABLE IF NOT EXISTS users ( + \\ uuid TEXT PRIMARY KEY, + \\ email TEXT, + \\ tokens TEXT, + \\ login_token TEXT, + \\ token_resetdate INTEGER, + \\ api_tokens TEXT, + \\ created_at INTEGER, + \\ subscription_data TEXT + \\) + ; + + try dbi.put( create_users_q, .{} ); + // ensure all fields actually exist in the database + dbi.put( "ALTER TABLE users ADD COLUMN email TEXT", .{} ) catch {}; + dbi.put( "ALTER TABLE users ADD COLUMN tokens TEXT", .{} ) catch {}; + dbi.put( "ALTER TABLE users ADD COLUMN login_token TEXT", .{} ) catch {}; + dbi.put( "ALTER TABLE users ADD COLUMN token_resetdate INTEGER", .{} ) catch {}; + dbi.put( "ALTER TABLE users ADD COLUMN api_tokens TEXT", .{} ) catch {}; + dbi.put( "ALTER TABLE users ADD COLUMN created_at INTEGER", .{} ) catch {}; + dbi.put( "ALTER TABLE users ADD COLUMN subscription_data TEXT", .{} ) catch {}; + + const create_userdata_q = + \\CREATE TABLE IF NOT EXISTS user_data ( + \\ uuid TEXT PRIMARY KEY, + \\ nickname TEXT, + \\ prompt_data TEXT, + \\ site_prefs TEXT, + \\ chat_files TEXT + \\) + ; + + try dbi.put( create_userdata_q, .{} ); + // ensure all fields actually exist in the database + dbi.put( "ALTER TABLE user_data ADD COLUMN nickname INTEGER", .{} ) catch {}; + dbi.put( "ALTER TABLE user_data ADD COLUMN prompt_data INTEGER", .{} ) catch {}; + dbi.put( "ALTER TABLE user_data ADD COLUMN site_prefs INTEGER", .{} ) catch {}; + dbi.put( "ALTER TABLE user_data ADD COLUMN chat_files INTEGER", .{} ) catch {}; +} + +pub fn init( dbfile: [:0]const u8 ) !void { + if( dbi_init ) return error.DbiAlreadyInitialized; + + const db = try sql.Db.init( .{ + .mode = Db.Mode{ .File = dbfile }, + .open_flags = .{ + .write = true, + .create = true + }, + .threading_mode = .MultiThread + } ); + + dbi = .{ .db = db }; + dbi_init = true; +} + +fn dbiCheck() !void { + if( !dbi_init ) return error.DbiNotInitialized; +} + +///methods of t get ignored +pub fn Row( comptime t: type ) type { + return struct { + v: t, + alloc: ArenaAlloc, + pub fn deinit( self: @This() ) void { + self.alloc.deinit(); + } + }; +} + +pub const Interface = struct { + const Self = Interface; + db: Db, + pub fn deinit( self: *Interface ) void { + self.db.deinit(); + } + + pub fn put( self: *Interface, comptime query: []const u8, value: anytype ) !void { + try dbiCheck(); + db_mutex.lock(); + defer db_mutex.unlock(); + + var statement = try self.db.prepare( query ); + defer statement.deinit(); + + try statement.exec( .{}, value ); + } + + ///returns the first row of the query + pub fn one( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) !t { + try dbiCheck(); + db_mutex.lock(); + defer db_mutex.unlock(); + + var statement = try self.db.prepare( query ); + defer statement.deinit(); + + const ret = try statement.one( t, .{}, value ); + return ret.?; + } + + ///returns all rows of the query + ///must be freed by caller + pub fn all( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) ![]t { + try dbiCheck(); + db_mutex.lock(); + defer db_mutex.unlock(); + + var statement = try self.db.prepare( query ); + defer statement.deinit(); + + return try statement.all( t, alloc, .{}, value ); + } + + pub fn oneAlloc( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) !Row(t) { + try dbiCheck(); + db_mutex.lock(); + defer db_mutex.unlock(); + + var arena = ArenaAlloc.init( alloc ); + const a = arena.allocator(); + + var statement = try self.db.prepareDynamic( query ); + defer statement.deinit(); + + const ret = try statement.oneAlloc( t, a, .{}, value ); + if( ret == null ) return error.QueryError; + return .{ .v = ret.?, .alloc = arena }; + } +}; diff --git a/private/src/main.zig b/private/src/main.zig new file mode 100644 index 0000000..5f397b8 --- /dev/null +++ b/private/src/main.zig @@ -0,0 +1,5 @@ +const z = @import( "std" ); + +pub fn main() !void { + z.debug.print( "hello world\n", .{} ); +} diff --git a/private/src/util.zig b/private/src/util.zig new file mode 100644 index 0000000..923c196 --- /dev/null +++ b/private/src/util.zig @@ -0,0 +1,4 @@ +const z = @import( "std" ); + +pub var gpa = z.heap.GeneralPurposeAllocator( .{ .thread_safe = true, .stack_trace_frames = 64 } ){}; +pub const alloc = gpa.allocator(); |
