https://github.com/nilslice/zig-interface
A compile-time interface checker for Zig that enables interface-based design with comprehensive type checking and detailed error reporting.
https://github.com/nilslice/zig-interface
zig-package
Last synced: 9 months ago
JSON representation
A compile-time interface checker for Zig that enables interface-based design with comprehensive type checking and detailed error reporting.
- Host: GitHub
- URL: https://github.com/nilslice/zig-interface
- Owner: nilslice
- License: mit
- Created: 2024-11-12T06:51:36.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-03-20T08:55:58.000Z (10 months ago)
- Last Synced: 2025-03-30T09:08:58.031Z (10 months ago)
- Topics: zig-package
- Language: Zig
- Homepage:
- Size: 16.6 KB
- Stars: 112
- Watchers: 3
- Forks: 2
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Zig Interfaces & Validation
A compile-time interface checker for Zig that enables interface-based design
with comprehensive type checking and detailed error reporting.
## Features
This library provides a way to define and verify interfaces in Zig at compile
time. It supports:
- Type-safe interface definitions with detailed error reporting
- Interface embedding (composition)
- Complex type validation including structs, enums, arrays, and slices
- Comprehensive compile-time error messages with helpful hints
- Flexible error union compatibility with `anyerror`
## Install
Add or update this library as a dependency in your zig project run the following command:
```sh
zig fetch --save git+https://github.com/nilslice/zig-interface
```
Afterwards add the library as a dependency to any module in your _build.zig_:
```zig
// ...
const interface_dependency = b.dependency("interface", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "main",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// import the exposed `interface` module from the dependency
exe.root_module.addImport("interface", interface_dependency.module("interface"));
// ...
```
In the end you can import the `interface` module. For example:
```zig
const Interface = @import("interface").Interface;
const Repository = Interface(.{
.create = fn(anytype, User) anyerror!u32,
.findById = fn(anytype, u32) anyerror!?User,
.update = fn(anytype, User) anyerror!void,
.delete = fn(anytype, u32) anyerror!void,
}, null);
```
## Usage
1. Define an interface with required method signatures:
```zig
const Repository = Interface(.{
.create = fn(anytype, User) anyerror!u32,
.findById = fn(anytype, u32) anyerror!?User,
.update = fn(anytype, User) anyerror!void,
.delete = fn(anytype, u32) anyerror!void,
}, null);
```
2. Implement the interface methods in your type:
```zig
const InMemoryRepository = struct {
allocator: std.mem.Allocator,
users: std.AutoHashMap(u32, User),
next_id: u32,
pub fn create(self: *InMemoryRepository, user: User) !u32 {
var new_user = user;
new_user.id = self.next_id;
try self.users.put(self.next_id, new_user);
self.next_id += 1;
return new_user.id;
}
// ... other Repository methods
};
```
3. Verify the implementation at compile time:
```zig
// In functions that accept interface implementations:
fn createUser(repo: anytype, name: []const u8, email: []const u8) !User {
comptime Repository.satisfiedBy(@TypeOf(repo));
// ... rest of implementation
}
// Or verify directly:
comptime Repository.satisfiedBy(InMemoryRepository);
```
## Interface Embedding
Interfaces can embed other interfaces to combine their requirements:
```zig
const Logger = Interface(.{
.log = fn(anytype, []const u8) void,
.getLogLevel = fn(anytype) u8,
}, null);
const Metrics = Interface(.{
.increment = fn(anytype, []const u8) void,
.getValue = fn(anytype, []const u8) u64,
}, .{ Logger }); // Embeds Logger interface
// Now implements both Metrics and Logger methods
const MonitoredRepository = Interface(.{
.create = fn(anytype, User) anyerror!u32,
.findById = fn(anytype, u32) anyerror!?User,
}, .{ Metrics });
```
> Note: you can embed arbitrarily many interfaces!
## Error Reporting
The library provides detailed compile-time errors when implementations don't
match:
```zig
// Wrong parameter type ([]u8 vs []const u8)
const BadImpl = struct {
pub fn writeAll(self: @This(), data: []u8) !void {
_ = self;
_ = data;
}
};
// Results in compile error:
// error: Method 'writeAll' parameter 1 has incorrect type:
// └─ Expected: []const u8
// └─ Got: []u8
// └─ Hint: Consider making the parameter type const
```
## Complex Types
The interface checker supports complex types including:
```zig
const ComplexTypes = Interface(.{
.process = fn(
anytype,
struct { config: Config, points: []const DataPoint },
enum { ready, processing, error },
[]const struct {
timestamp: i64,
data: ?[]const DataPoint,
status: Status,
}
) anyerror!?ProcessingResult,
}, null);
```