AleksandrShadrin/di.zig
A simple and lightweight dependency injection container for Zig.
A simple and lightweight dependency injection (DI) container for Zig. Manage your dependencies effortlessly and keep your code clean!
π¦ Features
Add the di module to your project using zig zon.
const di_dep = b.dependency("di", .{ .target = target, .optimize = optimize });
const di_module = di_dep.module("di");
const exe = b.addExecutable(.{...});
exe.root_module.addImport("di", di_module);
Initialize the Container. Start by setting up the DI container with an allocator.
const std = @import("std");
const di = @import("di");
const Container = di.Container;
pub fn main() !void {
const allocator = std.heap.page_allocator;
var container = Container.init(allocator);
defer container.deinit();
// Register your services here
}
Choose how you want your services to behave.
One shared instance.
try container.registerSingleton(MyService);
A new instance each time.
try container.registerTransient(MyService);
Managed within a specific scope.
try container.registerScoped(MyService);
You can also register factories to create instances of your services. This allows you to add multiple implementations
try container.registerSingletonFactory(builderFn);
After registering services, create a provider to resolve them.
var serviceProvider = try container.createServiceProvider();
defer serviceProvider.deinit();
Get instances of your services when needed.
const myService = try serviceProvider.resolve(MyService);
const myServices = try serviceProvider.resolveSlice(MyService); // get all registered services of type MyService, MyService if it was registered wihtout factory + all instances created by factories
Handle generic types with ease.
// register
try container.registerSingleton(MyService);
...
// resolve
const genericService = try serviceProvider.resolve(di.Generic(MyService, .{u8}));
Manage scoped services within a controlled environment.
var scope = try serviceProvider.initScope();
defer scope.deinit();
const scopedService = try scope.resolve(MyService);
Provide custom allocator if you don't want to use parent's
var allocator = ...;
var scope = try serviceProvider.initScopeWithAllocator(allocator);
...
Manually release a service if needed.
try serviceProvider.unresolve(resolvedService);
try serviceProvider.unresolve(resolvedServices); // resolved by resolveSlice
Here's a quick example to get you started!
const std = @import("std");
const di = @import("di");
const Container = di.Container;
// Example Services
const Logger = struct {
pub fn init() Logger {
return Logger{};
}
pub fn log(self: *Logger, message: []const u8) void {
_ = self;
std.log.info("{s}", .{message});
}
};
const Database = struct {
logger: *Logger,
pub fn init(logger: *Logger) Database {
return Database{
.logger = logger,
};
}
pub fn persist(self: *Database) void {
self.logger.log("Log some job");
}
};
pub fn main() !void {
const allocator = std.heap.page_allocator;
var container = Container.init(allocator);
defer container.deinit();
// Register services
try container.registerSingleton(Logger);
try container.registerTransient(Database);
// Create provider
var provider = try container.createServiceProvider();
defer provider.deinit();
// Resolve services
var db = try provider.resolve(Database);
// Use services
db.persist();
}
This example sets up a simple DI container, registers a Logger as a singleton and Database as a transient service, then resolves and uses them.
More examples can be founded in examples folder, use:
zig build run-${filename}
This project is MIT licensed. See the LICENSE file for details.