summaryrefslogtreecommitdiff
path: root/vendor/github.com/golobby/container/v3/container.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/golobby/container/v3/container.go')
-rw-r--r--vendor/github.com/golobby/container/v3/container.go299
1 files changed, 299 insertions, 0 deletions
diff --git a/vendor/github.com/golobby/container/v3/container.go b/vendor/github.com/golobby/container/v3/container.go
new file mode 100644
index 0000000..d97ba2d
--- /dev/null
+++ b/vendor/github.com/golobby/container/v3/container.go
@@ -0,0 +1,299 @@
+// Package container is a lightweight yet powerful IoC container for Go projects.
+// It provides an easy-to-use interface and performance-in-mind container to be your ultimate requirement.
+package container
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "unsafe"
+)
+
+// binding holds a resolver and a concrete (if already resolved).
+// It is the break for the Container wall!
+type binding struct {
+ resolver interface{} // resolver is the function that is responsible for making the concrete.
+ concrete interface{} // concrete is the stored instance for singleton bindings.
+ isSingleton bool // isSingleton is true if the binding is a singleton.
+}
+
+// make resolves the binding if needed and returns the resolved concrete.
+func (b *binding) make(c Container) (interface{}, error) {
+ if b.concrete != nil {
+ return b.concrete, nil
+ }
+
+ retVal, err := c.invoke(b.resolver)
+ if b.isSingleton {
+ b.concrete = retVal
+ }
+
+ return retVal, err
+}
+
+// Container holds the bindings and provides methods to interact with them.
+// It is the entry point in the package.
+type Container map[reflect.Type]map[string]*binding
+
+// New creates a new concrete of the Container.
+func New() Container {
+ return make(Container)
+}
+
+// bind maps an abstraction to concrete and instantiates if it is a singleton binding.
+func (c Container) bind(resolver interface{}, name string, isSingleton bool, isLazy bool) error {
+ reflectedResolver := reflect.TypeOf(resolver)
+ if reflectedResolver.Kind() != reflect.Func {
+ return errors.New("container: the resolver must be a function")
+ }
+
+ if reflectedResolver.NumOut() > 0 {
+ if _, exist := c[reflectedResolver.Out(0)]; !exist {
+ c[reflectedResolver.Out(0)] = make(map[string]*binding)
+ }
+ }
+
+ if err := c.validateResolverFunction(reflectedResolver); err != nil {
+ return err
+ }
+
+ var concrete interface{}
+ if !isLazy {
+ var err error
+ concrete, err = c.invoke(resolver)
+ if err != nil {
+ return err
+ }
+ }
+
+ if isSingleton {
+ c[reflectedResolver.Out(0)][name] = &binding{resolver: resolver, concrete: concrete, isSingleton: isSingleton}
+ } else {
+ c[reflectedResolver.Out(0)][name] = &binding{resolver: resolver, isSingleton: isSingleton}
+ }
+
+ return nil
+}
+
+func (c Container) validateResolverFunction(funcType reflect.Type) error {
+ retCount := funcType.NumOut()
+
+ if retCount == 0 || retCount > 2 {
+ return errors.New("container: resolver function signature is invalid - it must return abstract, or abstract and error")
+ }
+
+ resolveType := funcType.Out(0)
+ for i := 0; i < funcType.NumIn(); i++ {
+ if funcType.In(i) == resolveType {
+ return fmt.Errorf("container: resolver function signature is invalid - depends on abstract it returns")
+ }
+ }
+
+ return nil
+}
+
+// invoke calls a function and its returned values.
+// It only accepts one value and an optional error.
+func (c Container) invoke(function interface{}) (interface{}, error) {
+ arguments, err := c.arguments(function)
+ if err != nil {
+ return nil, err
+ }
+
+ values := reflect.ValueOf(function).Call(arguments)
+ if len(values) == 2 && values[1].CanInterface() {
+ if err, ok := values[1].Interface().(error); ok {
+ return values[0].Interface(), err
+ }
+ }
+ return values[0].Interface(), nil
+}
+
+// arguments returns the list of resolved arguments for a function.
+func (c Container) arguments(function interface{}) ([]reflect.Value, error) {
+ reflectedFunction := reflect.TypeOf(function)
+ argumentsCount := reflectedFunction.NumIn()
+ arguments := make([]reflect.Value, argumentsCount)
+
+ for i := 0; i < argumentsCount; i++ {
+ abstraction := reflectedFunction.In(i)
+ if concrete, exist := c[abstraction][""]; exist {
+ instance, err := concrete.make(c)
+ if err != nil {
+ return nil, err
+ }
+ arguments[i] = reflect.ValueOf(instance)
+ } else {
+ return nil, errors.New("container: no concrete found for: " + abstraction.String())
+ }
+ }
+
+ return arguments, nil
+}
+
+// Reset deletes all the existing bindings and empties the container.
+func (c Container) Reset() {
+ for k := range c {
+ delete(c, k)
+ }
+}
+
+// Singleton binds an abstraction to concrete in singleton mode.
+// It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
+// The resolver function can have arguments of abstraction that have been declared in the Container already.
+func (c Container) Singleton(resolver interface{}) error {
+ return c.bind(resolver, "", true, false)
+}
+
+// SingletonLazy binds an abstraction to concrete lazily in singleton mode.
+// The concrete is resolved only when the abstraction is resolved for the first time.
+// It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
+// The resolver function can have arguments of abstraction that have been declared in the Container already.
+func (c Container) SingletonLazy(resolver interface{}) error {
+ return c.bind(resolver, "", true, true)
+}
+
+// NamedSingleton binds a named abstraction to concrete in singleton mode.
+func (c Container) NamedSingleton(name string, resolver interface{}) error {
+ return c.bind(resolver, name, true, false)
+}
+
+// NamedSingleton binds a named abstraction to concrete lazily in singleton mode.
+// The concrete is resolved only when the abstraction is resolved for the first time.
+func (c Container) NamedSingletonLazy(name string, resolver interface{}) error {
+ return c.bind(resolver, name, true, true)
+}
+
+// Transient binds an abstraction to concrete in transient mode.
+// It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
+// The resolver function can have arguments of abstraction that have been declared in the Container already.
+func (c Container) Transient(resolver interface{}) error {
+ return c.bind(resolver, "", false, false)
+}
+
+// TransientLazy binds an abstraction to concrete lazily in transient mode.
+// Normally the resolver will be called during registration, but that is skipped in lazy mode.
+// It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
+// The resolver function can have arguments of abstraction that have been declared in the Container already.
+func (c Container) TransientLazy(resolver interface{}) error {
+ return c.bind(resolver, "", false, true)
+}
+
+// NamedTransient binds a named abstraction to concrete lazily in transient mode.
+func (c Container) NamedTransient(name string, resolver interface{}) error {
+ return c.bind(resolver, name, false, false)
+}
+
+// NamedTransient binds a named abstraction to concrete in transient mode.
+// Normally the resolver will be called during registration, but that is skipped in lazy mode.
+func (c Container) NamedTransientLazy(name string, resolver interface{}) error {
+ return c.bind(resolver, name, false, true)
+}
+
+// Call takes a receiver function with one or more arguments of the abstractions (interfaces).
+// It invokes the receiver function and passes the related concretes.
+func (c Container) Call(function interface{}) error {
+ receiverType := reflect.TypeOf(function)
+ if receiverType == nil || receiverType.Kind() != reflect.Func {
+ return errors.New("container: invalid function")
+ }
+
+ arguments, err := c.arguments(function)
+ if err != nil {
+ return err
+ }
+
+ result := reflect.ValueOf(function).Call(arguments)
+
+ if len(result) == 0 {
+ return nil
+ } else if len(result) == 1 && result[0].CanInterface() {
+ if result[0].IsNil() {
+ return nil
+ }
+ if err, ok := result[0].Interface().(error); ok {
+ return err
+ }
+ }
+
+ return errors.New("container: receiver function signature is invalid")
+}
+
+// Resolve takes an abstraction (reference of an interface type) and fills it with the related concrete.
+func (c Container) Resolve(abstraction interface{}) error {
+ return c.NamedResolve(abstraction, "")
+}
+
+// NamedResolve takes abstraction and its name and fills it with the related concrete.
+func (c Container) NamedResolve(abstraction interface{}, name string) error {
+ receiverType := reflect.TypeOf(abstraction)
+ if receiverType == nil {
+ return errors.New("container: invalid abstraction")
+ }
+
+ if receiverType.Kind() == reflect.Ptr {
+ elem := receiverType.Elem()
+
+ if concrete, exist := c[elem][name]; exist {
+ if instance, err := concrete.make(c); err == nil {
+ reflect.ValueOf(abstraction).Elem().Set(reflect.ValueOf(instance))
+ return nil
+ } else {
+ return fmt.Errorf("container: encountered error while making concrete for: %s. Error encountered: %w", elem.String(), err)
+ }
+ }
+
+ return errors.New("container: no concrete found for: " + elem.String())
+ }
+
+ return errors.New("container: invalid abstraction")
+}
+
+// Fill takes a struct and resolves the fields with the tag `container:"inject"`
+func (c Container) Fill(structure interface{}) error {
+ receiverType := reflect.TypeOf(structure)
+ if receiverType == nil {
+ return errors.New("container: invalid structure")
+ }
+
+ if receiverType.Kind() == reflect.Ptr {
+ elem := receiverType.Elem()
+ if elem.Kind() == reflect.Struct {
+ s := reflect.ValueOf(structure).Elem()
+
+ for i := 0; i < s.NumField(); i++ {
+ f := s.Field(i)
+
+ if t, exist := s.Type().Field(i).Tag.Lookup("container"); exist {
+ var name string
+
+ if t == "type" {
+ name = ""
+ } else if t == "name" {
+ name = s.Type().Field(i).Name
+ } else {
+ return fmt.Errorf("container: %v has an invalid struct tag", s.Type().Field(i).Name)
+ }
+
+ if concrete, exist := c[f.Type()][name]; exist {
+ instance, err := concrete.make(c)
+ if err != nil {
+ return err
+ }
+
+ ptr := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
+ ptr.Set(reflect.ValueOf(instance))
+
+ continue
+ }
+
+ return fmt.Errorf("container: cannot make %v field", s.Type().Field(i).Name)
+ }
+ }
+
+ return nil
+ }
+ }
+
+ return errors.New("container: invalid structure")
+}