home
tpb

repository View

tests(Clip): added explicit interface checking

change ID
tutkzxlsvtqvkxwytnsyxyvkzyvkvzqz
git commit
ecdcf0c0b89e478e8bf65cbd76655411c341c156
author
Grace Yoder <grace_yoder@outlook.com> - 2026-06-24T17:48:16-06:00
committer
Grace Yoder <grace_yoder@outlook.com> - 2026-06-25T16:42:38-06:00
parents
uqttuptknuzk
refs
signature
Present (not validated)
tests(Clip): added explicit interface checking

tree

type name size
file .crow/build.yml 2096 bytes
file .crow/release.yml 566 bytes
file .gitignore 354 bytes
file LICENSE 1073 bytes
file README.md 632 bytes
file build.zig 1056 bytes
file build.zig.zon 247 bytes
file src/Clip/OSC52.zig 4611 bytes
file src/Clip.zig 1236 bytes
file src/main.zig 1370 bytes

diff

build.zig

modified (file → file)

--- a/build.zig
+++ b/build.zig
@@ -14,6 +14,19 @@
 
     b.installArtifact(exe);
 
+    const tests = b.addTest(.{
+        .name = "tpb",
+        .root_module = b.createModule(.{
+            .root_source_file = b.path("src/main.zig"),
+            .target = target,
+            .optimize = optimize,
+        }),
+    });
+
+    const test_step = b.step("test", "Run tests");
+    const run_tests = b.addRunArtifact(tests);
+    test_step.dependOn(&run_tests.step);
+
     const run_step = b.step("run", "Run the app");
 
     const run_cmd = b.addRunArtifact(exe);

src/Clip.zig

modified (file → file)

--- a/src/Clip.zig
+++ b/src/Clip.zig
@@ -1 +1,30 @@
 pub const OSC52 = @import("Clip/OSC52.zig");
+
+// I am not sure if this is super idiomatic Zig or if this is bad practice, but
+// by ensuring that all Clip backends implement the same methods, we are able
+// to use the `anytype` type and we are able to have an interface that is able
+// to dispatch statically
+
+/// Check if a function with the given name and type exists.
+/// `@compileError` if not
+fn assertFn(comptime T: type, comptime name: []const u8, comptime Sig: type) void {
+    if (!@import("std").meta.hasFn(T, name))
+        @compileError(@typeName(T) ++ " missing fn " ++ name);
+    if (@TypeOf(@field(T, name)) != Sig)
+        @compileError(@typeName(T) ++ "." ++ name ++ " must be " ++ @typeName(Sig));
+}
+
+test {
+    inline for (@typeInfo(@This()).@"struct".decls) |decl| {
+        // Only grab types of structs
+        const T = @field(@This(), decl.name);
+        if (@TypeOf(T) != type) continue;
+        if (@typeInfo(T) != .@"struct") continue;
+
+        // ==============================
+        // === Interface Requirements ===
+        // ==============================
+        assertFn(T, "writeCopyBuffer", fn (*T, []const u8) anyerror!void);
+        assertFn(T, "writePasteboard", fn (*T) anyerror!void);
+    }
+}