github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/deps/deps.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package deps
    15  
    16  import (
    17  	"reflect"
    18  
    19  	"github.com/pingcap/log"
    20  	"github.com/pingcap/tiflow/pkg/errors"
    21  	"go.uber.org/dig"
    22  	"go.uber.org/zap"
    23  )
    24  
    25  // Deps provides a way to construct dependencies container, and supports
    26  // dependency injection.
    27  type Deps struct {
    28  	container *dig.Container
    29  }
    30  
    31  // NewDeps creates a new Dep instance
    32  func NewDeps() *Deps {
    33  	return &Deps{
    34  		container: dig.New(),
    35  	}
    36  }
    37  
    38  // Provide accepts a constructor and build a value into container
    39  func (d *Deps) Provide(constructor interface{}) error {
    40  	return d.container.Provide(constructor)
    41  }
    42  
    43  // Construct takes a function in the form of
    44  // `func(arg1 Type1, arg2 Type2,...) (ret, error)`.
    45  // The arguments to the function is automatically filled with
    46  // the dependency injection functionality.
    47  func (d *Deps) Construct(fn interface{}) (interface{}, error) {
    48  	fnTp := reflect.TypeOf(fn)
    49  	if fnTp.NumOut() != 2 {
    50  		log.Panic("Unexpected input type", zap.Any("type", reflect.TypeOf(fn)))
    51  	}
    52  
    53  	var in, out []reflect.Type
    54  	for i := 0; i < fnTp.NumIn(); i++ {
    55  		in = append(in, fnTp.In(i))
    56  	}
    57  	out = append(out, fnTp.Out(1))
    58  
    59  	invokeFnTp := reflect.FuncOf(in, out, false)
    60  
    61  	var obj reflect.Value
    62  	invokeFn := reflect.MakeFunc(invokeFnTp, func(args []reflect.Value) (results []reflect.Value) {
    63  		retVals := reflect.ValueOf(fn).Call(args)
    64  		obj = retVals[0]
    65  		return retVals[1:]
    66  	})
    67  
    68  	if err := d.container.Invoke(invokeFn.Interface()); err != nil {
    69  		return nil, errors.Trace(err)
    70  	}
    71  
    72  	return obj.Interface(), nil
    73  }
    74  
    75  // Fill injects dependencies from Deps to params
    76  func (d *Deps) Fill(params interface{}) error {
    77  	invokeFnTp := reflect.FuncOf(
    78  		[]reflect.Type{reflect.TypeOf(params).Elem()},
    79  		[]reflect.Type{reflect.TypeOf(new(error))},
    80  		false)
    81  	invokeFn := reflect.MakeFunc(invokeFnTp, func(args []reflect.Value) (results []reflect.Value) {
    82  		defer func() {
    83  			if v := recover(); v != nil {
    84  				results = []reflect.Value{reflect.ValueOf(errors.Errorf("internal error: %v", v))}
    85  			}
    86  		}()
    87  		reflect.ValueOf(params).Elem().Set(args[0])
    88  		return []reflect.Value{reflect.ValueOf(new(error))}
    89  	})
    90  	if err := d.container.Invoke(invokeFn.Interface()); err != nil {
    91  		return errors.Trace(err)
    92  	}
    93  	return nil
    94  }