157 lines
4.0 KiB
Go
157 lines
4.0 KiB
Go
|
// Copyright (c) 2019 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"
|
||
|
"unicode"
|
||
|
"unicode/utf8"
|
||
|
)
|
||
|
|
||
|
var _typeOfIn = reflect.TypeOf(In{})
|
||
|
|
||
|
// Extract fills the given struct with values from the dependency injection
|
||
|
// container on application initialization. The target MUST be a pointer to a
|
||
|
// struct. Only exported fields will be filled.
|
||
|
//
|
||
|
// Deprecated: Use Populate instead.
|
||
|
func Extract(target interface{}) Option {
|
||
|
v := reflect.ValueOf(target)
|
||
|
|
||
|
if t := v.Type(); t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
|
||
|
return Error(fmt.Errorf("Extract expected a pointer to a struct, got a %v", t))
|
||
|
}
|
||
|
|
||
|
v = v.Elem()
|
||
|
t := v.Type()
|
||
|
|
||
|
// We generate a function which accepts a single fx.In struct as an
|
||
|
// argument. This struct contains all exported fields of the target
|
||
|
// struct.
|
||
|
|
||
|
// Fields of the generated fx.In struct.
|
||
|
fields := make([]reflect.StructField, 0, t.NumField()+1)
|
||
|
|
||
|
// Anonymous dig.In field.
|
||
|
fields = append(fields, reflect.StructField{
|
||
|
Name: _typeOfIn.Name(),
|
||
|
Anonymous: true,
|
||
|
Type: _typeOfIn,
|
||
|
})
|
||
|
|
||
|
// List of values in the target struct aligned with the fields of the
|
||
|
// generated struct.
|
||
|
//
|
||
|
// So for example, if the target is,
|
||
|
//
|
||
|
// var target struct {
|
||
|
// Foo io.Reader
|
||
|
// bar []byte
|
||
|
// Baz io.Writer
|
||
|
// }
|
||
|
//
|
||
|
// The generated struct has the shape,
|
||
|
//
|
||
|
// struct {
|
||
|
// fx.In
|
||
|
//
|
||
|
// F0 io.Reader
|
||
|
// F2 io.Writer
|
||
|
// }
|
||
|
//
|
||
|
// And `targets` is,
|
||
|
//
|
||
|
// [
|
||
|
// target.Field(0), // Foo io.Reader
|
||
|
// target.Field(2), // Baz io.Writer
|
||
|
// ]
|
||
|
//
|
||
|
// As we iterate through the fields of the generated struct, we can copy
|
||
|
// the value into the corresponding value in the targets list.
|
||
|
targets := make([]reflect.Value, 0, t.NumField())
|
||
|
|
||
|
for i := 0; i < t.NumField(); i++ {
|
||
|
f := t.Field(i)
|
||
|
|
||
|
// Skip unexported fields.
|
||
|
if f.Anonymous {
|
||
|
// If embedded, StructField.PkgPath is not a reliable indicator of
|
||
|
// whether the field is exported. See
|
||
|
// https://github.com/golang/go/issues/21122
|
||
|
|
||
|
t := f.Type
|
||
|
if t.Kind() == reflect.Ptr {
|
||
|
t = t.Elem()
|
||
|
}
|
||
|
|
||
|
if !isExported(t.Name()) {
|
||
|
continue
|
||
|
}
|
||
|
} else if f.PkgPath != "" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// We don't copy over names or embedded semantics.
|
||
|
fields = append(fields, reflect.StructField{
|
||
|
Name: fmt.Sprintf("F%d", i),
|
||
|
Type: f.Type,
|
||
|
Tag: f.Tag,
|
||
|
})
|
||
|
targets = append(targets, v.Field(i))
|
||
|
}
|
||
|
|
||
|
// Equivalent to,
|
||
|
//
|
||
|
// func(r struct {
|
||
|
// fx.In
|
||
|
//
|
||
|
// F1 Foo
|
||
|
// F2 Bar
|
||
|
// }) {
|
||
|
// target.Foo = r.F1
|
||
|
// target.Bar = r.F2
|
||
|
// }
|
||
|
|
||
|
fn := reflect.MakeFunc(
|
||
|
reflect.FuncOf(
|
||
|
[]reflect.Type{reflect.StructOf(fields)},
|
||
|
nil, /* results */
|
||
|
false, /* variadic */
|
||
|
),
|
||
|
func(args []reflect.Value) []reflect.Value {
|
||
|
result := args[0]
|
||
|
for i := 1; i < result.NumField(); i++ {
|
||
|
targets[i-1].Set(result.Field(i))
|
||
|
}
|
||
|
return nil
|
||
|
},
|
||
|
)
|
||
|
|
||
|
return Invoke(fn.Interface())
|
||
|
}
|
||
|
|
||
|
// isExported reports whether the identifier is exported.
|
||
|
func isExported(id string) bool {
|
||
|
r, _ := utf8.DecodeRuneInString(id)
|
||
|
return unicode.IsUpper(r)
|
||
|
}
|