wip ErrCtx
This commit is contained in:
parent
72f6c9c6f4
commit
eb6003c957
@ -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));
|
@ -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);
|
||||||
|
31
src/User.zig
31
src/User.zig
@ -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();
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user