I’m learning the basics of working with sockets so I was using std.os.linux for LibC-like wrappers to syscalls, and I noticed that it returns a usize, not an error union, which means I have to do the C-style error checking rather than being able to use try statements to bubble up errors. I don’t know how to do compile time shenanigans yet, so I just used a few vim macros to create this monstrosity of a mapping.
Long block of mapping code
const std = @import("std");
const builtin = @import("builtin");
const linux = std.os.linux;
/// Error set generated from std.os.linux.E
pub const LinuxError = error{
/// Operation not permitted
PERM,
/// No such file or directory
NOENT,
/// No such process
SRCH,
/// Interrupted system call
INTR,
/// I/O error
IO,
/// No such device or address
NXIO,
/// Arg list too long
@"2BIG",
/// Exec format error
NOEXEC,
/// Bad file number
BADF,
/// No child processes
CHILD,
/// Try again
/// Also means: WOULDBLOCK: operation would block
AGAIN,
/// Out of memory
NOMEM,
/// Permission denied
ACCES,
/// Bad address
FAULT,
/// Block device required
NOTBLK,
/// Device or resource busy
BUSY,
/// File exists
EXIST,
/// Cross-device link
XDEV,
/// No such device
NODEV,
/// Not a directory
NOTDIR,
/// Is a directory
ISDIR,
/// Invalid argument
INVAL,
/// File table overflow
NFILE,
/// Too many open files
MFILE,
/// Not a typewriter
NOTTY,
/// Text file busy
TXTBSY,
/// File too large
FBIG,
/// No space left on device
NOSPC,
/// Illegal seek
SPIPE,
/// Read-only file system
ROFS,
/// Too many links
MLINK,
/// Broken pipe
PIPE,
/// Math argument out of domain of func
DOM,
/// Math result not representable
RANGE,
/// Resource deadlock would occur
DEADLK,
/// File name too long
NAMETOOLONG,
/// No record locks available
NOLCK,
/// Function not implemented
NOSYS,
/// Directory not empty
NOTEMPTY,
/// Too many symbolic links encountered
LOOP,
/// No message of desired type
NOMSG,
/// Identifier removed
IDRM,
/// Channel number out of range
CHRNG,
/// Level 2 not synchronized
L2NSYNC,
/// Level 3 halted
L3HLT,
/// Level 3 reset
L3RST,
/// Link number out of range
LNRNG,
/// Protocol driver not attached
UNATCH,
/// No CSI structure available
NOCSI,
/// Level 2 halted
L2HLT,
/// Invalid exchange
BADE,
/// Invalid request descriptor
BADR,
/// Exchange full
XFULL,
/// No anode
NOANO,
/// Invalid request code
BADRQC,
/// Invalid slot
BADSLT,
/// Bad font file format
BFONT,
/// Device not a stream
NOSTR,
/// No data available
NODATA,
/// Timer expired
TIME,
/// Out of streams resources
NOSR,
/// Machine is not on the network
NONET,
/// Package not installed
NOPKG,
/// Object is remote
REMOTE,
/// Link has been severed
NOLINK,
/// Advertise error
ADV,
/// Srmount error
SRMNT,
/// Communication error on send
COMM,
/// Protocol error
PROTO,
/// Multihop attempted
MULTIHOP,
/// RFS specific error
DOTDOT,
/// Not a data message
BADMSG,
/// Value too large for defined data type
OVERFLOW,
/// Name not unique on network
NOTUNIQ,
/// File descriptor in bad state
BADFD,
/// Remote address changed
REMCHG,
/// Can not access a needed shared library
LIBACC,
/// Accessing a corrupted shared library
LIBBAD,
/// .lib section in a.out corrupted
LIBSCN,
/// Attempting to link in too many shared libraries
LIBMAX,
/// Cannot exec a shared library directly
LIBEXEC,
/// Illegal byte sequence
ILSEQ,
/// Interrupted system call should be restarted
RESTART,
/// Streams pipe error
STRPIPE,
/// Too many users
USERS,
/// Socket operation on non-socket
NOTSOCK,
/// Destination address required
DESTADDRREQ,
/// Message too long
MSGSIZE,
/// Protocol wrong type for socket
PROTOTYPE,
/// Protocol not available
NOPROTOOPT,
/// Protocol not supported
PROTONOSUPPORT,
/// Socket type not supported
SOCKTNOSUPPORT,
/// Operation not supported on transport endpoint
/// This code also means `NOTSUP`.
OPNOTSUPP,
/// Protocol family not supported
PFNOSUPPORT,
/// Address family not supported by protocol
AFNOSUPPORT,
/// Address already in use
ADDRINUSE,
/// Cannot assign requested address
ADDRNOTAVAIL,
/// Network is down
NETDOWN,
/// Network is unreachable
NETUNREACH,
/// Network dropped connection because of reset
NETRESET,
/// Software caused connection abort
CONNABORTED,
/// Connection reset by peer
CONNRESET,
/// No buffer space available
NOBUFS,
/// Transport endpoint is already connected
ISCONN,
/// Transport endpoint is not connected
NOTCONN,
/// Cannot send after transport endpoint shutdown
SHUTDOWN,
/// Too many references: cannot splice
TOOMANYREFS,
/// Connection timed out
TIMEDOUT,
/// Connection refused
CONNREFUSED,
/// Host is down
HOSTDOWN,
/// No route to host
HOSTUNREACH,
/// Operation already in progress
ALREADY,
/// Operation now in progress
INPROGRESS,
/// Stale NFS file handle
STALE,
/// Structure needs cleaning
UCLEAN,
/// Not a XENIX named type file
NOTNAM,
/// No XENIX semaphores available
NAVAIL,
/// Is a named type file
ISNAM,
/// Remote I/O error
REMOTEIO,
/// Quota exceeded
DQUOT,
/// No medium found
NOMEDIUM,
/// Wrong medium type
MEDIUMTYPE,
/// Operation canceled
CANCELED,
/// Required key not available
NOKEY,
/// Key has expired
KEYEXPIRED,
/// Key has been revoked
KEYREVOKED,
/// Key was rejected by service
KEYREJECTED,
// for robust mutexes
/// Owner died
OWNERDEAD,
/// State not recoverable
NOTRECOVERABLE,
/// Operation not possible due to RF-kill
RFKILL,
/// Memory page has hardware error
HWPOISON,
// nameserver query return codes
/// DNS server returned answer with no data
NSRNODATA,
/// DNS server claims query was misformatted
NSRFORMERR,
/// DNS server returned general failure
NSRSERVFAIL,
/// Domain name not found
NSRNOTFOUND,
/// DNS server does not implement requested operation
NSRNOTIMP,
/// DNS server refused query
NSRREFUSED,
/// Misformatted DNS query
NSRBADQUERY,
/// Misformatted domain name
NSRBADNAME,
/// Unsupported address family
NSRBADFAMILY,
/// Misformatted DNS reply
NSRBADRESP,
/// Could not contact DNS servers
NSRCONNREFUSED,
/// Timeout while contacting DNS servers
NSRTIMEOUT,
/// End of file
NSROF,
/// Error reading file
NSRFILE,
/// Out of memory
NSRNOMEM,
/// Application terminated lookup
NSRDESTRUCTION,
/// Domain name is too long
NSRQUERYDOMAINTOOLONG,
/// Domain name is too long
NSRCNAMELOOP,
};
/// Converts Libc errno to Zig error values, courtesy of vim macros running
/// at 1 frame per minute.
pub fn errnoToError(errno: linux.E) LinuxError {
if (builtin.os.tag != .linux) {
@compileError("Idk how to work with anything but linux.");
}
switch (errno) {
.PERM => LinuxError.PERM,
.NOENT => LinuxError.NOENT,
.SRCH => LinuxError.SRCH,
.INTR => LinuxError.INTR,
.IO => LinuxError.IO,
.NXIO => LinuxError.NXIO,
.@"2BIG" => LinuxError.@"2BIG",
.NOEXEC => LinuxError.NOEXEC,
.BADF => LinuxError.BADF,
.CHILD => LinuxError.CHILD,
.AGAIN => LinuxError.AGAIN,
.NOMEM => LinuxError.NOMEM,
.ACCES => LinuxError.ACCES,
.FAULT => LinuxError.FAULT,
.NOTBLK => LinuxError.NOTBLK,
.BUSY => LinuxError.BUSY,
.EXIST => LinuxError.EXIST,
.XDEV => LinuxError.XDEV,
.NODEV => LinuxError.NODEV,
.NOTDIR => LinuxError.NOTDIR,
.ISDIR => LinuxError.ISDIR,
.INVAL => LinuxError.INVAL,
.NFILE => LinuxError.NFILE,
.MFILE => LinuxError.MFILE,
.NOTTY => LinuxError.NOTTY,
.TXTBSY => LinuxError.TXTBSY,
.FBIG => LinuxError.FBIG,
.NOSPC => LinuxError.NOSPC,
.SPIPE => LinuxError.SPIPE,
.ROFS => LinuxError.ROFS,
.MLINK => LinuxError.MLINK,
.PIPE => LinuxError.PIPE,
.DOM => LinuxError.DOM,
.RANGE => LinuxError.RANGE,
.DEADLK => LinuxError.DEADLK,
.NAMETOOLONG => LinuxError.NAMETOOLONG,
.NOLCK => LinuxError.NOLCK,
.NOSYS => LinuxError.NOSYS,
.NOTEMPTY => LinuxError.NOTEMPTY,
.LOOP => LinuxError.LOOP,
.NOMSG => LinuxError.NOMSG,
.IDRM => LinuxError.IDRM,
.CHRNG => LinuxError.CHRNG,
.L2NSYNC => LinuxError.L2NSYNC,
.L3HLT => LinuxError.L3HLT,
.L3RST => LinuxError.L3RST,
.LNRNG => LinuxError.LNRNG,
.UNATCH => LinuxError.UNATCH,
.NOCSI => LinuxError.NOCSI,
.L2HLT => LinuxError.L2HLT,
.BADE => LinuxError.BADE,
.BADR => LinuxError.BADR,
.XFULL => LinuxError.XFULL,
.NOANO => LinuxError.NOANO,
.BADRQC => LinuxError.BADRQC,
.BADSLT => LinuxError.BADSLT,
.BFONT => LinuxError.BFONT,
.NOSTR => LinuxError.NOSTR,
.NODATA => LinuxError.NODATA,
.TIME => LinuxError.TIME,
.NOSR => LinuxError.NOSR,
.NONET => LinuxError.NONET,
.NOPKG => LinuxError.NOPKG,
.REMOTE => LinuxError.REMOTE,
.NOLINK => LinuxError.NOLINK,
.ADV => LinuxError.ADV,
.SRMNT => LinuxError.SRMNT,
.COMM => LinuxError.COMM,
.PROTO => LinuxError.PROTO,
.MULTIHOP => LinuxError.MULTIHOP,
.DOTDOT => LinuxError.DOTDOT,
.BADMSG => LinuxError.BADMSG,
.OVERFLOW => LinuxError.OVERFLOW,
.NOTUNIQ => LinuxError.NOTUNIQ,
.BADFD => LinuxError.BADFD,
.REMCHG => LinuxError.REMCHG,
.LIBACC => LinuxError.LIBACC,
.LIBBAD => LinuxError.LIBBAD,
.LIBSCN => LinuxError.LIBSCN,
.LIBMAX => LinuxError.LIBMAX,
.LIBEXEC => LinuxError.LIBEXEC,
.ILSEQ => LinuxError.ILSEQ,
.RESTART => LinuxError.RESTART,
.STRPIPE => LinuxError.STRPIPE,
.USERS => LinuxError.USERS,
.NOTSOCK => LinuxError.NOTSOCK,
.DESTADDRREQ => LinuxError.DESTADDRREQ,
.MSGSIZE => LinuxError.MSGSIZE,
.PROTOTYPE => LinuxError.PROTOTYPE,
.NOPROTOOPT => LinuxError.NOPROTOOPT,
.PROTONOSUPPORT => LinuxError.PROTONOSUPPORT,
.SOCKTNOSUPPORT => LinuxError.SOCKTNOSUPPORT,
.OPNOTSUPP => LinuxError.OPNOTSUPP,
.PFNOSUPPORT => LinuxError.PFNOSUPPORT,
.AFNOSUPPORT => LinuxError.AFNOSUPPORT,
.ADDRINUSE => LinuxError.ADDRINUSE,
.ADDRNOTAVAIL => LinuxError.ADDRNOTAVAIL,
.NETDOWN => LinuxError.NETDOWN,
.NETUNREACH => LinuxError.NETUNREACH,
.NETRESET => LinuxError.NETRESET,
.CONNABORTED => LinuxError.CONNABORTED,
.CONNRESET => LinuxError.CONNRESET,
.NOBUFS => LinuxError.NOBUFS,
.ISCONN => LinuxError.ISCONN,
.NOTCONN => LinuxError.NOTCONN,
.SHUTDOWN => LinuxError.SHUTDOWN,
.TOOMANYREFS => LinuxError.TOOMANYREFS,
.TIMEDOUT => LinuxError.TIMEDOUT,
.CONNREFUSED => LinuxError.CONNREFUSED,
.HOSTDOWN => LinuxError.HOSTDOWN,
.HOSTUNREACH => LinuxError.HOSTUNREACH,
.ALREADY => LinuxError.ALREADY,
.INPROGRESS => LinuxError.INPROGRESS,
.STALE => LinuxError.STALE,
.UCLEAN => LinuxError.UCLEAN,
.NOTNAM => LinuxError.NOTNAM,
.NAVAIL => LinuxError.NAVAIL,
.ISNAM => LinuxError.ISNAM,
.REMOTEIO => LinuxError.REMOTEIO,
.DQUOT => LinuxError.DQUOT,
.NOMEDIUM => LinuxError.NOMEDIUM,
.MEDIUMTYPE => LinuxError.MEDIUMTYPE,
.CANCELED => LinuxError.CANCELED,
.NOKEY => LinuxError.NOKEY,
.KEYEXPIRED => LinuxError.KEYEXPIRED,
.KEYREVOKED => LinuxError.KEYREVOKED,
.KEYREJECTED => LinuxError.KEYREJECTED,
.OWNERDEAD => LinuxError.OWNERDEAD,
.NOTRECOVERABLE => LinuxError.NOTRECOVERABLE,
.RFKILL => LinuxError.RFKILL,
.HWPOISON => LinuxError.HWPOISON,
.NSRNODATA => LinuxError.NSRNODATA,
.NSRFORMERR => LinuxError.NSRFORMERR,
.NSRSERVFAIL => LinuxError.NSRSERVFAIL,
.NSRNOTFOUND => LinuxError.NSRNOTFOUND,
.NSRNOTIMP => LinuxError.NSRNOTIMP,
.NSRREFUSED => LinuxError.NSRREFUSED,
.NSRBADQUERY => LinuxError.NSRBADQUERY,
.NSRBADNAME => LinuxError.NSRBADNAME,
.NSRBADFAMILY => LinuxError.NSRBADFAMILY,
.NSRBADRESP => LinuxError.NSRBADRESP,
.NSRCONNREFUSED => LinuxError.NSRCONNREFUSED,
.NSRTIMEOUT => LinuxError.NSRTIMEOUT,
.NSROF => LinuxError.NSROF,
.NSRFILE => LinuxError.NSRFILE,
.NSRNOMEM => LinuxError.NSRNOMEM,
.NSRDESTRUCTION => LinuxError.NSRDESTRUCTION,
.NSRQUERYDOMAINTOOLONG => LinuxError.NSRQUERYDOMAINTOOLONG,
.NSRCNAMELOOP => LinuxError.NSRCNAMELOOP,
}
}
pub fn err(rv: usize) LinuxError!usize {
if (rv >= 0) {
return rv;
}
return errnoToError(linux.errno(rv));
}
Now I get to write code like this when using syscalls
const std = @import("std");
const linux = std.os.linux;
const e = @import("linuxerrors.zig");
pub fn createTcpServer(host_ip: usize, port: usize) e.LinuxError!void {
_ = host_ip;
_ = port;
const sock_fd = try e.err(linux.socket(
linux.AF.INET,
linux.SOCK.STREAM,
linux.IPPROTO.TCP,
));
}
My two questions are:
- Is there a better way to do this? maybe this mapping exists somewhere in the standard library that I can use or there’s some comptime magic to convert
std.os.linux.Eto an error set that is LSP-friendly? - How do I actually report the errors? If I want to do it in C I do
perror("thing I was trynna do"), but since I’m trying to not link against libc (for fun, no particular reason), I was wondering if I could use comptime magic to access to docstring above the error variant, convert it to a string and report the error? Otherwise I could use more vim macros to just create the mapping.
