use arena allocator, because this is a short lived cli tool

This commit is contained in:
2024-09-09 09:40:27 +02:00
parent ba9e6b06b6
commit 39bcda689d

View File

@@ -16,15 +16,13 @@ const Base32Error = @import("base32.zig").Base32Error;
pub fn main() !void { pub fn main() !void {
mainInternal() catch |err| { mainInternal() catch |err| {
switch (err) { switch (err) {
Base32Error.InvalidCharacter => { Base32Error.InvalidCharacter, Base32Error.InvalidPadding => {
try std.io.getStdErr().writer().print("The secret is invalid.\n", .{});
},
Base32Error.InvalidPadding => {
try std.io.getStdErr().writer().print("The secret is invalid.\n", .{}); try std.io.getStdErr().writer().print("The secret is invalid.\n", .{});
}, },
ArgumentError.UnknownParameter => { ArgumentError.UnknownParameter => {
try std.io.getStdErr().writer().print("Unknown argument\n", .{}); // do nothing, error message is already written (because the message contains the name of the unknown parameter)
}, },
ArgumentError.InvalidOtpAuthUrl => {},
else => |leftover_err| { else => |leftover_err| {
try std.io.getStdErr().writer().print("{?}\n", .{leftover_err}); try std.io.getStdErr().writer().print("{?}\n", .{leftover_err});
}, },
@@ -34,8 +32,9 @@ pub fn main() !void {
} }
fn mainInternal() !void { fn mainInternal() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const allocator = gpa.allocator(); defer arena.deinit();
const allocator = arena.allocator();
const args = try std.process.argsAlloc(allocator); const args = try std.process.argsAlloc(allocator);
@@ -171,15 +170,14 @@ fn parseArgs(allocator: Allocator, args: []const []const u8) !Args {
} }
result.show = args[i]; result.show = args[i];
} else { } else {
std.debug.print("unknown parameter: {s}\n", .{arg}); std.debug.print("unknown argument: {s}\n", .{arg});
return ArgumentError.UnknownParameter; return ArgumentError.UnknownParameter;
} }
} }
return result; return result;
} }
// todo use a real url parser fn parseOtpAuthUrl(allocator: Allocator, url: []const u8) !OtpAuthUrl {
fn parseOtpAuthUrl(url: []const u8) !OtpAuthUrl {
const uri = try std.Uri.parse(url); const uri = try std.Uri.parse(url);
if (!eql(u8, uri.scheme, "otpauth")) { if (!eql(u8, uri.scheme, "otpauth")) {
@@ -190,9 +188,6 @@ fn parseOtpAuthUrl(url: []const u8) !OtpAuthUrl {
return error.InvalidOtpAuthUrl; return error.InvalidOtpAuthUrl;
} }
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var name = try uri.path.toRawMaybeAlloc(allocator); var name = try uri.path.toRawMaybeAlloc(allocator);
name = std.mem.trimLeft(u8, name, "/"); name = std.mem.trimLeft(u8, name, "/");
@@ -259,11 +254,25 @@ fn read_config(allocator: Allocator, config_location: []const u8) !ArrayList(Otp
var authenticators = std.ArrayList(OtpAuthUrl).init(allocator); var authenticators = std.ArrayList(OtpAuthUrl).init(allocator);
var line_no: usize = 1;
while (try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 1024 * 1024)) |line| { while (try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 1024 * 1024)) |line| {
//defer allocator.free(line);
if (line.len > 0 and std.mem.trim(u8, line, " \r").len > 0) { if (line.len > 0 and std.mem.trim(u8, line, " \r").len > 0) {
const authenticator = try parseOtpAuthUrl(line); const authenticatorResult = parseOtpAuthUrl(allocator, line);
try authenticators.append(authenticator); if (authenticatorResult) |authenticator| {
try authenticators.append(authenticator);
} else |err| {
switch (err) {
error.InvalidOtpAuthUrl => {
try std.io.getStdErr().writer().print("invalid otpauth url in line {d}\n", .{line_no});
},
else => {
return err;
},
}
}
} }
line_no += 1;
} }
return authenticators; return authenticators;
} }
@@ -294,16 +303,18 @@ fn getAuthenticator(allocator: Allocator, name: []const u8, config_location: []c
} }
test "parse command line parameter: 'list'" { test "parse command line parameter: 'list'" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
const allocator = gpa.allocator(); defer arena.deinit();
const allocator = arena.allocator();
const arg = try parseArgs(allocator, &[_][]const u8{ "path/of/executable", "list" }); const arg = try parseArgs(allocator, &[_][]const u8{ "path/of/executable", "list" });
try expect(arg.list); try expect(arg.list);
} }
test "read list of entries" { test "read list of entries" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
const allocator = gpa.allocator(); defer arena.deinit();
const allocator = arena.allocator();
std.fs.cwd().deleteTree("test-tmp") catch unreachable; std.fs.cwd().deleteTree("test-tmp") catch unreachable;
try std.fs.cwd().makeDir("test-tmp"); try std.fs.cwd().makeDir("test-tmp");
@@ -313,7 +324,7 @@ test "read list of entries" {
std.fs.cwd().deleteTree("test-tmp") catch unreachable; std.fs.cwd().deleteTree("test-tmp") catch unreachable;
} }
_ = try file.write("otpauth://totp/token1?secret=c2VjcmV0Cg==\notpauth://totp/token2?secret=c2VjcmV0Cg==\n"); _ = try file.write("otpauth://totp/token1?secret=c2VjcmV0Cg==\notpauth://totp/token2?secret=c2VjcmV0Cg==");
const list: std.ArrayList([]const u8) = try executeGetList(allocator, "test-tmp/zig-totp"); const list: std.ArrayList([]const u8) = try executeGetList(allocator, "test-tmp/zig-totp");
try std.testing.expectEqualStrings("token1", list.items[0]); try std.testing.expectEqualStrings("token1", list.items[0]);
@@ -321,8 +332,33 @@ test "read list of entries" {
try std.testing.expectEqual(2, list.items.len); try std.testing.expectEqual(2, list.items.len);
} }
test "read list of entries, ignoring invalid otpauth urls" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const allocator = arena.allocator();
std.fs.cwd().deleteTree("test-tmp") catch unreachable;
try std.fs.cwd().makeDir("test-tmp");
const file = try std.fs.cwd().createFile("test-tmp/zig-totp", .{ .read = true });
defer {
file.close();
std.fs.cwd().deleteTree("test-tmp") catch unreachable;
}
_ = try file.write("otpauth://totp/token1?secret=c2VjcmV0Cg==\notpauth://invalid\n");
const list: std.ArrayList([]const u8) = try executeGetList(allocator, "test-tmp/zig-totp");
try std.testing.expectEqualStrings("token1", list.items[0]);
try std.testing.expectEqual(1, list.items.len);
}
test "parse oth pauth url" { test "parse oth pauth url" {
const actual: OtpAuthUrl = try parseOtpAuthUrl("otpauth://totp/foo?secret=MFQQ&period=31&digits=7"); var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const allocator = arena.allocator();
const actual: OtpAuthUrl = try parseOtpAuthUrl(allocator, "otpauth://totp/foo?secret=MFQQ&period=31&digits=7");
try std.testing.expectEqualStrings("MFQQ", actual.secretEncoded); try std.testing.expectEqualStrings("MFQQ", actual.secretEncoded);
try std.testing.expectEqualStrings("foo", actual.name); try std.testing.expectEqualStrings("foo", actual.name);