Skip to content

Commit

Permalink
Implement ProvideValue() method
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxim Bovtunov committed Feb 15, 2021
1 parent 4aee580 commit 02504b0
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ The format is based on
project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html): TBD, use modules or another vendor system.

## v1.9.0

### Added

- `container.ProvideValue()` function.

## v1.8.0

### Added
Expand Down
16 changes: 16 additions & 0 deletions cmp_nop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package di

import (
"reflect"
)

type nopCompiler struct{}

func (v nopCompiler) deps(s schema) ([]*node, error) {
return nil, nil
}

func (v nopCompiler) compile(dependencies []reflect.Value, s schema) (reflect.Value, error) {
bug()
return reflect.Value{}, nil
}
40 changes: 39 additions & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Container struct {
type diopts struct {
// Array of di.Provide() options.
provides []provideOptions
// Array of di.ProvideValue() options.
values []provideValueOptions
// Array of di.Invoke() options.
invokes []invokeOptions
// Array of di.Resolve() options.
Expand Down Expand Up @@ -79,6 +81,11 @@ func (c *Container) Apply(options ...Option) error {
}

func (c *Container) apply(di diopts) error {
for _, provide := range di.values {
if err := c.provideValue(provide.value, provide.options...); err != nil {
return fmt.Errorf("%s: %w", provide.frame, err)
}
}
// process di.Resolve() diopts
for _, provide := range di.provides {
if err := c.provide(provide.constructor, provide.options...); err != nil {
Expand Down Expand Up @@ -115,12 +122,39 @@ func (c *Container) Provide(constructor Constructor, options ...ProvideOption) e
return nil
}

// ProvideValue provides value as is.
func (c *Container) ProvideValue(value Value, options ...ProvideOption) error {
if err := c.provideValue(value, options...); err != nil {
return errWithStack(err)
}
return nil
}

func (c *Container) provideValue(value Value, options ...ProvideOption) error {
if value == nil {
return fmt.Errorf("invalid value, got nil")
}
params := ProvideParams{}
// apply provide diopts
for _, opt := range options {
opt.applyProvide(&params)
}
v := reflect.ValueOf(value)
n := &node{
compiler: nopCompiler{},
rt: v.Type(),
tags: params.Tags,
rv: &v,
}
return c.provideNode(n, params)
}

func (c *Container) provide(constructor Constructor, options ...ProvideOption) error {
if constructor == nil {
return fmt.Errorf("invalid constructor signature, got nil")
}
params := ProvideParams{}
// apply provide diopts
// apply provide options
for _, opt := range options {
opt.applyProvide(&params)
}
Expand All @@ -131,6 +165,10 @@ func (c *Container) provide(constructor Constructor, options ...ProvideOption) e
for k, v := range params.Tags {
n.tags[k] = v
}
return c.provideNode(n, params)
}

func (c *Container) provideNode(n *node, params ProvideParams) error {
c.schema.register(n)
// register interfaces
for _, cur := range params.Interfaces {
Expand Down
54 changes: 54 additions & 0 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,60 @@ func TestContainer_Provide(t *testing.T) {
})
}

func TestContainer_ProvideValue(t *testing.T) {
t.Run("provide nil value cause error", func(t *testing.T) {
c, err := di.New()
require.NoError(t, err)
require.NotNil(t, c)
err = c.ProvideValue(nil)
require.Error(t, err)
require.Contains(t, err.Error(), "container_test.go:")
require.Contains(t, err.Error(), "invalid value, got nil")
})

t.Run("provide and resolve value", func(t *testing.T) {
c, err := di.New()
require.NoError(t, err)
require.NotNil(t, c)
mux := &http.ServeMux{}
err = c.ProvideValue(mux, di.As(new(http.Handler)))
require.NoError(t, err)
err = c.Provide(func(handler http.Handler) *http.Server {
return &http.Server{
Handler: handler,
}
})
require.NoError(t, err)
var server *http.Server
err = c.Resolve(&server)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%p", mux), fmt.Sprintf("%p", server.Handler))
})

t.Run("provide values by option", func(t *testing.T) {
mux := &http.ServeMux{}
c, err := di.New(
di.ProvideValue(mux),
)
require.NoError(t, err)
require.NotNil(t, c)
var result *http.ServeMux
err = c.Resolve(&result)
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%p", mux), fmt.Sprintf("%p", result))
})

t.Run("provide nil value by option", func(t *testing.T) {
c, err := di.New(
di.ProvideValue(nil),
)
require.Error(t, err)
require.Nil(t, c)
require.Contains(t, err.Error(), "container_test.go:")
require.Contains(t, err.Error(), "invalid value, got nil")
})
}

func TestContainer_Resolve(t *testing.T) {
t.Run("resolve into nil cause error", func(t *testing.T) {
c, err := di.New()
Expand Down
22 changes: 22 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ func Provide(constructor Constructor, options ...ProvideOption) Option {
})
}

// ProvideValue provides value as is.
func ProvideValue(value Value, options ...ProvideOption) Option {
frame := stacktrace(0)
return option(func(c *diopts) {
c.values = append(c.values, provideValueOptions{
frame,
value,
options,
})
})
}

// Constructor is a function with follow signature:
//
// func NewHTTPServer(addr string, handler http.Handler) (server *http.Server, cleanup func(), err error) {
Expand All @@ -44,6 +56,9 @@ func Provide(constructor Constructor, options ...ProvideOption) Option {
// Third result is a optional error. Sometimes our types cannot be constructed.
type Constructor interface{}

// Value
type Value interface{}

// ProvideOption is a functional option interface that modify provide behaviour. See di.As(), di.WithName().
type ProvideOption interface {
applyProvide(params *ProvideParams)
Expand Down Expand Up @@ -244,6 +259,13 @@ type provideOptions struct {
options []ProvideOption
}

// struct that contains value with options.
type provideValueOptions struct {
frame callerFrame
value Value
options []ProvideOption
}

// struct that contains invoke function with options.
type invokeOptions struct {
frame callerFrame
Expand Down

0 comments on commit 02504b0

Please sign in to comment.