wip ErrCtx

This commit is contained in:
Motiejus Jakštys 2022-06-15 12:56:19 +03:00
parent 72f6c9c6f4
commit eb6003c957
5 changed files with 53 additions and 28 deletions

View File

@ -1,5 +1,5 @@
// ErrContext is a way to pass friendly error messages to the user. // ErrCtx is a way to pass friendly error messages to the user.
// Preallocates memory. // Preallocates memory. Discards messages that do not fit.
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
@ -7,15 +7,15 @@ const math = std.math;
const BoundedArray = std.BoundedArray; const BoundedArray = std.BoundedArray;
const capacity = 1 << 16; // 64K const capacity = 1 << 16; // 64K
const ErrContext = @This(); const ErrCtx = @This();
buf: BoundedArray(u8, capacity) = buf: BoundedArray(u8, capacity) =
BoundedArray(u8, capacity).init(0) catch unreachable, BoundedArray(u8, capacity).init(0) catch unreachable,
overflow: bool = false, overflow: bool = false,
pub fn write(self: *ErrContext, bytes: []const u8) error{Overflow}!usize { pub fn write(self: *ErrCtx, bytes: []const u8) error{}!usize {
const can_add = capacity - self.buf.len; const can_add = capacity - self.buf.len;
if (can_add == 0) return error.Overflow; if (can_add == 0) return bytes.len;
self.overflow = bytes.len > can_add; self.overflow = bytes.len > can_add;
self.buf.appendSliceAssumeCapacity(bytes[0..math.min(bytes.len, can_add)]); self.buf.appendSliceAssumeCapacity(bytes[0..math.min(bytes.len, can_add)]);
@ -23,19 +23,16 @@ pub fn write(self: *ErrContext, bytes: []const u8) error{Overflow}!usize {
// to be ignored in the iterator anyway. // to be ignored in the iterator anyway.
_ = self.buf.append(0) catch null; _ = self.buf.append(0) catch null;
if (self.overflow)
return error.Overflow;
return bytes.len; return bytes.len;
} }
const Writer = std.io.Writer(*ErrContext, error{Overflow}, write); const Writer = std.io.Writer(*ErrCtx, error{}, write);
pub fn writer(self: *ErrContext) Writer { pub fn writer(self: *ErrCtx) Writer {
return Writer{ .context = self }; return Writer{ .context = self };
} }
pub fn iterator(self: *const ErrContext) mem.SplitIterator(u8) { pub fn iterator(self: *const ErrCtx) mem.SplitIterator(u8) {
const slice = self.buf.constSlice(); const slice = self.buf.constSlice();
const last_byte = if (slice[slice.len - 1] == '0') slice.len - 1 else slice.len; const last_byte = if (slice[slice.len - 1] == '0') slice.len - 1 else slice.len;
return mem.split(u8, slice[0..last_byte], "\x00"); return mem.split(u8, slice[0..last_byte], "\x00");
@ -44,13 +41,13 @@ pub fn iterator(self: *const ErrContext) mem.SplitIterator(u8) {
const testing = std.testing; const testing = std.testing;
test "basics" { test "basics" {
var ctx = ErrContext{}; var ctx = ErrCtx{};
var wr = ctx.writer(); var wr = ctx.writer();
try wr.writeAll("0" ** 10); try wr.writeAll("0" ** 10);
try wr.writeAll("1" ** 10); try wr.writeAll("1" ** 10);
try testing.expectError(error.Overflow, wr.writeAll("3" ** capacity)); try wr.writeAll("3" ** capacity);
try testing.expectError(error.Overflow, wr.writeAll("foo")); try wr.writeAll("foo");
var it = ctx.iterator(); var it = ctx.iterator();
try testing.expectEqualSlices(u8, it.next().?, "0" ** 10); try testing.expectEqualSlices(u8, it.next().?, "0" ** 10);
@ -63,11 +60,11 @@ test "basics" {
} }
test "almost overflow" { test "almost overflow" {
var ctx = ErrContext{}; var ctx = ErrCtx{};
var wr = ctx.writer(); var wr = ctx.writer();
try wr.writeAll("0" ** (capacity - 2)); try wr.writeAll("0" ** (capacity - 2));
try testing.expectError(error.Overflow, wr.writeAll("11")); try wr.writeAll("11");
var it = ctx.iterator(); var it = ctx.iterator();
try testing.expectEqualSlices(u8, it.next().?, "0" ** (capacity - 2)); try testing.expectEqualSlices(u8, it.next().?, "0" ** (capacity - 2));

View File

@ -62,6 +62,11 @@ pub const FromReaderError = error{ InvalidRecord, OutOfMemory } || os.ReadError;
pub fn fromReader(allocator: Allocator, reader: anytype) FromReaderError![]Group { pub fn fromReader(allocator: Allocator, reader: anytype) FromReaderError![]Group {
var groups = ArrayList(Group).init(allocator); var groups = ArrayList(Group).init(allocator);
errdefer {
for (groups.items) |*group| group.deinit(allocator);
groups.deinit();
}
var member_ptrs = ArrayList([]const u8).init(allocator); var member_ptrs = ArrayList([]const u8).init(allocator);
defer member_ptrs.deinit(); defer member_ptrs.deinit();
var line = ArrayList(u8).init(allocator); var line = ArrayList(u8).init(allocator);

View File

@ -9,6 +9,7 @@ const BoundedArray = std.BoundedArray;
const pw_passwd = "x\x00"; const pw_passwd = "x\x00";
const User = @This(); const User = @This();
const ErrCtx = @import("ErrCtx.zig");
const PackedUser = @import("PackedUser.zig"); const PackedUser = @import("PackedUser.zig");
const validate = @import("validate.zig"); const validate = @import("validate.zig");
@ -24,19 +25,29 @@ pub fn clone(
self: *const User, self: *const User,
allocator: Allocator, allocator: Allocator,
) error{OutOfMemory}!User { ) error{OutOfMemory}!User {
const name = try allocator.dupe(u8, self.name);
errdefer allocator.free(name);
const gecos = try allocator.dupe(u8, self.gecos);
errdefer allocator.free(gecos);
const home = try allocator.dupe(u8, self.home);
errdefer allocator.free(home);
const shell = try allocator.dupe(u8, self.shell);
errdefer allocator.free(shell);
return User{ return User{
.uid = self.uid, .uid = self.uid,
.gid = self.gid, .gid = self.gid,
.name = try allocator.dupe(u8, self.name), .name = name,
.gecos = try allocator.dupe(u8, self.gecos), .gecos = gecos,
.home = try allocator.dupe(u8, self.home), .home = home,
.shell = try allocator.dupe(u8, self.shell), .shell = shell,
}; };
} }
// fromLine accepts a line of /etc/passwd (with or without the EOL) and makes a // fromLine accepts a line of /etc/passwd (with or without the EOL) and makes a
// User. // User.
fn fromLine(allocator: Allocator, line: []const u8) error{ InvalidRecord, OutOfMemory }!User { fn fromLine(allocator: Allocator, err_ctx: anytype, line: []const u8) error{ InvalidRecord, OutOfMemory }!User {
_ = err_ctx;
var it = mem.split(u8, line, ":"); var it = mem.split(u8, line, ":");
const name = it.next() orelse return error.InvalidRecord; const name = it.next() orelse return error.InvalidRecord;
_ = it.next() orelse return error.InvalidRecord; // password _ = it.next() orelse return error.InvalidRecord; // password
@ -113,12 +124,16 @@ pub fn deinit(self: *User, allocator: Allocator) void {
self.* = undefined; self.* = undefined;
} }
pub fn fromReader(allocator: Allocator, reader: anytype) ![]User { pub fn fromReader(allocator: Allocator, err_ctx: anytype, reader: anytype) ![]User {
var users = ArrayList(User).init(allocator); var users = ArrayList(User).init(allocator);
errdefer {
for (users.items) |*user| user.deinit(allocator);
users.deinit();
}
var buf: [max_line_len + 1]u8 = undefined; var buf: [max_line_len + 1]u8 = undefined;
// TODO: catch and interpret error
while (try reader.readUntilDelimiterOrEof(buf[0..], '\n')) |line| { while (try reader.readUntilDelimiterOrEof(buf[0..], '\n')) |line| {
const user = try fromLine(allocator, line); var user = try fromLine(allocator, err_ctx, line);
try users.append(user); try users.append(user);
} }
return users.toOwnedSlice(); return users.toOwnedSlice();

View File

@ -4,7 +4,7 @@ test "turbonss test suite" {
_ = @import("compress.zig"); _ = @import("compress.zig");
_ = @import("Corpus.zig"); _ = @import("Corpus.zig");
_ = @import("DB.zig"); _ = @import("DB.zig");
_ = @import("ErrContext.zig"); _ = @import("ErrCtx.zig");
_ = @import("Group.zig"); _ = @import("Group.zig");
_ = @import("header.zig"); _ = @import("header.zig");
_ = @import("libnss.zig"); _ = @import("libnss.zig");

View File

@ -12,6 +12,7 @@ const User = @import("../User.zig");
const Group = @import("../Group.zig"); const Group = @import("../Group.zig");
const Corpus = @import("../Corpus.zig"); const Corpus = @import("../Corpus.zig");
const DB = @import("../DB.zig"); const DB = @import("../DB.zig");
const ErrCtx = @import("../ErrCtx.zig");
const usage = const usage =
\\usage: turbonss-unix2db [options] \\usage: turbonss-unix2db [options]
@ -65,15 +66,22 @@ fn execute(
const groupFname = result.argFlag("--group") orelse "./group"; const groupFname = result.argFlag("--group") orelse "./group";
const outFile = result.argFlag("--output") orelse "./db.turbo"; const outFile = result.argFlag("--output") orelse "./db.turbo";
std.debug.print("passwd file name: {s}\n", .{passwdFname}); //std.debug.print("passwd file name: {s}\n", .{passwdFname});
var passwdFile = try fs.cwd().openFile(passwdFname, .{ .mode = .read_only }); var passwdFile = try fs.cwd().openFile(passwdFname, .{ .mode = .read_only });
defer passwdFile.close(); defer passwdFile.close();
var groupFile = try fs.cwd().openFile(groupFname, .{ .mode = .read_only }); var groupFile = try fs.cwd().openFile(groupFname, .{ .mode = .read_only });
defer groupFile.close(); defer groupFile.close();
var users = try User.fromReader(allocator, passwdFile.reader()); var err_ctx = ErrCtx{};
var users = try User.fromReader(
allocator,
err_ctx.writer(),
passwdFile.reader(),
);
defer for (users) |*user| user.deinit(allocator); defer for (users) |*user| user.deinit(allocator);
defer allocator.free(users); defer allocator.free(users);
var groups = try Group.fromReader(allocator, groupFile.reader()); var groups = try Group.fromReader(allocator, groupFile.reader());
defer for (groups) |*group| group.deinit(allocator); defer for (groups) |*group| group.deinit(allocator);
defer allocator.free(groups); defer allocator.free(groups);
@ -116,7 +124,7 @@ test "invalid argument" {
} }
test "smoke test" { test "smoke test" {
if (true) return error.SkipZigTest; //if (true) return error.SkipZigTest;
const allocator = testing.allocator; const allocator = testing.allocator;
var stderr = ArrayList(u8).init(allocator); var stderr = ArrayList(u8).init(allocator);