di API

di

package

API reference for the di package.

S
struct

Container

Container manages dependency injection with thread-safe access.

pkg/di/container.go:15-18
type Container struct

Example

c := di.New()
c.Provide("db", &Database{})
db := di.Get[*Database](c, "db")

Methods

Provide
Method

Provide registers a dependency by name.

Parameters

name string
instance any
func (*Container) Provide(name string, instance any)
{
	c.mu.Lock()
	defer c.mu.Unlock()
	c.providers[name] = instance
}
Get
Method

Get retrieves a dependency by name.

Parameters

name string

Returns

any
bool
func (*Container) Get(name string) (any, bool)
{
	c.mu.RLock()
	defer c.mu.RUnlock()
	v, ok := c.providers[name]
	return v, ok
}
MustGet
Method

MustGet retrieves a dependency and panics if not found.

Parameters

name string

Returns

any
func (*Container) MustGet(name string) any
{
	v, ok := c.Get(name)
	if !ok {
		panic("di: dependency not found: " + name)
	}
	return v
}
Has
Method

Has checks if a dependency is registered.

Parameters

name string

Returns

bool
func (*Container) Has(name string) bool
{
	c.mu.RLock()
	defer c.mu.RUnlock()
	_, ok := c.providers[name]
	return ok
}
Inject
Method

Inject populates struct fields with registered dependencies. Uses `inject:"name"` tags to match fields with providers. Falls back to field name if no inject tag is present.

Parameters

target any
func (*Container) Inject(target any)
{
	val := reflect.ValueOf(target)
	if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
		return
	}

	elem := val.Elem()
	elemType := elem.Type()

	c.mu.RLock()
	defer c.mu.RUnlock()

	for i := 0; i < elem.NumField(); i++ {
		fieldType := elemType.Field(i)
		fieldVal := elem.Field(i)

		if !fieldVal.CanSet() {
			continue
		}

		name := fieldType.Tag.Get("inject")
		if name == "" {
			name = fieldType.Name
		}

		if dep, ok := c.providers[name]; ok {
			depVal := reflect.ValueOf(dep)
			if depVal.Type().AssignableTo(fieldType.Type) {
				fieldVal.Set(depVal)
			}
		}
	}
}
Clone
Method

Clone creates a shallow copy of the container.

Returns

func (*Container) Clone() *Container
{
	c.mu.RLock()
	defer c.mu.RUnlock()

	clone := New()
	for k, v := range c.providers {
		clone.providers[k] = v
	}
	return clone
}
Keys
Method

Keys returns all registered dependency names.

Returns

[]string
func (*Container) Keys() []string
{
	c.mu.RLock()
	defer c.mu.RUnlock()

	keys := make([]string, 0, len(c.providers))
	for k := range c.providers {
		keys = append(keys, k)
	}
	return keys
}

Fields

Name Type Description
providers map[string]any
mu sync.RWMutex
F
function

New

New creates an empty DI container.

Returns

pkg/di/container.go:21-25
func New() *Container

{
	return &Container{
		providers: make(map[string]any),
	}
}
S
struct

testDB

pkg/di/container_test.go:7-9
type testDB struct

Fields

Name Type Description
Name string
S
struct

testService

pkg/di/container_test.go:11-14
type testService struct

Fields

Name Type Description
DB *testDB inject:"db"
Logger string inject:"logger"
F
function

TestContainer_ProvideAndGet

Parameters

pkg/di/container_test.go:16-34
func TestContainer_ProvideAndGet(t *testing.T)

{
	c := New()
	db := &testDB{Name: "test"}
	c.Provide("db", db)

	got, ok := c.Get("db")
	if !ok {
		t.Fatal("expected to find 'db'")
	}

	gotDB, ok := got.(*testDB)
	if !ok {
		t.Fatal("expected *testDB type")
	}

	if gotDB.Name != "test" {
		t.Errorf("got %q, want %q", gotDB.Name, "test")
	}
}
F
function

TestContainer_Has

Parameters

pkg/di/container_test.go:36-47
func TestContainer_Has(t *testing.T)

