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 {
mainInternal() catch |err| {
switch (err) {
Base32Error.InvalidCharacter => {
try std.io.getStdErr().writer().print("The secret is invalid.\n", .{});
},
Base32Error.InvalidPadding => {
Base32Error.InvalidCharacter, Base32Error.InvalidPadding => {
try std.io.getStdErr().writer().print("The secret is invalid.\n", .{});
},
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| {
try std.io.getStdErr().writer().print("{?}\n", .{leftover_err});
},
@@ -34,8 +32,9 @@ pub fn main() !void {
}
fn mainInternal() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.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];
} else {
std.debug.print("unknown parameter: {s}\n", .{arg});
std.debug.print("unknown argument: {s}\n", .{arg});
return ArgumentError.UnknownParameter;
}
}
return result;
}
// todo use a real url parser
fn parseOtpAuthUrl(url: []const u8) !OtpAuthUrl {
fn parseOtpAuthUrl(allocator: Allocator, url: []const u8) !OtpAuthUrl {
const uri = try std.Uri.parse(url);
if (!eql(u8, uri.scheme, "otpauth")) {
@@ -190,9 +188,6 @@ fn parseOtpAuthUrl(url: []const u8) !OtpAuthUrl {
return error.InvalidOtpAuthUrl;
}
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var name = try uri.path.toRawMaybeAlloc(allocator);
name = std.mem.trimLeft(u8, name, "/");
@@ -259,12 +254,26 @@ fn read_config(allocator: Allocator, config_location: []const u8) !ArrayList(Otp
var authenticators = std.ArrayList(OtpAuthUrl).init(allocator);
var line_no: usize = 1;
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) {
const authenticator = try parseOtpAuthUrl(line);
const authenticatorResult = parseOtpAuthUrl(allocator, line);
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;
}
@@ -294,16 +303,18 @@ fn getAuthenticator(allocator: Allocator, name: []const u8, config_location: []c
}
test "parse command line parameter: 'list'" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const allocator = arena.allocator();
const arg = try parseArgs(allocator, &[_][]const u8{ "path/of/executable", "list" });
try expect(arg.list);
}
test "read list of entries" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
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");
@@ -313,7 +324,7 @@ test "read list of entries" {
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");
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);
}
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" {
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("foo", actual.name);