github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/injector/resolver.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package injector
     7  
     8  import (
     9  	"reflect"
    10  
    11  	"github.com/insolar/vanilla/throw"
    12  )
    13  
    14  func NewDependencyResolver(target interface{}, globalParent DependencyRegistry, localParent DependencyRegistry,
    15  	onPutFn func(id string, v interface{}, from DependencyOrigin)) DependencyResolver {
    16  	if target == nil {
    17  		panic(throw.IllegalValue())
    18  	}
    19  	return DependencyResolver{target: target, globalParent: globalParent, localParent: localParent, onPutFn: onPutFn}
    20  }
    21  
    22  type DependencyOrigin uint8
    23  
    24  const (
    25  	DependencyFromLocal DependencyOrigin = 1 << iota
    26  	DependencyFromProvider
    27  )
    28  
    29  type DependencyResolver struct {
    30  	globalParent DependencyRegistry
    31  	localParent  DependencyRegistry
    32  	target       interface{}
    33  	implMap      map[reflect.Type]interface{}
    34  	resolved     map[string]interface{}
    35  	onPutFn      func(id string, v interface{}, from DependencyOrigin)
    36  }
    37  
    38  func (p *DependencyResolver) IsZero() bool {
    39  	return p.target == nil
    40  }
    41  
    42  func (p *DependencyResolver) IsEmpty() bool {
    43  	return len(p.resolved) == 0
    44  }
    45  
    46  func (p *DependencyResolver) Count() int {
    47  	return len(p.resolved)
    48  }
    49  
    50  func (p *DependencyResolver) Target() interface{} {
    51  	return p.target
    52  }
    53  
    54  func (p *DependencyResolver) ResolveAndReplace(overrides map[string]interface{}) {
    55  	for id, v := range overrides {
    56  		if id == "" {
    57  			panic(throw.IllegalValue())
    58  		}
    59  		p.resolveAndPut(id, v, DependencyFromLocal)
    60  	}
    61  }
    62  
    63  func (p *DependencyResolver) ResolveAndMerge(values map[string]interface{}) {
    64  	for id, v := range values {
    65  		if id == "" {
    66  			panic(throw.IllegalValue())
    67  		}
    68  		if _, ok := p.resolved[id]; ok {
    69  			continue
    70  		}
    71  		p.resolveAndPut(id, v, DependencyFromLocal)
    72  	}
    73  }
    74  
    75  func (p *DependencyResolver) FindDependency(id string) (interface{}, bool) {
    76  	if id == "" {
    77  		panic(throw.IllegalValue())
    78  	}
    79  	if v, ok := p.resolved[id]; ok { // allows nil values
    80  		return v, true
    81  	}
    82  
    83  	v, ok, _ := p.getFromParent(id)
    84  	return v, ok
    85  }
    86  
    87  func (p *DependencyResolver) FindImplementation(t reflect.Type, checkAmbiguous bool) (interface{}, error) {
    88  	if v := p.implMap[t]; v != nil {
    89  		return v, nil
    90  	} else if p.implMap == nil {
    91  		p.implMap = map[reflect.Type]interface{}{}
    92  	}
    93  
    94  	var from DependencyOrigin
    95  	id := ""
    96  	var v interface{}
    97  
    98  	if scanner, ok := p.localParent.(ScanDependencyRegistry); ok {
    99  		var err error
   100  		id, v, err = p.findImpl(scanner, t, checkAmbiguous, "")
   101  		switch {
   102  		case err != nil:
   103  			return nil, err
   104  		case v != nil:
   105  			from |= DependencyFromLocal
   106  		}
   107  	}
   108  
   109  	switch scanner, ok := p.globalParent.(ScanDependencyRegistry); {
   110  	case !ok:
   111  	case v == nil:
   112  		var err error
   113  		id, v, err = p.findImpl(scanner, t, checkAmbiguous, "")
   114  		switch {
   115  		case err != nil:
   116  			return nil, err
   117  		case v == nil:
   118  			return nil, nil
   119  		}
   120  	case checkAmbiguous:
   121  		if _, _, err := p.findImpl(scanner, t, true, id); err != nil {
   122  			return nil, err
   123  		}
   124  	}
   125  
   126  	if from&DependencyFromLocal != 0 {
   127  		p.putResolved(id, v, from)
   128  	}
   129  	p.implMap[t] = v
   130  
   131  	return v, nil
   132  }
   133  
   134  func (p *DependencyResolver) findImpl(scanner ScanDependencyRegistry, t reflect.Type, checkAmbiguous bool, fid string) (id string, v interface{}, err error) {
   135  	id = fid
   136  	scanner.ScanDependencies(func(xid string, xv interface{}) bool {
   137  		switch {
   138  		case xv == nil || xid == "":
   139  			return false
   140  		case !reflect.ValueOf(xv).Type().AssignableTo(t):
   141  			return false
   142  		case id != "":
   143  			err = throw.E("ambiguous dependency", struct {
   144  				ExpectedType reflect.Type
   145  				ID1, ID2 string
   146  			}{
   147  				t, id, xid,
   148  			})
   149  			return true
   150  		default:
   151  			id, v = xid, xv
   152  			return !checkAmbiguous
   153  		}
   154  	})
   155  	return
   156  }
   157  
   158  func (p *DependencyResolver) GetResolvedDependency(id string) (interface{}, bool) {
   159  	if id == "" {
   160  		panic(throw.IllegalValue())
   161  	}
   162  	return p.getResolved(id)
   163  }
   164  
   165  func (p *DependencyResolver) getFromParent(id string) (interface{}, bool, DependencyOrigin) {
   166  	if p.localParent != nil {
   167  		if v, ok := p.localParent.FindDependency(id); ok {
   168  			return v, true, DependencyFromLocal
   169  		}
   170  	}
   171  	if p.globalParent != nil {
   172  		if v, ok := p.globalParent.FindDependency(id); ok {
   173  			return v, true, 0
   174  		}
   175  	}
   176  	return nil, false, 0
   177  }
   178  
   179  func (p *DependencyResolver) getResolved(id string) (interface{}, bool) {
   180  	if v, ok := p.resolved[id]; ok { // allows nil values
   181  		return v, true
   182  	}
   183  	if v, ok, from := p.getFromParent(id); ok {
   184  		return p.resolveAndPut(id, v, from), true
   185  	}
   186  
   187  	return nil, false
   188  }
   189  
   190  func (p *DependencyResolver) resolveAndPut(id string, v interface{}, from DependencyOrigin) interface{} {
   191  	if dp, ok := v.(DependencyProviderFunc); ok {
   192  		p._putResolved(id, nil) // guard for resolve loop
   193  		v = dp(p.target, id, p.GetResolvedDependency)
   194  		p.putResolved(id, v, from|DependencyFromProvider)
   195  	} else if from&DependencyFromLocal != 0 {
   196  		p.putResolved(id, v, from)
   197  	}
   198  	return v
   199  }
   200  
   201  func (p *DependencyResolver) putResolved(id string, v interface{}, from DependencyOrigin) {
   202  	p._putResolved(id, v)
   203  	if p.onPutFn != nil {
   204  		p.onPutFn(id, v, from)
   205  	}
   206  }
   207  
   208  func (p *DependencyResolver) _putResolved(id string, v interface{}) {
   209  	if p.resolved == nil {
   210  		p.resolved = make(map[string]interface{})
   211  	}
   212  	p.resolved[id] = v
   213  }
   214  
   215  func (p *DependencyResolver) CopyResolved() map[string]interface{} {
   216  	n := len(p.resolved)
   217  	if n == 0 {
   218  		return nil
   219  	}
   220  	result := make(map[string]interface{}, n)
   221  
   222  	for id, v := range p.resolved {
   223  		result[id] = v
   224  	}
   225  
   226  	return result
   227  }
   228  
   229  func (p *DependencyResolver) Flush() map[string]interface{} {
   230  	n := len(p.resolved)
   231  	if n == 0 {
   232  		return nil
   233  	}
   234  	m := p.resolved
   235  	p.resolved = nil
   236  	return m
   237  }