143 lines
4.2 KiB
Go
143 lines
4.2 KiB
Go
// Copyright (c) 2022 Uber Technologies, Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package fx
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"go.uber.org/fx/internal/fxreflect"
|
|
)
|
|
|
|
// Replace provides instantiated values for graph modification as if
|
|
// they had been provided using a decorator with fx.Decorate.
|
|
// The most specific type of each value (as determined by reflection) is used.
|
|
//
|
|
// Refer to the documentation on fx.Decorate to see how graph modifications
|
|
// work with fx.Module.
|
|
//
|
|
// This serves a purpose similar to what fx.Supply does for fx.Provide.
|
|
//
|
|
// For example, given,
|
|
//
|
|
// var log *zap.Logger = ...
|
|
//
|
|
// The following two forms are equivalent.
|
|
//
|
|
// fx.Replace(log)
|
|
//
|
|
// fx.Decorate(
|
|
// func() *zap.Logger {
|
|
// return log
|
|
// },
|
|
// )
|
|
//
|
|
// Replace panics if a value (or annotation target) is an untyped nil or an error.
|
|
//
|
|
// # Replace Caveats
|
|
//
|
|
// As mentioned above, Replace uses the most specific type of the provided
|
|
// value. For interface values, this refers to the type of the implementation,
|
|
// not the interface. So if you try to replace an io.Writer, fx.Replace will
|
|
// use the type of the implementation.
|
|
//
|
|
// var stderr io.Writer = os.Stderr
|
|
// fx.Replace(stderr)
|
|
//
|
|
// Is equivalent to,
|
|
//
|
|
// fx.Decorate(func() *os.File { return os.Stderr })
|
|
//
|
|
// This is typically NOT what you intended. To replace the io.Writer in the
|
|
// container with the value above, we need to use the fx.Annotate function with
|
|
// the fx.As annotation.
|
|
//
|
|
// fx.Replace(
|
|
// fx.Annotate(os.Stderr, fx.As(new(io.Writer)))
|
|
// )
|
|
func Replace(values ...interface{}) Option {
|
|
decorators := make([]interface{}, len(values)) // one function per value
|
|
types := make([]reflect.Type, len(values))
|
|
for i, value := range values {
|
|
switch value := value.(type) {
|
|
case annotated:
|
|
var typ reflect.Type
|
|
value.Target, typ = newReplaceDecorator(value.Target)
|
|
decorators[i] = value
|
|
types[i] = typ
|
|
default:
|
|
decorators[i], types[i] = newReplaceDecorator(value)
|
|
}
|
|
}
|
|
|
|
return replaceOption{
|
|
Targets: decorators,
|
|
Types: types,
|
|
Stack: fxreflect.CallerStack(1, 0),
|
|
}
|
|
}
|
|
|
|
type replaceOption struct {
|
|
Targets []interface{}
|
|
Types []reflect.Type // type of value produced by constructor[i]
|
|
Stack fxreflect.Stack
|
|
}
|
|
|
|
func (o replaceOption) apply(m *module) {
|
|
for _, target := range o.Targets {
|
|
m.decorators = append(m.decorators, decorator{
|
|
Target: target,
|
|
Stack: o.Stack,
|
|
IsReplace: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
func (o replaceOption) String() string {
|
|
items := make([]string, 0, len(o.Targets))
|
|
for _, typ := range o.Types {
|
|
items = append(items, typ.String())
|
|
}
|
|
return fmt.Sprintf("fx.Replace(%s)", strings.Join(items, ", "))
|
|
}
|
|
|
|
// Returns a function that takes no parameters, and returns the given value.
|
|
func newReplaceDecorator(value interface{}) (interface{}, reflect.Type) {
|
|
switch value.(type) {
|
|
case nil:
|
|
panic("untyped nil passed to fx.Replace")
|
|
case error:
|
|
panic("error value passed to fx.Replace")
|
|
}
|
|
|
|
typ := reflect.TypeOf(value)
|
|
returnTypes := []reflect.Type{typ}
|
|
returnValues := []reflect.Value{reflect.ValueOf(value)}
|
|
|
|
ft := reflect.FuncOf([]reflect.Type{}, returnTypes, false)
|
|
fv := reflect.MakeFunc(ft, func([]reflect.Value) []reflect.Value {
|
|
return returnValues
|
|
})
|
|
|
|
return fv.Interface(), typ
|
|
}
|