Commit 66e5205047

Jakub Konka <kubkon@jakubkonka.com>
2020-06-23 21:54:36
Refactor PreopenList.find()
This commit generalizes `std.fs.wasi.PreopenList.find(...)` allowing search by `std.fs.wasi.PreopenType` union type rather than by dir name. In the future releases of WASI, it is expected to have more preopen types (or capabilities) than just directories. This commit aligns itself with that vision. This is a potentially breaking change. However, since `std.fs.wasi.PreopenList` wasn't made part of any Zig release yet, I think we should be OK to introduce those changes without pointing to any deprecations.
1 parent 78c6d39
Changed files (3)
doc/langref.html.in
@@ -9738,7 +9738,7 @@ pub fn main() !void {
 }
       {#code_end#}
       <pre><code>$ wasmtime --dir=. preopens.wasm
-0: { .fd = 3, .Dir = '.' }
+0: Preopen{ .fd = 3, .type = PreopenType{ .Dir = '.' } }
 </code></pre>
       {#header_close#}
       {#header_close#}
lib/std/fs/wasi.zig
@@ -5,13 +5,39 @@ const Allocator = mem.Allocator;
 
 usingnamespace std.os.wasi;
 
-/// Type of WASI preopen.
+/// Type-tag of WASI preopen.
 ///
 /// WASI currently offers only `Dir` as a valid preopen resource.
-pub const PreopenType = enum {
+pub const PreopenTypeTag = enum {
     Dir,
 };
 
+/// Type of WASI preopen.
+///
+/// WASI currently offers only `Dir` as a valid preopen resource.
+pub const PreopenType = union(PreopenTypeTag) {
+    /// Preopened directory type.
+    Dir: []const u8,
+
+    const Self = @This();
+
+    pub fn eql(self: Self, other: PreopenType) bool {
+        if (!mem.eql(u8, @tagName(self), @tagName(other))) return false;
+
+        switch (self) {
+            PreopenTypeTag.Dir => |this_path| return mem.eql(u8, this_path, other.Dir),
+        }
+    }
+
+    pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void {
+        try out_stream.print("PreopenType{{ ", .{});
+        switch (self) {
+            PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
+        }
+        return out_stream.print(" }}", .{});
+    }
+};
+
 /// WASI preopen struct. This struct consists of a WASI file descriptor
 /// and type of WASI preopen. It can be obtained directly from the WASI
 /// runtime using `PreopenList.populate()` method.
@@ -20,29 +46,15 @@ pub const Preopen = struct {
     fd: fd_t,
 
     /// Type of the preopen.
-    @"type": union(PreopenType) {
-        /// Path to a preopened directory.
-        Dir: []const u8,
-    },
+    @"type": PreopenType,
 
-    const Self = @This();
-
-    /// Construct new `Preopen` instance of type `PreopenType.Dir` from
-    /// WASI file descriptor and WASI path.
-    pub fn newDir(fd: fd_t, path: []const u8) Self {
-        return Self{
+    /// Construct new `Preopen` instance.
+    pub fn new(fd: fd_t, preopen_type: PreopenType) Preopen {
+        return Preopen{
             .fd = fd,
-            .@"type" = .{ .Dir = path },
+            .@"type" = preopen_type,
         };
     }
-
-    pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void {
-        try out_stream.print("{{ .fd = {}, ", .{self.fd});
-        switch (self.@"type") {
-            PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
-        }
-        return out_stream.print(" }}", .{});
-    }
 };
 
 /// Dynamically-sized array list of WASI preopens. This struct is a
@@ -113,24 +125,18 @@ pub const PreopenList = struct {
                 ESUCCESS => {},
                 else => |err| return os.unexpectedErrno(err),
             }
-            const preopen = Preopen.newDir(fd, path_buf);
+            const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf });
             try self.buffer.append(preopen);
             fd += 1;
         }
     }
 
-    /// Find preopen by path. If the preopen exists, return it.
+    /// Find preopen by type. If the preopen exists, return it.
     /// Otherwise, return `null`.
-    ///
-    /// TODO make the function more generic by searching by `PreopenType` union. This will
-    /// be needed in the future when WASI extends its capabilities to resources
-    /// other than preopened directories.
-    pub fn find(self: Self, path: []const u8) ?*const Preopen {
-        for (self.buffer.items) |preopen| {
-            switch (preopen.@"type") {
-                PreopenType.Dir => |preopen_path| {
-                    if (mem.eql(u8, path, preopen_path)) return &preopen;
-                },
+    pub fn find(self: Self, preopen_type: PreopenType) ?*const Preopen {
+        for (self.buffer.items) |*preopen| {
+            if (preopen.@"type".eql(preopen_type)) {
+                return preopen;
             }
         }
         return null;
@@ -156,7 +162,8 @@ test "extracting WASI preopens" {
     try preopens.populate();
 
     std.testing.expectEqual(@as(usize, 1), preopens.asSlice().len);
-    const preopen = preopens.find(".") orelse unreachable;
-    std.testing.expect(std.mem.eql(u8, ".", preopen.@"type".Dir));
+    const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
+    std.debug.print("\n{}\n", .{preopen});
+    std.testing.expect(!preopen.@"type".eql(PreopenType{ .Dir = "." }));
     std.testing.expectEqual(@as(usize, 3), preopen.fd);
 }
lib/std/testing.zig
@@ -215,7 +215,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
         defer preopens.deinit();
         preopens.populate() catch
             @panic("unable to make tmp dir for testing: unable to populate preopens");
-        const preopen = preopens.find(".") orelse
+        const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse
             @panic("unable to make tmp dir for testing: didn't find '.' in the preopens");
 
         return std.fs.Dir{ .fd = preopen.fd };