aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig13
-rw-r--r--src/Clip.zig29
2 files changed, 42 insertions, 0 deletions
diff --git a/build.zig b/build.zig
index 4e2fbe0..242a70c 100644
--- a/build.zig
+++ b/build.zig
@@ -14,6 +14,19 @@ pub fn build(b: *std.Build) void {
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);
diff --git a/src/Clip.zig b/src/Clip.zig
index 7d2f6db..8d264c8 100644
--- 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);
+ }
+}