Documentation ¶
Overview ¶
Package module provides a way to do dependency injection, with type-safe, without performance penalty. See examples for the basic usage.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoPrivoder = fmt.Errorf("can't find module")
ErrNoPrivoder means that it can't find a module within a given context. Usually it misses adding that module to a repo.
Functions ¶
This section is empty.
Types ¶
type Module ¶
type Module[T any] struct { // contains filtered or unexported fields }
Module provides a module to inject and retreive an instance with its type.
Example ¶
package main import ( "context" "fmt" "github.com/googollee/go-espresso/module" ) type DB interface { Target() string } type db struct { target string } func NewDB(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil } func (db *db) Target() string { return db.target } var ( ModuleDB = module.New[DB]() ProvideDB = ModuleDB.ProvideWithFunc(NewDB) ) type Cache struct { fallback DB } func NewCache(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, }, nil } var ( ModuleCache = module.New[*Cache]() ProvideCache = ModuleCache.ProvideWithFunc(NewCache) ) func main() { ctx := context.Background() repo := module.NewRepo() repo.Add(ProvideCache) repo.Add(ProvideDB) ctx, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } db := ModuleDB.Value(ctx) cache := ModuleCache.Value(ctx) fmt.Println("db target:", db.Target()) fmt.Println("cache fallback target:", cache.fallback.Target()) }
Output: db target: localhost.db cache fallback target: localhost.db
Example (CreateWithError) ¶
package main import ( "context" "fmt" "github.com/googollee/go-espresso/module" ) type DB interface { Target() string } type db struct { target string } func NewDB(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB } func NewCache(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, }, nil } var ModuleCache = module.New[*Cache]() func main() { ctx := context.Background() repo := module.NewRepo() repo.Add(ModuleDB.ProvideWithFunc(func(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil })) repo.Add(ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { _ = ModuleDB.Value(ctx) return nil, fmt.Errorf("new cache error") })) _, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } }
Output: inject error: creating with module *module_test.Cache: new cache error
Example (CreateWithPanic) ¶
package main import ( "context" "fmt" "github.com/googollee/go-espresso/module" ) type DB interface { Target() string } type db struct { target string } func NewDB(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB } func NewCache(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, }, nil } var ModuleCache = module.New[*Cache]() func main() { ctx := context.Background() repo := module.NewRepo() repo.Add(ModuleDB.ProvideWithFunc(func(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil })) repo.Add(ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { _ = ModuleDB.Value(ctx) panic(fmt.Errorf("new cache error")) })) defer func() { err := recover() fmt.Println("panic:", err) }() _, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } }
Output: panic: new cache error
Example (DuplicatingProviders) ¶
package main import ( "context" "fmt" "regexp" "github.com/googollee/go-espresso/module" ) type FileSystem interface { Read() Write() } type realFileSystem struct{} func NewRealFileSystem(context.Context) (FileSystem, error) { return &realFileSystem{}, nil } func (f *realFileSystem) Read() {} func (f *realFileSystem) Write() {} type mockFileSystem struct{} func NewMockFileSystem(context.Context) (FileSystem, error) { return &mockFileSystem{}, nil } var ( ModuleFileSystem = module.New[FileSystem]() ProvideRealFileSystem = ModuleFileSystem.ProvideWithFunc(NewRealFileSystem) ProvideMockFileSystem = ModuleFileSystem.ProvideWithFunc(NewMockFileSystem) ) func (f *mockFileSystem) Read() {} func (f *mockFileSystem) Write() {} func main() { defer func() { p := recover().(string) fmt.Println("panic:", regexp.MustCompile(`at .*`).ReplaceAllString(p, "at <removed file and line>")) }() repo := module.NewRepo() repo.Add(ProvideRealFileSystem) repo.Add(ProvideMockFileSystem) }
Output: panic: already have a provider with type "module_test.FileSystem", added at <removed file and line>
Example (NotExistingProvider) ¶
package main import ( "context" "fmt" "github.com/googollee/go-espresso/module" ) type DB interface { Target() string } type db struct { target string } func NewDB(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB } func NewCache(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, }, nil } var ( ModuleCache = module.New[*Cache]() ProvideCache = ModuleCache.ProvideWithFunc(NewCache) ) func main() { ctx := context.Background() repo := module.NewRepo() repo.Add(ProvideCache) // repo.Add(ProvideDB) _, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } }
Output: inject error: creating with module module_test.DB: can't find module
Example (WithOtherValue) ¶
package main import ( "context" "fmt" "github.com/googollee/go-espresso/module" ) type DB interface { Target() string } type db struct { target string } func NewDB(ctx context.Context) (DB, error) { return &db{ target: "localhost.db", }, nil } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB } func NewCache(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, }, nil } var ModuleCache = module.New[*Cache]() func main() { targetKey := "target" ctx := context.WithValue(context.Background(), targetKey, "target.db") repo := module.NewRepo() repo.Add(ModuleDB.ProvideWithFunc(func(ctx context.Context) (DB, error) { target := ctx.Value(targetKey).(string) return &db{ target: target, }, nil })) repo.Add(ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, }, nil })) ctx, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } db := ModuleDB.Value(ctx) cache := ModuleCache.Value(ctx) fmt.Println("db target:", db.Target()) fmt.Println("cache fallback target:", cache.fallback.Target()) }
Output: db target: target.db cache fallback target: target.db
func (*Module[T]) ProvideWithFunc ¶ added in v0.6.0
ProvideWithFunc returns a provider which provides instances creating from `ctor` function.
type Provider ¶ added in v0.6.0
type Provider interface {
// contains filtered or unexported methods
}
Provider is the interface to provide an Instance.
type Repo ¶
type Repo struct {
// contains filtered or unexported fields
}
Repo is a repository of modules, and to inject instances creating by modules into a context.
func (*Repo) InjectTo ¶ added in v0.6.0
InjectTo injects instances created by modules into a context `ctx`. It returns a new context with all injections. If any module creates an instance with an error, `InjectTo` returns that error with the module name. Injecting instances only create once if necessary. Calling `InjectTo` mutlple times share instances between returning contexts. InjectTo ignores all new providers adding to the Repo after the first run. So adding all providers before calling `InjectTo`.