package dig
import (
"fmt"
"reflect"
"go.uber.org/dig/internal/digreflect"
"go.uber.org/dig/internal/dot"
)
type decoratorState int
const (
decoratorReady decoratorState = iota
decoratorOnStack
decoratorCalled
)
type decorator interface {
Call(c containerStore ) error
ID() dot .CtorID
State() decoratorState
}
type decoratorNode struct {
dcor interface {}
dtype reflect .Type
id dot .CtorID
location *digreflect .Func
state decoratorState
params paramList
results resultList
orders map [*Scope ]int
s *Scope
callback Callback
beforeCallback BeforeCallback
}
func newDecoratorNode(dcor interface {}, s *Scope , opts decorateOptions ) (*decoratorNode , error ) {
dval := reflect .ValueOf (dcor )
dtype := dval .Type ()
dptr := dval .Pointer ()
pl , err := newParamList (dtype , s )
if err != nil {
return nil , err
}
rl , err := newResultList (dtype , resultOptions {})
if err != nil {
return nil , err
}
n := &decoratorNode {
dcor : dcor ,
dtype : dtype ,
id : dot .CtorID (dptr ),
location : digreflect .InspectFunc (dcor ),
orders : make (map [*Scope ]int ),
params : pl ,
results : rl ,
s : s ,
callback : opts .Callback ,
beforeCallback : opts .BeforeCallback ,
}
return n , nil
}
func (n *decoratorNode ) Call (s containerStore ) (err error ) {
if n .state == decoratorCalled {
return nil
}
n .state = decoratorOnStack
if err := shallowCheckDependencies (s , n .params ); err != nil {
return errMissingDependencies {
Func : n .location ,
Reason : err ,
}
}
args , err := n .params .BuildList (n .s )
if err != nil {
return errArgumentsFailed {
Func : n .location ,
Reason : err ,
}
}
if n .beforeCallback != nil {
n .beforeCallback (BeforeCallbackInfo {
Name : fmt .Sprintf ("%v.%v" , n .location .Package , n .location .Name ),
})
}
if n .callback != nil {
start := s .clock ().Now ()
defer func () {
n .callback (CallbackInfo {
Name : fmt .Sprintf ("%v.%v" , n .location .Package , n .location .Name ),
Error : err ,
Runtime : s .clock ().Since (start ),
})
}()
}
if n .s .recoverFromPanics {
defer func () {
if p := recover (); p != nil {
err = PanicError {
fn : n .location ,
Panic : p ,
}
}
}()
}
results := s .invoker ()(reflect .ValueOf (n .dcor ), args )
if err = n .results .ExtractList (n .s , true , results ); err != nil {
return err
}
n .state = decoratorCalled
return nil
}
func (n *decoratorNode ) ID () dot .CtorID { return n .id }
func (n *decoratorNode ) State () decoratorState { return n .state }
type DecorateOption interface {
apply(*decorateOptions )
}
type decorateOptions struct {
Info *DecorateInfo
Callback Callback
BeforeCallback BeforeCallback
}
func FillDecorateInfo (info *DecorateInfo ) DecorateOption {
return fillDecorateInfoOption {info : info }
}
type fillDecorateInfoOption struct { info *DecorateInfo }
func (o fillDecorateInfoOption ) String () string {
return fmt .Sprintf ("FillDecorateInfo(%p)" , o .info )
}
func (o fillDecorateInfoOption ) apply (opts *decorateOptions ) {
opts .Info = o .info
}
type DecorateInfo struct {
ID ID
Inputs []*Input
Outputs []*Output
}
func (c *Container ) Decorate (decorator interface {}, opts ...DecorateOption ) error {
return c .scope .Decorate (decorator , opts ...)
}
func (s *Scope ) Decorate (decorator interface {}, opts ...DecorateOption ) error {
var options decorateOptions
for _ , opt := range opts {
opt .apply (&options )
}
dn , err := newDecoratorNode (decorator , s , options )
if err != nil {
return err
}
keys , err := findResultKeys (dn .results )
if err != nil {
return err
}
for _ , k := range keys {
if _ , ok := s .decorators [k ]; ok {
return newErrInvalidInput (
fmt .Sprintf ("cannot decorate using function %v: %s already decorated" , dn .dtype , k ), nil )
}
s .decorators [k ] = dn
}
if info := options .Info ; info != nil {
params := dn .params .DotParam ()
results := dn .results .DotResult ()
info .ID = (ID )(dn .id )
info .Inputs = make ([]*Input , len (params ))
info .Outputs = make ([]*Output , len (results ))
for i , param := range params {
info .Inputs [i ] = &Input {
t : param .Type ,
optional : param .Optional ,
name : param .Name ,
group : param .Group ,
}
}
for i , res := range results {
info .Outputs [i ] = &Output {
t : res .Type ,
name : res .Name ,
group : res .Group ,
}
}
}
return nil
}
func findResultKeys(r resultList ) ([]key , error ) {
var (
q []result
keys []key
)
q = append (q , r )
for len (q ) > 0 {
res := q [0 ]
q = q [1 :]
switch innerResult := res .(type ) {
case resultSingle :
keys = append (keys , key {t : innerResult .Type , name : innerResult .Name })
case resultGrouped :
if innerResult .Type .Kind () != reflect .Slice {
return nil , newErrInvalidInput ("decorating a value group requires decorating the entire value group, not a single value" , nil )
}
keys = append (keys , key {t : innerResult .Type .Elem (), group : innerResult .Group })
case resultObject :
for _ , f := range innerResult .Fields {
q = append (q , f .Result )
}
case resultList :
q = append (q , innerResult .Results ...)
}
}
return keys , nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .