aports/testing/aercbook/zig-0.10.patch
Coco Liliace a92475203d testing/aercbook: new aport
https://sr.ht/~renerocksai/aercbook/
Minimalistic address book for aerc
2023-03-16 13:07:37 +01:00

588 lines
26 KiB
Diff

From bde89d2af1d042c015c3999933b5101278af83eb Mon Sep 17 00:00:00 2001
From: Rene Schallner <rene@rocks.ai>
Date: Fri, 23 Dec 2022 14:44:34 +0100
Subject: [PATCH 1/3] updated code to work with zig master 0.11
---
build.zig | 2 +-
src/args.zig | 202 ++++++++++++++++++++++++++----------------
src/gitversiontag.zig | 7 +-
src/main.zig | 13 +--
4 files changed, 140 insertions(+), 84 deletions(-)
diff --git a/build.zig b/build.zig
index 92e964e..ab9b67e 100644
--- a/build.zig
+++ b/build.zig
@@ -5,7 +5,7 @@ pub fn build(b: *std.build.Builder) void {
// write src/version.zig
const alloc = std.heap.page_allocator;
const gvs = gitVersionTag(alloc);
- const efmt = "WARNING: could not write src/version.zig:\n {s}\n";
+ const efmt = "WARNING: could not write src/version.zig:\n {!}\n";
if (std.fs.cwd().createFile("src/version.zig", .{})) |file| {
defer file.close();
const zigfmt = "pub const version_string = \"{s}\";";
diff --git a/src/args.zig b/src/args.zig
index a790120..7fa3517 100644
--- a/src/args.zig
+++ b/src/args.zig
@@ -1,7 +1,3 @@
-// From: https://github.com/MasterQ32/zig-args
-// Commit: 72a79c87fdf5aaa98f81796fbf6500b5c06b1ebc
-// (compatible with zig 0.9.1)
-
const std = @import("std");
/// Parses arguments for the given specification and our current process.
@@ -9,9 +5,13 @@ const std = @import("std");
/// - `allocator` is the allocator that is used to allocate all required memory
/// - `error_handling` defines how parser errors will be handled.
pub fn parseForCurrentProcess(comptime Spec: type, allocator: std.mem.Allocator, error_handling: ErrorHandling) !ParseArgsResult(Spec, null) {
- var args = std.process.args();
+ // Use argsWithAllocator for portability.
+ // All data allocated by the ArgIterator is freed at the end of the function.
+ // Data returned to the user is always duplicated using the allocator.
+ var args = try std.process.argsWithAllocator(allocator);
+ defer args.deinit();
- const executable_name = try (args.next(allocator) orelse {
+ const executable_name = args.next() orelse {
try error_handling.process(error.NoExecutableName, Error{
.option = "",
.kind = .missing_executable_name,
@@ -19,12 +19,11 @@ pub fn parseForCurrentProcess(comptime Spec: type, allocator: std.mem.Allocator,
// we do not assume any more arguments appear here anyways...
return error.NoExecutableName;
- });
- errdefer allocator.free(executable_name);
+ };
var result = try parseInternal(Spec, null, &args, allocator, error_handling);
- result.executable_name = executable_name;
+ result.executable_name = try allocator.dupeZ(u8, executable_name);
return result;
}
@@ -34,9 +33,13 @@ pub fn parseForCurrentProcess(comptime Spec: type, allocator: std.mem.Allocator,
/// - `allocator` is the allocator that is used to allocate all required memory
/// - `error_handling` defines how parser errors will be handled.
pub fn parseWithVerbForCurrentProcess(comptime Spec: type, comptime Verb: type, allocator: std.mem.Allocator, error_handling: ErrorHandling) !ParseArgsResult(Spec, Verb) {
- var args = std.process.args();
+ // Use argsWithAllocator for portability.
+ // All data allocated by the ArgIterator is freed at the end of the function.
+ // Data returned to the user is always duplicated using the allocator.
+ var args = try std.process.argsWithAllocator(allocator);
+ defer args.deinit();
- const executable_name = try (args.next(allocator) orelse {
+ const executable_name = args.next() orelse {
try error_handling.process(error.NoExecutableName, Error{
.option = "",
.kind = .missing_executable_name,
@@ -44,12 +47,11 @@ pub fn parseWithVerbForCurrentProcess(comptime Spec: type, comptime Verb: type,
// we do not assume any more arguments appear here anyways...
return error.NoExecutableName;
- });
- errdefer allocator.free(executable_name);
+ };
var result = try parseInternal(Spec, Verb, &args, allocator, error_handling);
- result.executable_name = executable_name;
+ result.executable_name = try allocator.dupeZ(u8, executable_name);
return result;
}
@@ -92,16 +94,15 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
var result_arena_allocator = result.arena.allocator();
var arglist = std.ArrayList([:0]const u8).init(allocator);
- errdefer arglist.deinit();
+ defer arglist.deinit();
var last_error: ?anyerror = null;
- while (args_iterator.next(result_arena_allocator)) |item_or_error| {
- const item = try item_or_error;
-
+ while (args_iterator.next()) |item| {
if (std.mem.startsWith(u8, item, "--")) {
if (std.mem.eql(u8, item, "--")) {
// double hyphen is considered 'everything from here now is positional'
+ result.raw_start_index = arglist.items.len;
break;
}
@@ -135,19 +136,21 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
const Tag = std.meta.Tag(Verb);
inline for (std.meta.fields(Verb)) |verb_info| {
if (verb.* == @field(Tag, verb_info.name)) {
- inline for (std.meta.fields(verb_info.field_type)) |fld| {
- if (std.mem.eql(u8, pair.name, fld.name)) {
- try parseOption(
- verb_info.field_type,
- result_arena_allocator,
- &@field(verb.*, verb_info.name),
- args_iterator,
- error_handling,
- &last_error,
- fld.name,
- pair.value,
- );
- found = true;
+ if (comptime canHaveFieldsAndIsNotZeroSized(verb_info.type)) {
+ inline for (std.meta.fields(verb_info.type)) |fld| {
+ if (std.mem.eql(u8, pair.name, fld.name)) {
+ try parseOption(
+ verb_info.type,
+ result_arena_allocator,
+ &@field(verb.*, verb_info.name),
+ args_iterator,
+ error_handling,
+ &last_error,
+ fld.name,
+ pair.value,
+ );
+ found = true;
+ }
}
}
}
@@ -166,7 +169,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
} else if (std.mem.startsWith(u8, item, "-")) {
if (std.mem.eql(u8, item, "-")) {
// single hyphen is considered a positional argument
- try arglist.append(item);
+ try arglist.append(try result_arena_allocator.dupeZ(u8, item));
} else {
var any_shorthands = false;
for (item[1..]) |char, index| {
@@ -202,30 +205,32 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
if (!found) {
const Tag = std.meta.Tag(Verb);
inline for (std.meta.fields(Verb)) |verb_info| {
- const VerbType = verb_info.field_type;
- if (verb.* == @field(Tag, verb_info.name)) {
- const target_value = &@field(verb.*, verb_info.name);
- if (@hasDecl(VerbType, "shorthands")) {
- any_shorthands = true;
- inline for (std.meta.fields(@TypeOf(VerbType.shorthands))) |fld| {
- if (fld.name.len != 1)
- @compileError("All shorthand fields must be exactly one character long!");
- if (fld.name[0] == char) {
- const real_name = @field(VerbType.shorthands, fld.name);
- const real_fld_type = @TypeOf(@field(target_value.*, real_name));
-
- // -2 because we stripped of the "-" at the beginning
- if (requiresArg(real_fld_type) and index != item.len - 2) {
- last_error = error.EncounteredUnexpectedArgument;
- try error_handling.process(error.EncounteredUnexpectedArgument, Error{
- .option = &option_name,
- .kind = .invalid_placement,
- });
- } else {
- try parseOption(VerbType, result_arena_allocator, target_value, args_iterator, error_handling, &last_error, real_name, null);
+ const VerbType = verb_info.type;
+ if (comptime canHaveFieldsAndIsNotZeroSized(VerbType)) {
+ if (verb.* == @field(Tag, verb_info.name)) {
+ const target_value = &@field(verb.*, verb_info.name);
+ if (@hasDecl(VerbType, "shorthands")) {
+ any_shorthands = true;
+ inline for (std.meta.fields(@TypeOf(VerbType.shorthands))) |fld| {
+ if (fld.name.len != 1)
+ @compileError("All shorthand fields must be exactly one character long!");
+ if (fld.name[0] == char) {
+ const real_name = @field(VerbType.shorthands, fld.name);
+ const real_fld_type = @TypeOf(@field(target_value.*, real_name));
+
+ // -2 because we stripped of the "-" at the beginning
+ if (requiresArg(real_fld_type) and index != item.len - 2) {
+ last_error = error.EncounteredUnexpectedArgument;
+ try error_handling.process(error.EncounteredUnexpectedArgument, Error{
+ .option = &option_name,
+ .kind = .invalid_placement,
+ });
+ } else {
+ try parseOption(VerbType, result_arena_allocator, target_value, args_iterator, error_handling, &last_error, real_name, null);
+ }
+ last_error = null; // we need to reset that error here, as it was set previously
+ found = true;
}
- last_error = null; // we need to reset that error here, as it was set previously
- found = true;
}
}
}
@@ -255,7 +260,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
inline for (std.meta.fields(Verb)) |fld| {
if (std.mem.eql(u8, item, fld.name)) {
// found active verb, default-initialize it
- result.verb = @unionInit(Verb, fld.name, fld.field_type{});
+ result.verb = @unionInit(Verb, fld.name, fld.type{});
}
}
@@ -270,7 +275,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
}
}
- try arglist.append(item);
+ try arglist.append(try result_arena_allocator.dupeZ(u8, item));
}
}
@@ -279,22 +284,28 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
// This will consume the rest of the arguments as positional ones.
// Only executes when the above loop is broken.
- while (args_iterator.next(result_arena_allocator)) |item_or_error| {
- const item = try item_or_error;
- try arglist.append(item);
+ while (args_iterator.next()) |item| {
+ try arglist.append(try result_arena_allocator.dupeZ(u8, item));
}
- result.positionals = arglist.toOwnedSlice();
+ result.positionals = try arglist.toOwnedSlice();
return result;
}
+fn canHaveFieldsAndIsNotZeroSized(comptime T: type) bool {
+ return switch (@typeInfo(T)) {
+ .Struct, .Union, .Enum, .ErrorSet => @sizeOf(T) != 0,
+ else => false,
+ };
+}
+
/// The return type of the argument parser.
pub fn ParseArgsResult(comptime Generic: type, comptime MaybeVerb: ?type) type {
if (@typeInfo(Generic) != .Struct)
@compileError("Generic argument definition must be a struct");
if (MaybeVerb) |Verb| {
- const ti: std.builtin.TypeInfo = @typeInfo(Verb);
+ const ti: std.builtin.Type = @typeInfo(Verb);
if (ti != .Union or ti.Union.tag_type == null)
@compileError("Verb must be a tagged union");
}
@@ -318,6 +329,9 @@ pub fn ParseArgsResult(comptime Generic: type, comptime MaybeVerb: ?type) type {
/// The positional arguments that were passed to the process.
positionals: [][:0]const u8,
+ // The index of the first "raw arg", meaning the first arg after "--"
+ raw_start_index: ?usize = null,
+
/// Name of the executable file (or: zeroth argument)
executable_name: ?[:0]const u8,
@@ -491,21 +505,28 @@ fn parseOption(
) !void {
const field_type = @TypeOf(@field(target_struct, name));
- const final_value = if (value) |val|
- val // use the literal value
- else if (requiresArg(field_type))
+ const final_value = if (value) |val| blk: {
+ // use the literal value
+ const res = try arena.dupeZ(u8, val);
+ break :blk res;
+ } else if (requiresArg(field_type)) blk: {
// fetch from parser
- try (args.next(arena) orelse {
+ const val = args.next();
+ if (val == null or std.mem.eql(u8, val.?, "--")) {
last_error.* = error.MissingArgument;
try error_handling.process(error.MissingArgument, Error{
.option = "--" ++ name,
.kind = .missing_argument,
});
return;
- })
- else
+ }
+
+ const res = try arena.dupeZ(u8, val.?);
+ break :blk res;
+ } else blk: {
// argument is "empty"
- "";
+ break :blk "";
+ };
@field(target_struct, name) = convertArgumentValue(field_type, arena, final_value) catch |err| {
last_error.* = err;
@@ -515,7 +536,7 @@ fn parseOption(
});
// we couldn't parse the value, so we return a undefined value as we have signalled an
// error and won't return this anyways.
- return undefined;
+ return;
};
}
@@ -671,10 +692,10 @@ const TestIterator = struct {
return TestIterator{ .sequence = items };
}
- pub fn next(self: *@This(), allocator: std.mem.Allocator) ?(error{OutOfMemory}![:0]u8) {
+ pub fn next(self: *@This()) ?[:0]const u8 {
if (self.index >= self.sequence.len)
return null;
- const result = try allocator.dupeZ(u8, self.sequence[self.index]);
+ const result = self.sequence[self.index];
self.index += 1;
return result;
}
@@ -791,9 +812,9 @@ test "shorthand parsing (no verbs)" {
test "basic parsing (with verbs)" {
var titerator = TestIterator.init(&[_][:0]const u8{
- "booze", // verb
- "--output",
+ "--output", // non-verb options can come before or after verb
"foobar",
+ "booze", // verb
"--with-offset",
"--numberOfBytes",
"-250",
@@ -901,3 +922,36 @@ test "strings with sentinel" {
try std.testing.expectEqualStrings("foobar", args.options.output.?);
}
+
+test "option argument --" {
+ var titerator = TestIterator.init(&[_][:0]const u8{
+ "--output",
+ "--",
+ });
+
+ try std.testing.expectError(error.MissingArgument, parseInternal(
+ struct {
+ output: ?[:0]const u8 = null,
+ },
+ null,
+ &titerator,
+ std.testing.allocator,
+ .silent,
+ ));
+}
+
+test "index of raw indicator --" {
+ var titerator = TestIterator.init(&[_][:0]const u8{ "stdin", "-", "--", "not-stdin", "-", "--" });
+
+ var args = try parseInternal(
+ struct {},
+ null,
+ &titerator,
+ std.testing.allocator,
+ .print,
+ );
+ defer args.deinit();
+
+ try std.testing.expectEqual(args.raw_start_index, 2);
+ try std.testing.expectEqual(args.positionals.len, 5);
+}
diff --git a/src/gitversiontag.zig b/src/gitversiontag.zig
index 2a05a24..40aed0f 100644
--- a/src/gitversiontag.zig
+++ b/src/gitversiontag.zig
@@ -1,5 +1,5 @@
const std = @import("std");
-pub fn gitVersionTag(a: std.mem.Allocator) ![]const u8 {
+pub fn gitVersionTag(a: std.mem.Allocator) []const u8 {
const args = [_][]const u8{
"git",
"tag",
@@ -7,13 +7,14 @@ pub fn gitVersionTag(a: std.mem.Allocator) ![]const u8 {
};
if (std.ChildProcess.exec(.{ .argv = args[0..], .allocator = a })) |ret| {
- if (std.mem.split(u8, ret.stdout, "\n").next()) |firstline| {
+ var it = std.mem.split(u8, ret.stdout, "\n");
+ if (it.next()) |firstline| {
return firstline;
} else {
return "unknown";
}
} else |err| {
- std.log.err("Unable to spawn and wait: {any}", .{err});
+ std.log.err("Unable to spawn and wait: {!}", .{err});
}
return "unknown";
}
diff --git a/src/main.zig b/src/main.zig
index 3b4d57d..ae21302 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -108,7 +108,8 @@ fn addToAddressBook(
key: []const u8,
value: []const u8,
) !void {
- var file = try std.fs.cwd().openFile(filn, .{ .write = true });
+ // TODO check if write onlu would be OK
+ var file = try std.fs.cwd().openFile(filn, .{ .mode = .read_write });
defer file.close();
try file.seekFromEnd(0);
try file.writer().print("\n{s} : {s}", .{ key, value });
@@ -119,7 +120,7 @@ fn addEmailsToAddressBook(
map: std.StringHashMap([]const u8),
emails: std.StringHashMap([]const u8),
) !void {
- var file = try std.fs.cwd().openFile(filn, .{ .write = true });
+ var file = try std.fs.cwd().openFile(filn, .{ .mode = .read_write });
defer file.close();
try file.seekFromEnd(0);
var it = emails.iterator();
@@ -355,7 +356,7 @@ pub fn main() anyerror!void {
// do nothing
} else |err| {
const errwriter = std.io.getStdErr().writer();
- try errwriter.print("Error {s}: {s}\n", .{ err, filn });
+ try errwriter.print("Error {!}: {s}\n", .{ err, filn });
return;
}
const ret = try parseMailFromStdin(alloc);
@@ -393,7 +394,7 @@ pub fn main() anyerror!void {
//
} else |err| {
const errwriter = std.io.getStdErr().writer();
- try errwriter.print("Error {s}: {s}\n", .{ err, filn });
+ try errwriter.print("Error {!}: {s}\n", .{ err, filn });
return;
}
// check if key exists
@@ -426,7 +427,7 @@ pub fn main() anyerror!void {
// do nothing
} else |err| {
const errwriter = std.io.getStdErr().writer();
- try errwriter.print("Error {s}: {s}\n", .{ err, filn });
+ try errwriter.print("Error {!}: {s}\n", .{ err, filn });
return;
}
@@ -470,7 +471,7 @@ pub fn main() anyerror!void {
}
}
} else |err| {
- std.debug.print("{s}", .{err});
+ std.debug.print("{!}", .{err});
help();
}
}
--
2.40.0
From 7efc520767869f2eea82a65b943c0d3d64a2b33c Mon Sep 17 00:00:00 2001
From: Rene Schallner <rene@renerocks.ai>
Date: Wed, 28 Dec 2022 13:33:16 +0100
Subject: [PATCH 2/3] back-ported zig-master branch for zig-0.10
---
src/args.zig | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/args.zig b/src/args.zig
index 7fa3517..daf27ac 100644
--- a/src/args.zig
+++ b/src/args.zig
@@ -94,7 +94,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
var result_arena_allocator = result.arena.allocator();
var arglist = std.ArrayList([:0]const u8).init(allocator);
- defer arglist.deinit();
+ errdefer arglist.deinit();
var last_error: ?anyerror = null;
@@ -136,11 +136,11 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
const Tag = std.meta.Tag(Verb);
inline for (std.meta.fields(Verb)) |verb_info| {
if (verb.* == @field(Tag, verb_info.name)) {
- if (comptime canHaveFieldsAndIsNotZeroSized(verb_info.type)) {
- inline for (std.meta.fields(verb_info.type)) |fld| {
+ if (comptime canHaveFieldsAndIsNotZeroSized(verb_info.field_type)) {
+ inline for (std.meta.fields(verb_info.field_type)) |fld| {
if (std.mem.eql(u8, pair.name, fld.name)) {
try parseOption(
- verb_info.type,
+ verb_info.field_type,
result_arena_allocator,
&@field(verb.*, verb_info.name),
args_iterator,
@@ -205,7 +205,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
if (!found) {
const Tag = std.meta.Tag(Verb);
inline for (std.meta.fields(Verb)) |verb_info| {
- const VerbType = verb_info.type;
+ const VerbType = verb_info.field_type;
if (comptime canHaveFieldsAndIsNotZeroSized(VerbType)) {
if (verb.* == @field(Tag, verb_info.name)) {
const target_value = &@field(verb.*, verb_info.name);
@@ -260,7 +260,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
inline for (std.meta.fields(Verb)) |fld| {
if (std.mem.eql(u8, item, fld.name)) {
// found active verb, default-initialize it
- result.verb = @unionInit(Verb, fld.name, fld.type{});
+ result.verb = @unionInit(Verb, fld.name, fld.field_type{});
}
}
@@ -288,7 +288,7 @@ fn parseInternal(comptime Generic: type, comptime MaybeVerb: ?type, args_iterato
try arglist.append(try result_arena_allocator.dupeZ(u8, item));
}
- result.positionals = try arglist.toOwnedSlice();
+ result.positionals = arglist.toOwnedSlice();
return result;
}
--
2.40.0
From 85e5c4c3c8dfdec3d103ce3778e8cfe35ad4b074 Mon Sep 17 00:00:00 2001
From: Rene Schallner <rene@renerocks.ai>
Date: Wed, 28 Dec 2022 14:00:59 +0100
Subject: [PATCH 3/3] updated readme
---
README.md | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index e75ebba..8d0b6da 100644
--- a/README.md
+++ b/README.md
@@ -260,8 +260,8 @@ suggested as completion.
# Building it
-Make sure you have [zig 0.9.1](https://ziglang.org/download/) installed. Then
-run:
+Make sure you have the latest stable release of zig, [zig
+0.10.0](https://ziglang.org/download/) installed. Then run:
```console
zig build
@@ -272,7 +272,8 @@ This will produce `aercbook` in the `./zig-out/bin/` directory. From there,
# Tested with
-- zig 0.9.1
-- aerc 0.11.0
-- on Linux: NixOS 22.05 ([patched for aerc 0.11.0 instead of
- 0.10.0](https://sr.ht/~renerocksai/nixpkgs/))
+- zig 0.10.0
+- aerc 0.13.0
+- on Linux:
+ - NixOS 22.05 ([patched for aerc 0.11.0 instead of 0.10.0](https://sr.ht/~renerocksai/nixpkgs/))
+ - Ubuntu 20.04.5 LTS on crostini (ChromeOS x86_64)
--
2.40.0