1const std = @import("std");
2
3pub const DriverType = enum {
4 sqlite3,
5 postgresql,
6};
7
8pub fn Driver(comptime etype: DriverType) type {
9 return switch (etype) {
10 .sqlite3 => @import("./sqlite3.zig"),
11 .postgresql => @import("./postgresql.zig"),
12 };
13}
14
15pub const Error = blk: {
16 var Err: type = error{};
17 for (@typeInfo(DriverType).@"enum".fields) |f| {
18 Err = Err || Driver(@field(DriverType, f.name)).Error;
19 }
20 break :blk Err;
21};
22
23pub fn connect(_type: DriverType, allocator: std.mem.Allocator, connection: [:0]const u8) !Engine {
24 return switch (_type) {
25 inline else => |t| @unionInit(Engine, @tagName(t), try .connect(allocator, connection)),
26 };
27}
28
29pub const Engine = union(DriverType) {
30 sqlite3: Driver(.sqlite3),
31 postgresql: Driver(.postgresql),
32
33 pub fn close(engine: *Engine) void {
34 return switch (engine.*) {
35 inline else => |*e| e.close(),
36 };
37 }
38
39 pub fn exec(engine: *Engine, alloc: std.mem.Allocator, comptime query: []const u8, args: anytype) !void {
40 return switch (engine.*) {
41 inline else => |*e| e.exec(alloc, query, args),
42 };
43 }
44
45 pub fn first(engine: *Engine, alloc: std.mem.Allocator, comptime T: type, comptime query: []const u8, args: anytype) !?T {
46 return switch (engine.*) {
47 inline else => |*e| e.first(alloc, T, query, args),
48 };
49 }
50
51 pub fn collect(engine: *Engine, alloc: std.mem.Allocator, comptime T: type, comptime query: []const u8, args: anytype) ![]T {
52 return switch (engine.*) {
53 inline else => |*e| e.collect(alloc, T, query, args),
54 };
55 }
56
57 pub fn collectDyn(engine: *Engine, alloc: std.mem.Allocator, comptime T: type, query: []const u8, args: anytype) ![]T {
58 return switch (engine.*) {
59 .sqlite3 => |e| {
60 var list = std.array_list.Managed(T).init(alloc);
61 errdefer list.deinit();
62 var stmt: Driver(.sqlite3).Statement = try .prepare(e, query);
63 defer stmt.finalize();
64 try stmt.bindArgs(alloc, args);
65 const iter = stmt.iterate();
66 errdefer iter.reset();
67 while (try iter.step(alloc, T)) |row| try list.append(row);
68 return list.toOwnedSlice();
69 },
70 .postgresql => {
71 @panic("TODO");
72 },
73 };
74 }
75
76 //
77
78 pub fn doesTableExist(engine: *Engine, alloc: std.mem.Allocator, name: []const u8) !bool {
79 return switch (engine.*) {
80 inline else => |*e| e.doesTableExist(alloc, name),
81 };
82 }
83
84 pub fn hasColumnWithName(engine: *Engine, alloc: std.mem.Allocator, comptime table: []const u8, comptime column: []const u8) !bool {
85 return switch (engine.*) {
86 inline else => |*e| e.hasColumnWithName(alloc, table, column),
87 };
88 }
89
90 pub fn createTable(engine: *Engine, alloc: std.mem.Allocator, comptime name: []const u8, comptime pk_name: []const u8, pk_type: type) !void {
91 return switch (engine.*) {
92 inline else => |*e| e.createTable(alloc, name, pk_name, pk_type),
93 };
94 }
95
96 pub fn addColumn(engine: *Engine, alloc: std.mem.Allocator, comptime table_name: []const u8, comptime col_name: []const u8, T: type) !void {
97 if (switch (@typeInfo(T)) {
98 .@"struct", .@"enum", .@"union" => @hasDecl(T, "foreign_table"),
99 else => false,
100 }) {
101 return addColumnForeign(engine, alloc, table_name, col_name, T, T.foreign_table, T.foreign_column);
102 }
103 return switch (engine.*) {
104 inline else => |*e| e.addColumn(alloc, table_name, col_name, T),
105 };
106 }
107
108 pub fn addColumnForeign(engine: *Engine, alloc: std.mem.Allocator, comptime table_name: []const u8, comptime col_name: []const u8, T: type, comptime table_name2: []const u8, comptime col_name2: []const u8) !void {
109 return switch (engine.*) {
110 inline else => |*e| e.addColumnForeign(alloc, table_name, col_name, T, table_name2, col_name2),
111 };
112 }
113};