Documentation ¶
Overview ¶
Package axon is a simple and generic-friendly DI library.
Example ¶
A full-scale example of injecting values into a struct
package main import ( "fmt" "github.com/eddieowens/axon" "os" ) // A full-scale example of injecting values into a struct func main() { axon.Add(axon.NewKey("secret"), os.Getenv("LOGIN_SECRET")) axon.Add(axon.NewTypeKey[LoginServiceClient](new(loginServiceClient))) axon.Add(axon.NewTypeKey[UserService](new(userService))) axon.Add(axon.NewTypeKeyFactory[DBClient](axon.NewFactory[DBClient](func(_ axon.Injector) (DBClient, error) { // inject username, DB name, password, etc. return &dbClient{}, nil }))) api := new(ApiGateway) _ = axon.Inject(api) api.UserService.DeleteUser("user") } type DBClient interface { DeleteUser(username string) } type dbClient struct { } func (d *dbClient) DeleteUser(username string) { fmt.Println("Deleting", username, "from DB!") } type LoginServiceClient interface { Logout(username string) } type loginServiceClient struct { // Injects the secret via a key called "secret" LoginSecret string `inject:"secret"` } func (l *loginServiceClient) Logout(username string) { fmt.Println("Logout for", username) } type UserService interface { DeleteUser(username string) } type userService struct { // Injects the default LoginServiceClient implementation. LoginServiceClient LoginServiceClient `inject:",type"` DBClient DBClient `inject:",type"` } func (u *userService) DeleteUser(username string) { u.DBClient.DeleteUser(username) u.LoginServiceClient.Logout(username) fmt.Println("Successfully deleted user") } type ApiGateway struct { UserService UserService `inject:",type"` }
Output: Deleting user from DB! Logout for user Successfully deleted user
Index ¶
- Variables
- func Add[K InjectableKey](key K, val any, opts ...opts.Opt[AddOpts])
- func Get[V any](opts ...opts.Opt[GetOpts]) (V, error)
- func Inject[V any](val V, opt ...opts.Opt[InjectorInjectOpts]) error
- func InjectAdd[K InjectableKey](inj Injector, key K, val any, _ ...opts.Opt[AddOpts])
- func InjectorGet[V any](inj Injector, ops ...opts.Opt[GetOpts]) (out V, err error)
- func MustGet[V any](opts ...opts.Opt[GetOpts]) V
- func NewTypeKeyFactory[V any](val Factory) (Key, Factory)
- func NewTypeKeyProvider[V any](val V) (Key, *Provider[V])
- func WithKey[V InjectableKey](k V) opts.Opt[GetOpts]
- func WithSkipFieldErrs() opts.Opt[InjectorInjectOpts]
- type AddOpts
- type Factory
- type FactoryFunc
- type GetOpts
- type InjectableKey
- type Injector
- type InjectorAddOpts
- type InjectorGetOpts
- type InjectorInjectOpts
- type Key
- type KeyConstraint
- type MutableValue
- type OnConstructFunc
- type Provider
- type Resolver
- type StorageGetter
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // InjectTag is the tag to look up within a struct for injection. InjectTag = "inject" // InjectTagValueType instructs the Injector to use the type as a Key rather than the name. If a name is specified, // the name takes precedence. InjectTagValueType = "type" )
var ( // ErrPtrToStruct if Inject is not passed a ptr to struct. ErrPtrToStruct = errors.New("value must be a ptr to a struct") // ErrNotFound key is not found in the Injector. ErrNotFound = errors.New("not found") // ErrInvalidType the type in the Injector is not the same as the type that is being injected. ErrInvalidType = errors.New("invalid type") // ErrInvalidField the field is not settable. ErrInvalidField = errors.New("invalid field") )
var DefaultInjector = NewInjector()
DefaultInjector acts as a global-level Injector for all operations. If you want to create your own Injector, use NewInjector.
Functions ¶
func Add ¶ added in v0.7.0
func Add[K InjectableKey](key K, val any, opts ...opts.Opt[AddOpts])
Add same as InjectAdd but uses the DefaultInjector.
Example ¶
Simple example of adding values to the injector.
package main import ( "fmt" "github.com/eddieowens/axon" ) func main() { // Use a string key axon.Add("key", "val") val := axon.MustGet[string](axon.WithKey("key")) fmt.Println(val) // Use a type as a key axon.Add(axon.NewTypeKey(1)) i := axon.MustGet[int]() fmt.Println(i) }
Output: val 1
func Get ¶ added in v0.7.0
Get same as InjectorGet but uses the DefaultInjector.
Example ¶
Simple example of getting values from the injector.
package main import ( "fmt" "github.com/eddieowens/axon" ) func main() { axon.Add("key", "val") val := axon.MustGet[string](axon.WithKey("key")) fmt.Println(val) }
Output: val
func Inject ¶ added in v0.7.0
func Inject[V any](val V, opt ...opts.Opt[InjectorInjectOpts]) error
Inject injects all fields in val marked with the InjectTag using the DefaultInjector. If any errors are encountered when trying to inject values to val, an error is returned. val must be a ptr to a struct.
Example ¶
Simple example of injecting values into a struct
package main import ( "fmt" "github.com/eddieowens/axon" ) func main() { type ExampleStruct struct { Val string `inject:"val"` Int int `inject:",type"` } axon.Add("val", "val") axon.Add(axon.NewTypeKey[int](1)) // Only struct pointers allowed. out := new(ExampleStruct) _ = axon.Inject(out) fmt.Println(out.Val) fmt.Println(out.Int) }
Output: val 1
func InjectAdd ¶ added in v0.7.0
InjectAdd adds a value into the inj using a key. If InjectAdd is called on a pre-existing value, it is overwritten.
func InjectorGet ¶ added in v0.7.0
InjectorGet calls Injector.Get but also adds support for generics. This adds some convenience in type casting
Add(axon.NewTypeKey(1)) // anytime someone asks for the type "int" the value "1" is injected. myInt, err := InjectorGet[int]() fmt.Println(myInt) // prints "1"
func NewTypeKeyFactory ¶ added in v0.7.0
NewTypeKeyFactory rather than tying a particular type to a value like NewTypeKey, this func ties a type to a Factory.
func NewTypeKeyProvider ¶ added in v0.7.0
NewTypeKeyProvider rather than tying a particular type to a value like NewTypeKey, this func ties a type to a provider.
func WithKey ¶ added in v0.7.0
func WithKey[V InjectableKey](k V) opts.Opt[GetOpts]
WithKey specifies an InjectableKey to Get. If not specified, all Get funcs return the specified generic type.
Add("mykey", 1) mykey := MustGet[int](WithKey("mykey")) fmt.Println(mykey) // prints 1
func WithSkipFieldErrs ¶ added in v0.7.0
func WithSkipFieldErrs() opts.Opt[InjectorInjectOpts]
WithSkipFieldErrs allows for the Injector.Inject method to skip over field errors that are encountered when attempting to inject values onto a struct. The Injector.Inject method may still return an error, but they will not be due to problems encountered on individual fields.
Types ¶
type Factory ¶
Factory produces the specified type whenever the Injector is retrieving the value (e.g. during Injector.Get or Injector.Inject). This factory will only ever be called once to construct the value unless a downstream dependency changes. Any Injector.Get method calls within the Build method will be registered as dependencies of the resulting type.
func NewFactory ¶ added in v0.7.0
func NewFactory[T any](f FactoryFunc[T]) Factory
NewFactory creates a Factory.
Example ¶
type Service struct { DBClient DBClient `inject:",type"` } axon.Add(axon.NewTypeKeyFactory[DBClient](axon.NewFactory[DBClient](func(_ axon.Injector) (DBClient, error) { // construct the DB client. return &dbClient{}, nil }))) s := new(Service) _ = axon.Inject(s) s.DBClient.DeleteUser("user")
Output: Deleting user from DB!
type FactoryFunc ¶ added in v0.7.0
type GetOpts ¶ added in v0.7.0
type GetOpts struct { // A specific Key to get from the Injector. Key Key }
type InjectableKey ¶ added in v0.7.0
InjectableKey is a type constraint for the supported keys within the Injector.
type Injector ¶
type Injector interface { // Inject injects all fields on a struct that are tagged with the InjectTag from the Injector. d must be a pointer to // a struct and the fields that are tagged must be public. If the InjectTag is not present on the struct or if the // value is already set, it will not be injected. // // All errors should be checked with errors.Is as they may be wrapped. Inject(d any, opts ...opts.Opt[InjectorInjectOpts]) error // Add adds the val indexed by a Key. The underlying value for a Key should be a comparable value since the underlying // implementation utilizes a map. All calls to Add will overwrite existing values and no checks are done. Be aware that // if Add overwrites an existing value, all of that values dependencies will be reconstructed on the next call to Get or // Inject. // // If you want any updates made here to be reflected within the value themselves, use a provider. Add(key Key, val any, ops ...opts.Opt[InjectorAddOpts]) // Get gets a value given a Key. If Get is unable to find the Key, ErrNotFound is returned. The first call to Get will // cause the underlying value to be constructed if it is a Factory. Get(k Key, o ...opts.Opt[InjectorGetOpts]) (any, error) }
Injector allows for the storage, retrieval, and construction of objects.
type InjectorAddOpts ¶ added in v0.7.0
type InjectorAddOpts struct { }
InjectorAddOpts opts for the Injector.Add method.
type InjectorGetOpts ¶ added in v0.7.0
type InjectorGetOpts struct { }
InjectorGetOpts opts for the Injector.Get method.
type InjectorInjectOpts ¶ added in v0.7.0
type InjectorInjectOpts struct { // See WithSkipFieldErrs. SkipFieldErr bool }
InjectorInjectOpts opts for the Injector.Inject method.
type Key ¶ added in v0.7.0
type Key struct {
// contains filtered or unexported fields
}
Key the key type for the Injector.
func NewKey ¶ added in v0.7.0
func NewKey[V KeyConstraint](val V) Key
NewKey creates a general purpose Key with any KeyConstraint value. Generally used with a string
NewKey("my_key")
func NewTypeKey ¶ added in v0.7.0
NewTypeKey returns a Key using the type V as well as the passed in value val.
type KeyConstraint ¶ added in v0.7.0
type KeyConstraint interface { string }
type MutableValue ¶ added in v0.7.0
type MutableValue interface { // SetValue is called when the Injector is looking to inject a value to the implementor. val may not be the same expected // type or could also be nil. All implementations should do type checks as well as nil checks. // // Because this causes a mutation of the implementor, this method should always be implemented via a pointer receiver. SetValue(val any) error }
MutableValue allows for any implementation to control how a value is being set within the Injector.
Normally the Injector sets an injected value via reflect.ValueOf(instance).Set(value) but if you want to have some instance managed by the Injector be mutated in a specific way, you can implement MutableValue with a pointer receiver.
type OnConstructFunc ¶ added in v0.7.0
type Provider ¶
type Provider[T any] struct { // contains filtered or unexported fields }
Provider allows values to be mutated in real-time in a thread-safe manner. Providers should be used when you have a source of data which you want to be updated in multiple places at once even after leaving the Injector. Values returned by Provider.Get should never be stored as that defeats the purpose of the Provider altogether. Instead, Provider.Get should be called every time one wants to read the data which the Provider provides.
For example
Add("one", 1) one := MustGet[int]("one") fmt.Println(one) // prints 1 Add("one", 2) fmt.Println(one) // still prints 1
To allow for dynamic value updates, you can use a provider.
Add("one", NewProvider(1)) one := MustGet[*Provider[int]]("one") fmt.Println(one.Get()) // prints 1 Add("one", NewProvider(2)) fmt.Println(one.Get()) // prints 2
func NewProvider ¶
Example ¶
Whenever Injector.Add is called, the value is updated within the Injector only. If you've already called Get from the Injector before an Add is called, the value you hold is not updated, for example
package main import ( "fmt" "github.com/eddieowens/axon" ) func main() { type Server struct { ApiKey *axon.Provider[string] `inject:"api_key"` } axon.Add("api_key", axon.NewProvider("123")) server := new(Server) _ = axon.Inject(server) fmt.Println(server.ApiKey.Get()) axon.Add("api_key", axon.NewProvider("456")) fmt.Println(server.ApiKey.Get()) }
Output: 123 456