diff --git a/src/watcher/file.zig b/src/watcher/file.zig index 7c68b61..d916bd1 100644 --- a/src/watcher/file.zig +++ b/src/watcher/file.zig @@ -38,13 +38,19 @@ fn FileStream(comptime xev: type) type { /// The underlying file fd: FdType, - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, .read = .read, .write = .write, .threadpool = true, }); + pub const close = S.close; + pub const poll = S.poll; + pub const read = S.read; + pub const write = S.write; + pub const writeInit = S.writeInit; + pub const queueWrite = S.queueWrite; /// Initialize a File from a std.fs.File. pub fn init(file: std.fs.File) !Self { @@ -318,7 +324,7 @@ fn FileDynamic(comptime xev: type) type { pub const Union = xev.Union(&.{"File"}); - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, .read = .read, @@ -326,6 +332,11 @@ fn FileDynamic(comptime xev: type) type { .threadpool = true, .type = "File", }); + pub const close = S.close; + pub const poll = S.poll; + pub const read = S.read; + pub const write = S.write; + pub const queueWrite = S.queueWrite; pub fn init(file: std.fs.File) !Self { return .{ .backend = switch (xev.backend) { @@ -492,6 +503,18 @@ fn FileTests( comptime Impl: type, ) type { return struct { + test "File: Stream decls" { + if (!@hasDecl(Impl, "S")) return; + const Stream = Impl.S; + inline for (@typeInfo(Stream).@"struct".decls) |decl| { + const Decl = @TypeOf(@field(Stream, decl.name)); + if (Decl == void) continue; + if (!@hasDecl(Impl, decl.name)) { + @compileError("missing decl: " ++ decl.name); + } + } + } + test "kqueue: zero-length read for readiness" { if (builtin.os.tag != .macos) return error.SkipZigTest; diff --git a/src/watcher/stream.zig b/src/watcher/stream.zig index a947244..4b1ca38 100644 --- a/src/watcher/stream.zig +++ b/src/watcher/stream.zig @@ -7,10 +7,10 @@ const queue = @import("../queue.zig"); /// Options for creating a stream type. Each of the options makes the /// functionality available for the stream. pub const Options = struct { - read: ReadMethod, - write: WriteMethod, - close: bool, - poll: bool, + read: ReadMethod = .none, + write: WriteMethod = .none, + close: bool = false, + poll: bool = false, /// True to schedule the read/write on the threadpool. threadpool: bool = false, @@ -104,10 +104,9 @@ pub fn Shared(comptime xev: type) type { }; } -/// Creates a stream type that is meant to be embedded within other -/// types using "usingnamespace". A stream is something that supports read, -/// write, close, etc. The exact operations supported are defined by the -/// "options" struct. +/// Creates a stream type that is meant to be embedded within other types. +/// A stream is something that supports read, write, close, etc. The exact +/// operations supported are defined by the "options" struct. /// /// T requirements: /// - field named "fd" of type fd_t or socket_t @@ -115,10 +114,25 @@ pub fn Shared(comptime xev: type) type { /// pub fn Stream(comptime xev: type, comptime T: type, comptime options: Options) type { return struct { - pub usingnamespace if (options.close) Closeable(xev, T, options) else struct {}; - pub usingnamespace if (options.poll) Pollable(xev, T, options) else struct {}; - pub usingnamespace if (options.read != .none) Readable(xev, T, options) else struct {}; - pub usingnamespace if (options.write != .none) Writeable(xev, T, options) else struct {}; + const C_: ?type = if (options.close) Closeable(xev, T, options) else null; + pub const close = if (C_) |C| C.close else {}; + + const P_: ?type = if (options.poll) Pollable(xev, T, options) else null; + pub const poll = if (P_) |P| poll: { + if (!@hasDecl(P, "poll")) break :poll {}; + break :poll P.poll; + } else {}; + + const R_: ?type = if (options.read != .none) Readable(xev, T, options) else null; + pub const read = if (R_) |R| R.read else {}; + + const W_: ?type = if (options.write != .none) Writeable(xev, T, options) else null; + pub const writeInit = if (W_) |W| writeInit: { + if (xev.dynamic) break :writeInit {}; + break :writeInit W.writeInit; + } else {}; + pub const write = if (W_) |W| W.write else null; + pub const queueWrite = if (W_) |W| W.queueWrite else {}; }; } @@ -1034,13 +1048,19 @@ pub fn GenericStream(comptime xev: type) type { pub const Union = xev.Union(&.{"Stream"}); - pub usingnamespace Stream(xev, Self, .{ + const S = Stream(xev, Self, .{ .close = true, .poll = true, .read = .read, .write = .write, .type = "Stream", }); + pub const close = S.close; + pub const poll = S.poll; + pub const read = S.read; + pub const write = S.write; + pub const writeInit = S.writeInit; + pub const queueWrite = S.queueWrite; pub fn initFd(fd: std.posix.pid_t) Self { return .{ .backend = switch (xev.backend) { @@ -1075,12 +1095,18 @@ pub fn GenericStream(comptime xev: type) type { /// The underlying file fd: std.posix.fd_t, - pub usingnamespace Stream(xev, Self, .{ + const S = Stream(xev, Self, .{ .close = true, .poll = true, .read = .read, .write = .write, }); + pub const close = S.close; + pub const poll = S.poll; + pub const read = S.read; + pub const write = S.write; + pub const writeInit = S.writeInit; + pub const queueWrite = S.queueWrite; /// Initialize a generic stream from a file descriptor. pub fn initFd(fd: std.posix.fd_t) Self { @@ -1104,6 +1130,17 @@ pub fn GenericStream(comptime xev: type) type { fn GenericStreamTests(comptime xev: type, comptime Impl: type) type { return struct { + test "Stream decls" { + if (!@hasDecl(Impl, "S")) return; + inline for (@typeInfo(Impl.S).@"struct".decls) |decl| { + const Decl = @TypeOf(@field(Impl.S, decl.name)); + if (Decl == void) continue; + if (!@hasDecl(Impl, decl.name)) { + @compileError("missing decl: " ++ decl.name); + } + } + } + test "pty: child to parent" { const testing = std.testing; switch (builtin.os.tag) { diff --git a/src/watcher/tcp.zig b/src/watcher/tcp.zig index 9c42672..fa1812d 100644 --- a/src/watcher/tcp.zig +++ b/src/watcher/tcp.zig @@ -26,12 +26,18 @@ fn TCPStream(comptime xev: type) type { fd: FdType, - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, .read = .recv, .write = .send, }); + pub const close = S.close; + pub const poll = S.poll; + pub const read = S.read; + pub const write = S.write; + pub const writeInit = S.writeInit; + pub const queueWrite = S.queueWrite; /// Initialize a new TCP with the family from the given address. Only /// the family is used, the actual address has no impact on the created @@ -249,7 +255,7 @@ fn TCPDynamic(comptime xev: type) type { pub const Union = xev.Union(&.{"TCP"}); - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, .read = .read, @@ -257,6 +263,11 @@ fn TCPDynamic(comptime xev: type) type { .threadpool = true, .type = "TCP", }); + pub const close = S.close; + pub const poll = S.poll; + pub const read = S.read; + pub const write = S.write; + pub const queueWrite = S.queueWrite; pub fn init(addr: std.net.Address) !Self { return .{ .backend = switch (xev.backend) { @@ -492,6 +503,18 @@ fn TCPDynamic(comptime xev: type) type { fn TCPTests(comptime xev: type, comptime Impl: type) type { return struct { + test "TCP: Stream decls" { + if (!@hasDecl(Impl, "S")) return; + const Stream = Impl.S; + inline for (@typeInfo(Stream).@"struct".decls) |decl| { + const Decl = @TypeOf(@field(Stream, decl.name)); + if (Decl == void) continue; + if (!@hasDecl(Impl, decl.name)) { + @compileError("missing decl: " ++ decl.name); + } + } + } + test "TCP: accept/connect/send/recv/close" { // We have no way to get a socket in WASI from a WASI context. if (builtin.os.tag == .wasi) return error.SkipZigTest; diff --git a/src/watcher/udp.zig b/src/watcher/udp.zig index 7702f88..7df3ab1 100644 --- a/src/watcher/udp.zig +++ b/src/watcher/udp.zig @@ -46,12 +46,12 @@ fn UDPSendto(comptime xev: type) type { userdata: ?*anyopaque, }; - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, - .read = .none, - .write = .none, }); + pub const close = S.close; + pub const poll = S.poll; /// Initialize a new UDP with the family from the given address. Only /// the family is used, the actual address has no impact on the created @@ -226,12 +226,10 @@ fn UDPSendtoIOCP(comptime xev: type) type { userdata: ?*anyopaque, }; - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, - .poll = false, - .read = .none, - .write = .none, }); + pub const close = S.close; /// Initialize a new UDP with the family from the given address. Only /// the family is used, the actual address has no impact on the created @@ -423,12 +421,12 @@ fn UDPSendMsg(comptime xev: type) type { }, }; - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, - .read = .none, - .write = .none, }); + pub const close = S.close; + pub const poll = S.poll; /// Initialize a new UDP with the family from the given address. Only /// the family is used, the actual address has no impact on the created @@ -694,13 +692,13 @@ fn UDPDynamic(comptime xev: type) type { pub const Union = xev.Union(&.{"UDP"}); pub const State = xev.Union(&.{ "UDP", "State" }); - pub usingnamespace stream.Stream(xev, Self, .{ + const S = stream.Stream(xev, Self, .{ .close = true, .poll = true, - .read = .none, - .write = .none, .type = "UDP", }); + pub const close = S.close; + pub const poll = S.poll; pub fn init(addr: std.net.Address) !Self { return .{ .backend = switch (xev.backend) { @@ -901,6 +899,18 @@ fn UDPDynamic(comptime xev: type) type { fn UDPTests(comptime xev: type, comptime Impl: type) type { return struct { + test "UDP: Stream decls" { + if (!@hasDecl(Impl, "S")) return; + const Stream = Impl.S; + inline for (@typeInfo(Stream).@"struct".decls) |decl| { + const Decl = @TypeOf(@field(Stream, decl.name)); + if (Decl == void) continue; + if (!@hasDecl(Impl, decl.name)) { + @compileError("missing decl: " ++ decl.name); + } + } + } + test "UDP: read/write" { if (builtin.os.tag == .freebsd) return error.SkipZigTest; const testing = std.testing; diff --git a/src/windows.zig b/src/windows.zig index c2b2fa0..7e13a73 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -4,7 +4,35 @@ const std = @import("std"); const windows = std.os.windows; const posix = std.posix; -pub usingnamespace std.os.windows; +// Forwarded declarations of std.os.windows. +pub const DWORD = windows.DWORD; +pub const FALSE = windows.FALSE; +pub const TRUE = windows.TRUE; +pub const INFINITE = windows.INFINITE; +pub const HANDLE = windows.HANDLE; +pub const INVALID_HANDLE_VALUE = windows.INVALID_HANDLE_VALUE; +pub const OVERLAPPED = windows.OVERLAPPED; +pub const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY; +pub const DUPLICATE_SAME_ACCESS = windows.DUPLICATE_SAME_ACCESS; +pub const GENERIC_READ = windows.GENERIC_READ; +pub const GENERIC_WRITE = windows.GENERIC_WRITE; +pub const OPEN_ALWAYS = windows.OPEN_ALWAYS; +pub const FILE_FLAG_OVERLAPPED = windows.FILE_FLAG_OVERLAPPED; +pub const ReadFileError = windows.ReadFileError; +pub const WriteFileError = windows.WriteFileError; +pub const Win32Error = windows.Win32Error; +pub const WSASocketW = windows.WSASocketW; +pub const kernel32 = windows.kernel32; +pub const ws2_32 = windows.ws2_32; +pub const unexpectedWSAError = windows.unexpectedWSAError; +pub const unexpectedError = windows.unexpectedError; +pub const sliceToPrefixedFileW = windows.sliceToPrefixedFileW; +pub const CloseHandle = windows.CloseHandle; +pub const QueryPerformanceCounter = windows.QueryPerformanceCounter; +pub const QueryPerformanceFrequency = windows.QueryPerformanceFrequency; +pub const GetQueuedCompletionStatusEx = windows.GetQueuedCompletionStatusEx; +pub const PostQueuedCompletionStatus = windows.PostQueuedCompletionStatus; +pub const CreateIoCompletionPort = windows.CreateIoCompletionPort; pub extern "kernel32" fn DeleteFileW(lpFileName: [*:0]const u16) callconv(windows.WINAPI) windows.BOOL; @@ -182,7 +210,7 @@ pub const exp = struct { lpSecurityAttributes: ?*windows.SECURITY_ATTRIBUTES, lpName: ?windows.LPCSTR, ) !windows.HANDLE { - const handle = kernel32.CreateJobObjectA(lpSecurityAttributes, lpName); + const handle = exp.kernel32.CreateJobObjectA(lpSecurityAttributes, lpName); return switch (windows.kernel32.GetLastError()) { .SUCCESS => handle, .ALREADY_EXISTS => CreateJobObjectError.AlreadyExists, @@ -191,7 +219,7 @@ pub const exp = struct { } pub fn AssignProcessToJobObject(hJob: windows.HANDLE, hProcess: windows.HANDLE) posix.UnexpectedError!void { - const result: windows.BOOL = kernel32.AssignProcessToJobObject(hJob, hProcess); + const result: windows.BOOL = exp.kernel32.AssignProcessToJobObject(hJob, hProcess); if (result == windows.FALSE) { const err = windows.kernel32.GetLastError(); return switch (err) { @@ -206,7 +234,7 @@ pub const exp = struct { lpJobObjectInformation: windows.LPVOID, cbJobObjectInformationLength: windows.DWORD, ) posix.UnexpectedError!void { - const result: windows.BOOL = kernel32.SetInformationJobObject( + const result: windows.BOOL = exp.kernel32.SetInformationJobObject( hJob, JobObjectInformationClass, lpJobObjectInformation,