module

package
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2024 License: MIT Imports: 5 Imported by: 0

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

View Source
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 BuildFunc

type BuildFunc[T any] func(context.Context) (T, error)

BuildFunc is the constructor of an Instance.

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 New

func New[T any]() Module[T]

New creates a new module with type `T` and the constructor `builder`.

func (*Module[T]) ProvideWithFunc added in v0.6.0

func (m *Module[T]) ProvideWithFunc(ctor BuildFunc[T]) Provider

ProvideWithFunc returns a provider which provides instances creating from `ctor` function.

func (Module[T]) Value

func (m Module[T]) Value(ctx context.Context) T

Value returns an instance of T which is injected to the context.

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 NewRepo

func NewRepo() *Repo

NewRepo creates a Repo instance.

func (*Repo) Add

func (r *Repo) Add(provider Provider)

Add adds a provider to the repo.

func (*Repo) InjectTo added in v0.6.0

func (r *Repo) InjectTo(ctx context.Context) (context.Context, error)

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`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL