Last active
December 5, 2020 21:52
-
-
Save alexnask/9e3226f06df97d4d58db184cfefd1b06 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const trait = std.meta.trait; | |
const assert = std.debug.assert; | |
fn NextCase(comptime src_error_set: type, comptime dst_error_set: type) type { | |
const src_errors = @typeInfo(src_error_set).ErrorSet.?; | |
const dst_errors = @typeInfo(dst_error_set).ErrorSet orelse @compileError("Cannot dispatch into anyerror"); | |
if (dst_errors.len == 0) | |
@compileError("Destination set is empty."); | |
if (dst_errors.len > src_errors.len) | |
@compileError("Destination set is bigger than current error set."); | |
for (dst_errors) |dst_error| { | |
for (src_errors) |src_error| { | |
if (std.mem.eql(u8, src_error.name, dst_error.name)) | |
break; | |
} else { | |
@compileError("Destination element " ++ dst_error.name ++ " is not in current error set"); | |
} | |
} | |
// Ok! Time to make our new error set. | |
const Error = std.builtin.TypeInfo.Error; | |
// @TODO: Try when this gets to zero | |
const err_set_len = src_errors.len - dst_errors.len; | |
var errors: [err_set_len]Error = undefined; | |
var idx = 0; | |
// For some reason I cant label here and use a continue in an if inside the second for | |
for (src_errors) |src_error| { | |
var found = false; | |
for (dst_errors) |dst_error| { | |
if (std.mem.eql(u8, src_error.name, dst_error.name)) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
errors[idx] = src_error; | |
idx += 1; | |
} | |
} | |
assert(idx == err_set_len); | |
const reduced_err_set = @Type(.{ .ErrorSet = &errors }); | |
return Case(reduced_err_set); | |
} | |
fn Case(comptime src_error_set: type) type { | |
assert(trait.is(.ErrorSet)(src_error_set)); | |
if (src_error_set == anyerror) @compileError("Cannot dispatch over anyerror"); | |
return struct { | |
const curr_error_set = src_error_set; | |
err: src_error_set, | |
pub inline fn case( | |
self: @This(), | |
comptime dst_error_set: type, | |
comptime action: macro(err: dst_error_set) void, | |
) !NextCase(src_error_set, dst_error_set) { | |
const Next = NextCase(src_error_set, dst_error_set); | |
inline for (comptime std.meta.fields(dst_error_set)) |err| { | |
if (self.err == @field(anyerror, err.name)) { | |
try action(@errSetCast(dst_error_set, self.err)); | |
} | |
} | |
return Next{ .err = @errSetCast(Next.curr_error_set, self.err) }; | |
} | |
}; | |
} | |
pub fn error_dispatch(err: anytype) Case(@TypeOf(err)) { | |
return .{ | |
.err = err, | |
}; | |
} | |
const SomeIoError = error{ | |
OutOfMemory, | |
AccessDenied, | |
FileClosed, | |
PipeBroken, | |
StatFailed, | |
}; | |
fn ohgod() SomeIoError!void { | |
return error.OutOfMemory; | |
} | |
// Make with fns then with macros, explore the possibilites | |
test "Dispatcher" { | |
ohgod() catch |err| return error_dispatch(err).case( | |
error{ FileClosed, StatFailed }, | |
|_| { recover(); return; }, | |
).case( | |
error{ PipeBroken, OutOfMemory }, | |
|e| { if (!some_cond) return e; }, | |
); // @TypeOf(whole_expr.err) == error{AccessDenie} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment