diff --git a/src/c.zig b/src/c.zig index 04d6023..46975dc 100644 --- a/src/c.zig +++ b/src/c.zig @@ -3,13 +3,22 @@ pub const c = @cImport({ @cInclude("objc/message.h"); }); -/// This is a funky helper to help with the fact that some macOS -/// SDKs have an i8 return value for bools and some have stdbool. -pub fn boolResult(comptime Fn: type, result: anytype) bool { - const fn_info = @typeInfo(Fn).@"fn"; - return switch (fn_info.return_type.?) { +/// On some targets, Objective-C uses `i8` instead of `bool`. +/// This helper casts a target value type to `bool`. +pub fn boolResult(result: c.BOOL) bool { + return switch (c.BOOL) { bool => result, i8 => result == 1, - else => @compileError("unhandled class_addIvar return type"), + else => @compileError("unexpected boolean type"), + }; +} + +/// On some targets, Objective-C uses `i8` instead of `bool`. +/// This helper casts a `bool` value to the target value type. +pub fn boolParam(param: bool) c.BOOL { + return switch (c.BOOL) { + bool => param, + i8 => @intFromBool(param), + else => @compileError("unexpected boolean type"), }; } diff --git a/src/class.zig b/src/class.zig index 4691698..bf92b7c 100644 --- a/src/class.zig +++ b/src/class.zig @@ -38,7 +38,7 @@ pub const Class = struct { } pub fn isMetaClass(self: Class) bool { - return c.class_isMetaClass(self.value) == 1; + return boolResult(c.class_isMetaClass(self.value)); } pub fn getInstanceSize(self: Class) usize { @@ -46,11 +46,11 @@ pub const Class = struct { } pub fn respondsToSelector(self: Class, sel: objc.Sel) bool { - return c.class_respondsToSelector(self.value, sel.value) == 1; + return boolResult(c.class_respondsToSelector(self.value, sel.value)); } pub fn conformsToProtocol(self: Class, protocol: objc.Protocol) bool { - return c.class_conformsToProtocol(self.value, &protocol.value) == 1; + return boolResult(c.class_conformsToProtocol(self.value, &protocol.value)); } // currently only allows for overriding methods previously defined, e.g. by a superclass. @@ -78,7 +78,7 @@ pub const Class = struct { assert(fn_info.params[0].type == c.id); assert(fn_info.params[1].type == c.SEL); const encoding = comptime objc.comptimeEncode(Fn); - return boolResult(@TypeOf(c.class_addMethod), c.class_addMethod( + return boolResult(c.class_addMethod( self.value, objc.sel(name).value, @ptrCast(&imp), @@ -91,7 +91,7 @@ pub const Class = struct { pub fn addIvar(self: Class, name: [:0]const u8) bool { // The return type is i8 when we're cross compiling, unsure why. const result = c.class_addIvar(self.value, name, @sizeOf(c.id), @alignOf(c.id), "@"); - return boolResult(@TypeOf(c.class_addIvar), result); + return boolResult(result); } }; diff --git a/src/object.zig b/src/object.zig index d80b611..73fd485 100644 --- a/src/object.zig +++ b/src/object.zig @@ -1,5 +1,7 @@ const std = @import("std"); -const c = @import("c.zig").c; +const cpkg = @import("c.zig"); +const c = cpkg.c; +const boolResult = cpkg.boolResult; const objc = @import("main.zig"); const MsgSend = @import("msg_send.zig").MsgSend; const Iterator = @import("iterator.zig").Iterator; @@ -102,7 +104,7 @@ pub const Object = struct { } pub fn isClass(self: Object) bool { - return c.object_isClass(self.value) == 1; + return boolResult(c.object_isClass(self.value)); } pub fn getInstanceVariable(self: Object, name: [:0]const u8) Object { diff --git a/src/protocol.zig b/src/protocol.zig index e72e170..24b64b0 100644 --- a/src/protocol.zig +++ b/src/protocol.zig @@ -1,16 +1,19 @@ const std = @import("std"); -const c = @import("c.zig").c; +const cpkg = @import("c.zig"); +const c = cpkg.c; +const boolParam = cpkg.boolParam; +const boolResult = cpkg.boolResult; const objc = @import("main.zig"); pub const Protocol = extern struct { value: *c.Protocol, pub fn conformsToProtocol(self: Protocol, other: Protocol) bool { - return c.protocol_conformsToProtocol(self.value, other.value) == 1; + return boolResult(c.protocol_conformsToProtocol(self.value, other.value)); } pub fn isEqual(self: Protocol, other: Protocol) bool { - return c.protocol_isEqual(self.value, other.value) == 1; + return boolResult(c.protocol_isEqual(self.value, other.value)); } pub fn getName(self: Protocol) [:0]const u8 { @@ -23,13 +26,11 @@ pub const Protocol = extern struct { is_required: bool, is_instance: bool, ) ?objc.Property { - const isRequired: u8 = if (is_required) 1 else 0; - const isInstance: u8 = if (is_instance) 1 else 0; return .{ .value = c.protocol_getProperty( self.value, name, - isRequired, - isInstance, + boolParam(is_required), + boolParam(is_instance), ) orelse return null }; } @@ -42,3 +43,18 @@ pub const Protocol = extern struct { pub fn getProtocol(name: [:0]const u8) ?Protocol { return .{ .value = c.objc_getProtocol(name) orelse return null }; } + +test Protocol { + const testing = std.testing; + const fs_proto = getProtocol("NSFileManagerDelegate") orelse return error.ProtocolNotFound; + try testing.expectEqualStrings("NSFileManagerDelegate", fs_proto.getName()); + + const obj_proto = getProtocol("NSObject") orelse return error.ProtocolNotFound; + try testing.expect(fs_proto.conformsToProtocol(obj_proto)); + + const url_proto = getProtocol("NSURLSessionDelegate") orelse return error.ProtocolNotFound; + try testing.expect(!fs_proto.conformsToProtocol(url_proto)); + + const hash_prop = obj_proto.getProperty("hash", true, true) orelse return error.ProtocolPropertyNotFound; + try testing.expectEqualStrings("hash", hash_prop.getName()); +}