// errors — domain-agnostic error types. Mirrors ref/hare/errors/. // // Named-void tagged-union variants, so `(T | errors.invalid | ...)` // composes with every other module's error surface at the tag level. // Per-domain errors (io.eof, io.closed, io.underread, strconv.overflow, // ...) live in their own modules; this module ships the generic // conditions Hare's errors:: exports plus the [[errno]] bridge that // wraps a raw [[os.errno]] into a portable [[error]]. package errors; import os; // The named conditions, ordered per ref/hare/errors/common.ha. // The requested resource is not available. export type busy = !void; // An attempt was made to create a resource which already exists. export type exists = !void; // A function was called with an invalid combination of arguments. export type invalid = !void; // The user does not have permission to use this resource. export type noaccess = !void; // An entry was requested which does not exist. export type noentry = !void; // The requested operation caused a numeric overflow condition. export type overflow = !void; // The requested operation is not supported. export type unsupported = !void; // The requested operation timed out. export type timeout = !void; // The requested operation was cancelled. export type cancelled = !void; // A connection attempt was refused. export type refused = !void; // An operation was interrupted. export type interrupted = !void; // The user should attempt an operation again. export type again = !void; // Network unreachable export type netunreachable = !void; // Up to 24 bytes of arbitrary, 8-byte-aligned storage for the opaque // error type's domain-specific data. ref/hare/errors/opaque.ha:41. export type opaque_data = [3]u64; // An "opaque" error wraps an implementation-specific underlying error // behind a function that stringifies it plus a small storage area. // ref/hare/errors/opaque.ha:32. The `strerror` field keeps Hare's `*fn` // pointer (filled via `(&fn): *fn(...)`, mirroring io's `*reader` slot, // lib/io/types.ww:53); only Hare's `const` is dropped (ww has none, cf // lib/io/types.ww:57). export type opaque_ = !struct { strerror: *fn(op: *opaque_data) str, data: opaque_data, }; // A tagged union of all error types. ref/hare/errors/common.ha:43. // Enumerated explicitly rather than spread via Hare's `...error` — // ww's spread-flatten layout is the deferred #199b / #204 fix. export type error = !( busy | exists | invalid | noaccess | noentry | overflow | unsupported | timeout | cancelled | refused | interrupted | again | netunreachable | opaque_ ); // Wraps an [[os.errno]] to produce an [[error]], which may be // [[opaque_]]. ref/hare/errors/rt.ha:9. The mapped errnos become named // conditions; the unmapped tail is carried in an [[opaque_]] whose // stringifier defers to [[os.strerror]]. export fn errno(e: os.errno) error = { // A `!void` condition is returned by instance, not by name: a bare // `return refused;` would emit an undefined symbol reference (the // type, not a value). cf lib/io/stream.ww:53-56. The instance // auto-widens to the [[error]] union on return. switch (e) { case os.ECONNREFUSED: { let r: refused; return r; }; case os.ECANCELED: { let r: cancelled; return r; }; case os.EOVERFLOW: { let r: overflow; return r; }; case os.EACCES: { let r: noaccess; return r; }; case os.EINVAL: { let r: invalid; return r; }; case os.EEXIST: { let r: exists; return r; }; case os.ENOENT: { let r: noentry; return r; }; case os.ETIMEDOUT: { let r: timeout; return r; }; case os.EBUSY: { let r: busy; return r; }; case os.EINTR: { let r: interrupted; return r; }; case os.EAGAIN: { let r: again; return r; }; case os.ENETUNREACH: { let r: netunreachable; return r; }; }; // An unmapped errno falls through the switch into the opaque_ wrap // below. ww switches aren't required exhaustive, so Hare's terminal // `case => void;` (rt.ha:24, present only to satisfy exhaustiveness) // is dropped rather than written as an explicit no-op default — // matching the sibling [[os.strerror]] fall-through. // // ww has no `static assert`; Hare guards size(errno) <= // size(opaque_data) there. The invariant holds structurally: // opaque_data is [3]u64 (24B), errno is one machine int. let err: opaque_; err.strerror = (&rt_strerror): *fn(op: *opaque_data) str; let ptr = (&err.data): *os.errno; *ptr = e; return err; }; // rt_strerror — the [[opaque_]] stringifier for an errno wrapped by // [[errno]]. ref/hare/errors/rt.ha:31. fn rt_strerror(op: *opaque_data) str = { let e = (op): *os.errno; return os.strerror(*e); };