Skip to content

Instantly share code, notes, and snippets.

@alexnask
Last active December 5, 2020 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexnask/9e3226f06df97d4d58db184cfefd1b06 to your computer and use it in GitHub Desktop.
Save alexnask/9e3226f06df97d4d58db184cfefd1b06 to your computer and use it in GitHub Desktop.
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