milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
This commit is contained in:
@@ -25,35 +25,24 @@ const list_sql =
|
||||
|
||||
/// Every string in the result is a heap copy owned by `gpa`.
|
||||
pub fn listBlocklistSources(database: *db.Db, gpa: Allocator) db.Error!std.ArrayList(model.BlocklistSource) {
|
||||
var stmt = try database.prepare(list_sql);
|
||||
defer stmt.deinit();
|
||||
return crud.listRows(model.BlocklistSource, database, gpa, list_sql, readBlocklistSource);
|
||||
}
|
||||
|
||||
var out: std.ArrayList(model.BlocklistSource) = .empty;
|
||||
// `errdefer`s run in reverse: the free pass is declared last so it runs
|
||||
// before the backing array is released.
|
||||
errdefer out.deinit(gpa);
|
||||
errdefer freeBlocklistSources(gpa, out.items);
|
||||
|
||||
while (try stmt.step()) {
|
||||
const url = try stmt.columnTextAlloc(gpa, 0);
|
||||
errdefer gpa.free(url);
|
||||
const name = try stmt.columnTextAlloc(gpa, 1);
|
||||
errdefer gpa.free(name);
|
||||
try out.append(gpa, .{
|
||||
.url = url,
|
||||
.name = name,
|
||||
.enabled = stmt.columnBool(2),
|
||||
.is_suggested = stmt.columnBool(3),
|
||||
});
|
||||
}
|
||||
return out;
|
||||
fn readBlocklistSource(stmt: *db.Stmt, gpa: Allocator) db.Error!model.BlocklistSource {
|
||||
const url = try stmt.columnTextAlloc(gpa, 0);
|
||||
errdefer gpa.free(url);
|
||||
const name = try stmt.columnTextAlloc(gpa, 1);
|
||||
errdefer gpa.free(name);
|
||||
return .{
|
||||
.url = url,
|
||||
.name = name,
|
||||
.enabled = stmt.columnBool(2),
|
||||
.is_suggested = stmt.columnBool(3),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn freeBlocklistSources(gpa: Allocator, items: []const model.BlocklistSource) void {
|
||||
for (items) |item| {
|
||||
gpa.free(item.url);
|
||||
gpa.free(item.name);
|
||||
}
|
||||
crud.freeRows(model.BlocklistSource, gpa, items);
|
||||
}
|
||||
|
||||
const insert_sql =
|
||||
@@ -125,21 +114,7 @@ const list_rows_sql = row_columns_sql ++ " ORDER BY url";
|
||||
/// order `listBlocklistSources` uses. Every string is a heap copy owned by
|
||||
/// `gpa`; free the whole list with `freeSourceRows` and then `deinit` the list.
|
||||
pub fn listSourceRows(database: *db.Db, gpa: Allocator) db.Error!std.ArrayList(SourceRow) {
|
||||
var stmt = try database.prepare(list_rows_sql);
|
||||
defer stmt.deinit();
|
||||
|
||||
var out: std.ArrayList(SourceRow) = .empty;
|
||||
// `errdefer`s run in reverse: the free pass is declared last so it runs
|
||||
// before the backing array is released.
|
||||
errdefer out.deinit(gpa);
|
||||
errdefer freeSourceRows(gpa, out.items);
|
||||
|
||||
while (try stmt.step()) {
|
||||
const row = try readSourceRow(&stmt, gpa);
|
||||
errdefer freeSourceRow(gpa, row);
|
||||
try out.append(gpa, row);
|
||||
}
|
||||
return out;
|
||||
return crud.listRows(SourceRow, database, gpa, list_rows_sql, readSourceRow);
|
||||
}
|
||||
|
||||
fn readSourceRow(stmt: *db.Stmt, gpa: Allocator) db.Error!SourceRow {
|
||||
@@ -163,14 +138,13 @@ fn readSourceRow(stmt: *db.Stmt, gpa: Allocator) db.Error!SourceRow {
|
||||
};
|
||||
}
|
||||
|
||||
/// Frees `url`, `name` and the `checksum` payload when it is not null.
|
||||
pub fn freeSourceRow(gpa: Allocator, row: SourceRow) void {
|
||||
gpa.free(row.url);
|
||||
gpa.free(row.name);
|
||||
if (row.checksum) |value| gpa.free(value);
|
||||
crud.freeRow(SourceRow, gpa, row);
|
||||
}
|
||||
|
||||
pub fn freeSourceRows(gpa: Allocator, items: []const SourceRow) void {
|
||||
for (items) |item| freeSourceRow(gpa, item);
|
||||
crud.freeRows(SourceRow, gpa, items);
|
||||
}
|
||||
|
||||
const update_stats_sql =
|
||||
@@ -438,6 +412,12 @@ fn listSourceRowsUnderFailure(gpa: Allocator) !void {
|
||||
var rows = try listSourceRows(&database, gpa);
|
||||
defer rows.deinit(gpa);
|
||||
defer freeSourceRows(gpa, rows.items);
|
||||
|
||||
// `checksum` is the one allocated optional in this directory. Without a
|
||||
// non-null one in the result the injection never reaches its allocation and
|
||||
// this test stops covering the shape it exists for. Row id 1 sorts last:
|
||||
// `seedSources` inserts `c.example` first and the list orders by url.
|
||||
try testing.expect(rows.items[2].checksum != null);
|
||||
}
|
||||
|
||||
test "listSourceRows is leak-safe under allocation failure" {
|
||||
|
||||
Reference in New Issue
Block a user