deps

package module
v0.4.4 Latest Latest
Warning

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

Go to latest
Published: Nov 29, 2022 License: GPL-3.0 Imports: 4 Imported by: 2

README

deps

Dependency injection with Go and generics. Featuring lazy loading, lifetime control, pointer usage detection, hierarchal scopes, resource freeing, dynamic providers, and dynamic types.

package main

import github.com/ClickerMonkey/deps

type Port int
type Env struct {
  Connection string
}
type Database struct {
  Query func(sql string) []any
}
type UserPreferences struct {
  Name string
}
type Param[V any] struct {
  Value V
}
// Dynamic values, essential for generics
func (p *Param[V]) ProvideDynamic(scope *Scope) error {
  // populate p
  return nil
}

func main() {
  // Set values or provide functions
  deps.Set(Port(8080))

  // Globally provided value
  deps.Provide(deps.Provider[Env]{
    Create: func(scope *deps.Scope) (*Env, error) {
      // TODO load environment values and return env
      return &Env{}, nil
    },
  })
  // Value that lives on scope so it can be properly freed
  deps.Provide(deps.Provider[Database]{
    Lifetime: deps.LifetimeScope,
    Create: func(scope *deps.Scope) (*Database, error) {
      env, err := deps.GetScoped[Env](scope)
      if err != nil {
        return nil, err
      }
      // TODO create connection from env.Connection
      return &Database{}, nil
    },
    Free: func(scope *deps.Scope, db *Database) error {
      // TODO close connection
    },
  })
  // Value that exists globally but is notified when its potentially modified.
  deps.Provide(deps.Provider[UserPreferences]{
    Create: func(scope *deps.Scope) (*UserPreferences, error) {
      return &UserPreferences{Name: "ClickerMonkey"}, nil
    },
    AfterPointerUse: func(scope *deps.Scope, prefs *UserPreferences) error {
      // save user preferences, a pointer was requested to change its state.
      return nil
    },
  })

  // Invoke global function
  deps.Invoke(func(port Port, param Param[string]) {
    // do stuff with port.
    // param was dynamically created by an instance of its type, great for generics
  })

  // A child scope from Global
  s := deps.New()
  env, _ := deps.Get[Env]() // get env from global scope
  db, _ := deps.GetScoped[Database](s) // gets database connection and stores it in this scope
  s.Free() // free values in this scope

  // Store a value directly on this scope
  s.Set("Phil") // newName below
  // Also do a custom provider for this scope only
  deps.ProvideScoped(s, deps.Provider[int]{
    Create: func(scope *deps.Scope) (*int, error) {
      age := 33
      return &age, nil
    }
  })

  // Invoke a function and provide it with values from the scope.
  s.Invoke(func(env Env, prefs *UserPreferences, newName string, age int) {
    prefs.Name = newName // would trigger a notification above
  })
  
  // We can also dynamically provide other values that are not set in the scope or its ancestors or
  // are associated with any providers or implements deps.Dynamic.
  s.DynamicProvider = func(typ reflect.Type, scope *Scope) (any, error) {
    // generate value of typ if supported, otherwise return nil, nil
    return nil, nil
  }

  // For good measure
  deps.Global().Free()
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidValue = errors.New("invalid argument for invoke")
View Source
var ErrMissingCreate = errors.New("provider missing create function")
View Source
var ErrNoProvider = errors.New("no provider exists for the given type")
View Source
var ErrNotFunc = errors.New("only funcs can be invoked")
View Source
var ErrNotPointer = errors.New("only pointers can be set on a scope")

Functions

func Get

func Get[V any]() (*V, error)

Returns a constant value from the global scope.

func GetScoped

func GetScoped[V any](scope *Scope) (*V, error)

Returns a constant value from the given scope and an error if there was an error trying to create the value. If a value with the expected type does not exist in this scope or its parent and a dynamic provider is defined that is called. If the result of the dynamic pointer is type V or *V then it's returned without error.

func Hydrate

func Hydrate(value any) error

Given a pointer to any value this will traverse it using the global scope and when it finds types of provided values it updates them.

func IsNil added in v0.4.3

func IsNil(i any) bool

func Provide

func Provide[V any](provider Provider[V])

Registers a provider on the global scope. A Provider can specify lifetime rules and can handle lazily creating new values and freeing them when their lifetime expires. A provider can also be notified about a potential value change when Invoke is called with a function which accepts the pointer argument.

func ProvideScoped

func ProvideScoped[V any](scoped *Scope, provider Provider[V])

Registers a provider on the given scope. A Provider can specify lifetime rules and can handle lazily creating new values and freeing them when their lifetime expires. A provider can also be notified about a potential value change when Invoke is called with a function which accepts the pointer argument.

func Set

func Set[V any](value *V)

Sets a constant value on the global scope.

func SetScoped

func SetScoped[V any](scope *Scope, value *V)

Sets a constant value on the given scope.

func TypeOf added in v0.2.0

func TypeOf[V any]() reflect.Type

Returns the reflect.Type of V

Types

type Dynamic added in v0.3.0

type Dynamic interface {
	// Given the scope its trying to be created in, the specific type, try to populate
	// the instance of this value. If there was an error it will be passed up through
	// the invokation or the hydration request.
	ProvideDynamic(scope *Scope) error
}

If a type to be provided doesn't have a provider but implements this interface the type itself becomes a provider. This is especially useful for types with generics and the types are not known ahead of time or there are too many to be individually provided.

func GetDynamic added in v0.3.0

func GetDynamic(typ reflect.Type) Dynamic

Given a type it returns an instance of it if it implements the Dynamic interface. If it does not, nil is returned.

type DynamicProvider added in v0.2.0

type DynamicProvider func(typ reflect.Type, scope *Scope) (any, error)

A dynamic provider if a requested type does not have value or provider. If the value returned is not the expected type a ErrNoProvider will be thrown.

type Lifetime

type Lifetime int

How long values should last in a scope.

const (
	// The value should last forever, or until scope.Free() is called. If a provider or value
	// is not explicitly set on the current scope it will reach out to the parent scopes all the way
	// to the global scope, and prefer to place values on the global scope since they desire to
	// last forever.
	LifetimeForever Lifetime = iota
	// The value will be created on the given scope and freed when scope.Free() is called.
	LifetimeScope
	// The value will be created for invoke or hydration but immediately freed after that.
	LifetimeOnce
)

type Provider

type Provider[V any] struct {
	Lifetime        Lifetime
	Create          func(scope *Scope) (*V, error)
	AfterPointerUse func(scope *Scope, value *V) error
	Free            func(scope *Scope, value *V) error
}

type Result added in v0.1.2

type Result []any

func Invoke

func Invoke(fn any) (Result, error)

Invokes a function passing provided values from the global scope as arguments. Any argument types that do not have a constant or provider will get their default value.

func (Result) Defined added in v0.4.4

func (r Result) Defined() []any

Returns the non-nil values in the result.

func (Result) Err added in v0.1.2

func (r Result) Err() error

Returns the first non-nil error in the result.

type Scope

type Scope struct {
	Dynamic DynamicProvider
	// contains filtered or unexported fields
}

func Global

func Global() *Scope

Returns the global scope. All scopes created with New() has this scope as the parent. The global Set, Get, Provide, Invoke, & Hydrate functions operate based on providers given to this global scope. All child scopes can return values created globally depending on the provided lifetime.

func New

func New() *Scope

Creates a new scope with the global scope as the parent.

func (*Scope) Free

func (scope *Scope) Free() error

Frees all values in this scope.

func (*Scope) FreeOnce

func (scope *Scope) FreeOnce() error

Frees all values in this scope with a lifetime of once.

func (*Scope) Get

func (scope *Scope) Get(key reflect.Type) (any, error)

Gets a value from this scope with the given type and potentially returns an error. If it doesn't exist on this scope a provider is searched through the parent scopes. If the provider has a lifetime of forever its created on the deepest scope, otherwise scope and once lifetime values are stored in this scope.

func (*Scope) Hydrate

func (scope *Scope) Hydrate(value any) error

Given a pointer to any value this will traverse it using this scope and when it finds types of provided values it updates them. Once the hydrated values are doing being used scope.FreeOnce() should be called.

func (*Scope) Invoke

func (scope *Scope) Invoke(fn any) (Result, error)

Invokes the given function by providing arguments of the requested types with values found or provided in this scope and its parents. If the function has a pointer argument to a provided type and the provider has a AfterPointerUse defined it will be called after the function returns. If any values were created on this scope with a lifetime of once they will be freed after the function returns.

func (*Scope) Parent

func (scope *Scope) Parent() *Scope

Returns this scope's parent.

func (*Scope) Set

func (scope *Scope) Set(value any) error

Sets a value on this scope.

func (*Scope) Spawn

func (scope *Scope) Spawn() *Scope

Returns a child to this scope.

Jump to

Keyboard shortcuts

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