# Comptime Interface testing

**URL:** <https://ziggit.dev/t/comptime-interface-testing/3958>\
**Category:** Challenge\
**Created:** [April 14, 2024, 10:06pm UTC](https://ziggit.dev/t/comptime-interface-testing/3958 "2024-04-14T22:06:31Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![nyc](https://ziggit.dev/user_avatar/ziggit.dev/nyc/32/1167_2.png) [@nyc](https://ziggit.dev/u/nyc)\
**Post date:** [April 14, 2024, 10:06pm UTC](https://ziggit.dev/t/comptime-interface-testing/3958/1 "2024-04-14T22:06:31Z")

</div>

Here is a start of comptime code that will test a struct to see if it contains all the fields and delcs of another struct. I ideally you also want to extend this for unions, error sets, and all other types. Function parameters might also have interfaces in them too that need to be recursively checked. This works for basic types and is a basic start:

```zig
const Iface = struct {
    b: u32,
    fn funca(_: u8, _: u32) bool {
        return true;
    }
    fn funcb(_: []u8) u32 {
        return 0;
    }
};

const Class = struct {
    a: u32,
    b: u32,

    fn funca(_: u8, _: u32) bool {
        return true;
    }
    fn funcc() void {}
    fn funcb(_: []u8) u32 {
        return 0;
    }
};

fn get_field(comptime class: type, comptime name: []const u8) ?std.builtin.Type.StructField {
    const ti = @typeInfo(class).Struct;
    inline for (ti.fields) |f| {
        if (std.mem.eql(u8, name, f.name)) {
            return f;
        }
    }
    return null;
}

fn has_interface(comptime iface: type, comptime class: type) bool {
    const iti = @typeInfo(iface).Struct;
    inline for (iti.decls) |i| {
        const x = @field(iface, i);
        const y = @field(class, i);
        if (!std.meta.eql(@TypeOf(x), @TypeOf(y)))
            return false;
    }
    inline for (iti.fields) |i| {
        const fi = get_field(class, i.name);
        if (fi == null or !std.meta.eql(i.type, fi.?.type))
            return false;
    }
    return true;
}

test "same" {
    std.debug.print("\n{}\n", .{has_interface(Iface, Class)});
}

```

You could even add a see also to documentation to point to the interface type. (I still want interfaces in the language, but this is kind of neat)

---

<div class="post-metadata">

**Author:** ![DracoTheDrake](https://ziggit.dev/user_avatar/ziggit.dev/dracothedrake/32/2856_2.png) [@DracoTheDrake](https://ziggit.dev/u/DracoTheDrake)\
**Post date:** [August 12, 2024, 1:42am UTC](https://ziggit.dev/t/comptime-interface-testing/3958/2 "2024-08-12T01:42:43Z")

</div>

I don’t know if this answers the challenge…

```zig
const std = @import("std");

pub fn enforceInterface(comptime iface: type, comptime T: type) type {
    const iface_info = @typeInfo(iface);

    if (iface_info != .Struct or @typeInfo(T) != .Struct) {
        @compileError("enforceInterface accepts only structs as arguments");
    }

    inline for (iface_info.Struct.decls) |iface_decl| {
        const iface_decl_type = @TypeOf(@field(iface, iface_decl.name));

        if (!@hasDecl(T, iface_decl.name)) {
            @compileError(std.fmt.comptimePrint("missing public declaration '{s}.{s}' of type '{s}'", .{ @typeName(T), iface_decl.name, @typeName(iface_decl_type) }));
        }

        const T_decl_type = @TypeOf(@field(T, iface_decl.name));
        const iface_decl_info = @typeInfo(iface_decl_type);
        const T_decl_info = @typeInfo(T_decl_type);

        if (iface_decl_info == .Fn and T_decl_info == .Fn) {
            const iface_return_type = @typeInfo(iface_decl_type).Fn.return_type;
            const T_return_type = @typeInfo(T_decl_type).Fn.return_type;

            if (iface_return_type != T_return_type and iface_return_type != null) {
                const iface_rt_info = @typeInfo(iface_return_type.?);
                const T_rt_info = @typeInfo(T_return_type.?);
                if (iface_rt_info == .ErrorUnion and T_rt_info == .ErrorUnion) {
                    if (iface_rt_info.ErrorUnion.payload == T_rt_info.ErrorUnion.payload) {
                        continue;
                    }
                }
            }
        }

        if (iface_decl_type != T_decl_type) {
            @compileError(std.fmt.comptimePrint("expected type '{s}', found '{s}' for declaration of '{s}.{s}'", .{ @typeName(iface_decl_type), @typeName(T_decl_type), @typeName(T), iface_decl.name }));
        }
    }
    return T;
}

```

Usage:

```zig
const Iface = @import("iface.zig");
const TestStruct = enforceInterface(Iface, @import("teststruct.zig"));

```
