-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherror_abi.zig
143 lines (125 loc) · 4.12 KB
/
error_abi.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
const std = @import("std");
const expect = std.testing.expect;
const expectError = std.testing.expectError;
/// Value that serializes a !T
/// The T value is memset to all zeros.
pub fn ErrorVal(comptime T: type) type {
return packed struct {
const Self = @This();
err: u32,
val: T,
pub fn init() Self {
var ret = Self{ .err = 0, .val = undefined };
@memset(@ptrCast([*]u8, &ret.val), 0, @sizeOf(T));
return ret;
}
};
}
/// Hash an error value string; is a simple sum hash.
/// Must be the actual error name, not one from a ErrorSet
/// i.e "error.<whatever>"
pub fn errorHash(comptime err: anyerror) u32 {
const errname = @errorName(err);
comptime var ret: u32 = 0;
comptime {
for (errname) |c| {
ret += c;
ret += @truncate(u8, ret);
}
}
return ret;
}
/// Given an ErrorSet, a return type, and an ErrorVal of the same return type
/// This fn will deduce if the result has an error, if not return the value
/// if so, return the actual error.
/// If the error that was returned is unknown, then returns error.UnknownExternalError.
pub fn errorUnwrap(comptime errset: type, comptime T: type, result: ErrorVal(T)) !T {
comptime checkForCollisions(errset);
if (result.err == 0) {
return result.val;
}
const errs = comptime std.meta.fields(errset);
inline for (errs) |err| {
const err_val = @intToError(err.value);
if (errorHash(err_val) == result.err) {
return err_val;
}
}
return error.UnknownExternalError;
}
/// Wraps a result from a fn that can error and returns a errorval
pub fn errorWrap(comptime errset: type, comptime T: type, val: errset!T) ErrorVal(T) {
comptime checkForCollisions(errset);
var res = ErrorVal(T).init();
if (val) |actual_val| {
res.val = actual_val;
} else |thiserr| {
inline for (comptime std.meta.fields(errset)) |err| {
const err_val = @intToError(err.value);
if (thiserr == err_val) {
res.err = errorHash(err_val);
break;
}
}
}
return res;
}
fn checkForCollisions(comptime errset: type) void {
comptime {
const errs = std.meta.fields(errset);
for (errs) |err1, i| {
const errval1 = @intToError(err1.value);
for (errs) |err2, j| {
if (i == j) continue;
const errval2 = @intToError(err2.value);
if (errorHash(errval1) == errorHash(errval2)) {
const msg = "Hash collision of error." ++ err1.name ++ " and error." ++ err2.name;
@compileError(msg);
}
}
}
}
}
// Error set to test with
const TestingErrors = error{
LameError,
WackError,
};
// exported fn to test with
// add export to test this fn as exported, removed to not export in importing projects
fn testexport(err: bool) ErrorVal(u32) {
var res = errorWrap(TestingErrors, u32, dummyfn(err));
return res;
}
// test fn that may return an error
fn dummyfn(err: bool) !u32 {
if (err) return error.WackError;
return 1;
}
test "exported error" {
var ret = ErrorVal(u32).init();
expect(ret.err == 0);
expect(ret.val == 0);
}
const Collisions = error{
abcd,
abdc,
dcba,
cdba,
};
test "errorHash and collisions" {
const hash = errorHash(error.LameError);
expect(hash == 1968);
checkForCollisions(Collisions);
expect(errorHash(error.abcd) != errorHash(error.abdc));
expect(errorHash(error.abcd) != errorHash(error.dcba));
expect(errorHash(error.abcd) != errorHash(error.cdba));
}
test "errorWrap/errorUnwrap" {
var result = errorWrap(TestingErrors, u32, error.LameError);
expectError(error.LameError, errorUnwrap(TestingErrors, u32, result));
result.err = errorHash(error.OutOfMemory);
expectError(error.UnknownExternalError, errorUnwrap(TestingErrors, u32, result));
expect(1 == try errorUnwrap(TestingErrors, u32, testexport(false)));
expectError(error.WackError, errorUnwrap(TestingErrors, u32, testexport(true)));
}