{
	c := New()
	c.Provide("exists", "value")

	if !c.Has("exists") {
		t.Error("Has should return true for 'exists'")
	}

	if c.Has("missing") {
		t.Error("Has should return false for 'missing'")
	}
}
F
function

TestContainer_Inject

Parameters

pkg/di/container_test.go:49-65
func TestContainer_Inject(t *testing.T)

{
	c := New()
	db := &testDB{Name: "injected"}
	c.Provide("db", db)
	c.Provide("logger", "stdout")

	svc := &testService{}
	c.Inject(svc)

	if svc.DB != db {
		t.Errorf("DB not injected correctly")
	}

	if svc.Logger != "stdout" {
		t.Errorf("Logger: got %q, want %q", svc.Logger, "stdout")
	}
}
F
function

TestContainer_InjectFallbackToFieldName

Parameters

pkg/di/container_test.go:67-82
func TestContainer_InjectFallbackToFieldName(t *testing.T)

{
	type noTagService struct {
		DB *testDB
	}

	c := New()
	db := &testDB{Name: "fallback"}
	c.Provide("DB", db)

	svc := &noTagService{}
	c.Inject(svc)

	if svc.DB != db {
		t.Error("DB not injected via field name fallback")
	}
}
F
function

TestContainer_Clone

Parameters

pkg/di/container_test.go:84-102
func TestContainer_Clone(t *testing.T)

{
	c := New()
	c.Provide("key", "value")

	clone := c.Clone()
	clone.Provide("new", "added")

	if !clone.Has("key") {
		t.Error("clone should have 'key'")
	}

	if !clone.Has("new") {
		t.Error("clone should have 'new'")
	}

	if c.Has("new") {
		t.Error("original should not have 'new'")
	}
}
F
function

TestContainer_Keys

Parameters

pkg/di/container_test.go:104-113
func TestContainer_Keys(t *testing.T)

{
	c := New()
	c.Provide("a", 1)
	c.Provide("b", 2)

	keys := c.Keys()
	if len(keys) != 2 {
		t.Errorf("got %d keys, want 2", len(keys))
	}
}
F
function

TestContainer_MustGet_Panic

Parameters

pkg/di/container_test.go:115-125
func TestContainer_MustGet_Panic(t *testing.T)

{
	c := New()

	defer func() {
		if r := recover(); r == nil {
			t.Error("MustGet should panic for missing key")
		}
	}()

	c.MustGet("missing")
}
F
function

TestResolve

Parameters

pkg/di/container_test.go:127-138
func TestResolve(t *testing.T)

{
	c := New()
	c.Provide("num", 42)

	got, ok := Resolve[int](c, "num")
	if !ok {
		t.Fatal("expected to resolve 'num'")
	}
	if got != 42 {
		t.Errorf("got %d, want 42", got)
	}
}
F
function

TestResolve_TypeMismatch

Parameters

pkg/di/container_test.go:140-148
func TestResolve_TypeMismatch(t *testing.T)

{
	c := New()
	c.Provide("num", 42)

	_, ok := Resolve[string](c, "num")
	if ok {
		t.Error("should return false for type mismatch")
	}
}
F
function

TestMustResolve_Panic

Parameters

pkg/di/container_test.go:150-160
func TestMustResolve_Panic(t *testing.T)

{
	c := New()

	defer func() {
		if r := recover(); r == nil {
			t.Error("MustResolve should panic for missing key")
		}
	}()

	MustResolve[int](c, "missing")
}
F
function

Resolve

Resolve retrieves a typed dependency from the container.
Returns zero value and false if not found or type mismatch.

Parameters

name
string

Returns

T
bool
pkg/di/resolve.go:5-16
func Resolve[T any](c *Container, name string) (T, bool)

{
	var zero T
	v, ok := c.Get(name)
	if !ok {
		return zero, false
	}
	typed, ok := v.(T)
	if !ok {
		return zero, false
	}
	return typed, true
}
F
function

MustResolve

MustResolve retrieves a typed dependency and panics if not found.

Parameters

name
string

Returns

T
pkg/di/resolve.go:19-25
func MustResolve[T any](c *Container, name string) T

{
	v, ok := Resolve[T](c, name)
	if !ok {
		panic("di: cannot resolve " + name)
	}
	return v
}