Skip to content

Commit c9b24f8

Browse files
kkHAIKErossb83tchung1118sywhang
authored
Add fx.From annotation (#990)
Adds fx.From annotation to allow injecting struct ptr types for interface dependencies which they implement. Co-authored-by: rossb83 <[email protected]> Co-authored-by: Taiwon Chung <[email protected]> Co-authored-by: Sung Yoon Whang <[email protected]>
1 parent dfa2060 commit c9b24f8

File tree

2 files changed

+523
-0
lines changed

2 files changed

+523
-0
lines changed

annotated.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,12 +1176,193 @@ func extractResultFields(types []reflect.Type) ([]reflect.StructField, func(int,
11761176
}
11771177
}
11781178

1179+
type fromAnnotation struct {
1180+
targets []interface{}
1181+
types []reflect.Type
1182+
}
1183+
1184+
var _ Annotation = (*fromAnnotation)(nil)
1185+
1186+
// From is an [Annotation] that annotates the parameter(s) for a function (i.e. a
1187+
// constructor) to be accepted from other provided types. It is analogous to the
1188+
// [As] for parameter types to the constructor.
1189+
//
1190+
// For example,
1191+
//
1192+
// type Runner interface { Run() }
1193+
// func NewFooRunner() *FooRunner // implements Runner
1194+
// func NewRunnerWrap(r Runner) *RunnerWrap
1195+
//
1196+
// fx.Provide(
1197+
// fx.Annotate(
1198+
// NewRunnerWrap,
1199+
// fx.From(new(*FooRunner)),
1200+
// ),
1201+
// )
1202+
//
1203+
// Is equivalent to,
1204+
//
1205+
// fx.Provide(func(r *FooRunner) *RunnerWrap {
1206+
// // need *FooRunner instead of Runner
1207+
// return NewRunnerWrap(r)
1208+
// })
1209+
//
1210+
// When the annotated function takes in multiple parameters, each type gets
1211+
// mapped to corresponding positional parameter of the annotated function
1212+
//
1213+
// For example,
1214+
//
1215+
// func NewBarRunner() *BarRunner // implements Runner
1216+
// func NewRunnerWraps(r1 Runner, r2 Runner) *RunnerWraps
1217+
//
1218+
// fx.Provide(
1219+
// fx.Annotate(
1220+
// NewRunnerWraps,
1221+
// fx.From(new(*FooRunner), new(*BarRunner)),
1222+
// ),
1223+
// )
1224+
//
1225+
// Is equivalent to,
1226+
//
1227+
// fx.Provide(func(r1 *FooRunner, r2 *BarRunner) *RunnerWraps {
1228+
// return NewRunnerWraps(r1, r2)
1229+
// })
1230+
func From(interfaces ...interface{}) Annotation {
1231+
return &fromAnnotation{targets: interfaces}
1232+
}
1233+
1234+
func (fr *fromAnnotation) apply(ann *annotated) error {
1235+
if len(ann.From) > 0 {
1236+
return errors.New("cannot apply more than one line of From")
1237+
}
1238+
ft := reflect.TypeOf(ann.Target)
1239+
fr.types = make([]reflect.Type, len(fr.targets))
1240+
for i, typ := range fr.targets {
1241+
if ft.IsVariadic() && i == ft.NumIn()-1 {
1242+
return errors.New("fx.From: cannot annotate a variadic argument")
1243+
}
1244+
t := reflect.TypeOf(typ)
1245+
if t == nil || t.Kind() != reflect.Ptr {
1246+
return fmt.Errorf("fx.From: argument must be a pointer to a type that implements some interface: got %v", t)
1247+
}
1248+
fr.types[i] = t.Elem()
1249+
}
1250+
ann.From = fr.types
1251+
return nil
1252+
}
1253+
1254+
// build builds and returns a constructor after applying a From annotation
1255+
func (fr *fromAnnotation) build(ann *annotated) (interface{}, error) {
1256+
paramTypes, remap, err := fr.parameters(ann)
1257+
if err != nil {
1258+
return nil, err
1259+
}
1260+
resultTypes, _ := ann.currentResultTypes()
1261+
1262+
origFn := reflect.ValueOf(ann.Target)
1263+
newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
1264+
newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
1265+
args = remap(args)
1266+
return origFn.Call(args)
1267+
})
1268+
return newFn.Interface(), nil
1269+
}
1270+
1271+
// parameters returns the type for the parameters of the annotated function,
1272+
// and a function that maps the arguments of the annotated function
1273+
// back to the arguments of the target function.
1274+
func (fr *fromAnnotation) parameters(ann *annotated) (
1275+
types []reflect.Type,
1276+
remap func([]reflect.Value) []reflect.Value,
1277+
err error,
1278+
) {
1279+
ft := reflect.TypeOf(ann.Target)
1280+
types = make([]reflect.Type, ft.NumIn())
1281+
for i := 0; i < ft.NumIn(); i++ {
1282+
types[i] = ft.In(i)
1283+
}
1284+
1285+
// No parameter annotations. Return the original types
1286+
// and an identity function.
1287+
if len(fr.targets) == 0 {
1288+
return types, func(args []reflect.Value) []reflect.Value {
1289+
return args
1290+
}, nil
1291+
}
1292+
1293+
// Turn parameters into an fx.In struct.
1294+
inFields := []reflect.StructField{_inAnnotationField}
1295+
1296+
// The following situations may occur:
1297+
// 1. there was a variadic argument, so it was pre-transformed.
1298+
// 2. another parameter annotation was transformed (ex: ParamTags).
1299+
// so need to visit fields of the fx.In struct.
1300+
if len(types) > 0 && isIn(types[0]) {
1301+
paramType := types[0]
1302+
1303+
for i := 1; i < paramType.NumField(); i++ {
1304+
origField := paramType.Field(i)
1305+
field := reflect.StructField{
1306+
Name: origField.Name,
1307+
Type: origField.Type,
1308+
Tag: origField.Tag,
1309+
}
1310+
if i-1 < len(fr.types) {
1311+
t := fr.types[i-1]
1312+
if !t.Implements(field.Type) {
1313+
return nil, nil, fmt.Errorf("invalid fx.From: %v does not implement %v", t, field.Type)
1314+
}
1315+
field.Type = t
1316+
}
1317+
1318+
inFields = append(inFields, field)
1319+
}
1320+
1321+
types = []reflect.Type{reflect.StructOf(inFields)}
1322+
return types, func(args []reflect.Value) []reflect.Value {
1323+
param := args[0]
1324+
args[0] = reflect.New(paramType).Elem()
1325+
for i := 1; i < paramType.NumField(); i++ {
1326+
args[0].Field(i).Set(param.Field(i))
1327+
}
1328+
return args
1329+
}, nil
1330+
}
1331+
1332+
for i, t := range types {
1333+
field := reflect.StructField{
1334+
Name: fmt.Sprintf("Field%d", i),
1335+
Type: t,
1336+
}
1337+
if i < len(fr.types) {
1338+
t := fr.types[i]
1339+
if !t.Implements(field.Type) {
1340+
return nil, nil, fmt.Errorf("invalid fx.From: %v does not implement %v", t, field.Type)
1341+
}
1342+
field.Type = t
1343+
}
1344+
1345+
inFields = append(inFields, field)
1346+
}
1347+
1348+
types = []reflect.Type{reflect.StructOf(inFields)}
1349+
return types, func(args []reflect.Value) []reflect.Value {
1350+
params := args[0]
1351+
args = args[:0]
1352+
for i := 0; i < ft.NumIn(); i++ {
1353+
args = append(args, params.Field(i+1))
1354+
}
1355+
return args
1356+
}, nil
1357+
}
1358+
11791359
type annotated struct {
11801360
Target interface{}
11811361
Annotations []Annotation
11821362
ParamTags []string
11831363
ResultTags []string
11841364
As [][]reflect.Type
1365+
From []reflect.Type
11851366
FuncPtr uintptr
11861367
Hooks []*lifecycleHookAnnotation
11871368
// container is used to build private scopes for lifecycle hook functions
@@ -1202,6 +1383,9 @@ func (ann annotated) String() string {
12021383
if as := ann.As; len(as) > 0 {
12031384
fmt.Fprintf(&sb, ", fx.As(%v)", as)
12041385
}
1386+
if from := ann.From; len(from) > 0 {
1387+
fmt.Fprintf(&sb, ", fx.From(%v)", from)
1388+
}
12051389
return sb.String()
12061390
}
12071391

0 commit comments

Comments
 (0)