diff options
Diffstat (limited to 'service/injector.go')
-rw-r--r-- | service/injector.go | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/service/injector.go b/service/injector.go new file mode 100644 index 00000000..e7dfaa0b --- /dev/null +++ b/service/injector.go @@ -0,0 +1,112 @@ +package service + +import ( + "reflect" + "fmt" +) + +const initMethod = "Init" + +var noConfig = fmt.Errorf("no config has been provided") + +// calls Init method with automatically resolved arguments. +func initService(s interface{}, cfg Config, c *container) (bool, error) { + r := reflect.TypeOf(s) + + m, ok := r.MethodByName(initMethod) + if !ok { + // no Init method is presented, assuming service does not need + // initialization. + return false, nil + } + + if err := verifySignature(m); err != nil { + return false, err + } + + // hydrating + values, err := injectValues(m, s, cfg, c) + if err != nil { + return false, err + } + + // initiating service + out := m.Func.Call(values) + + if out[1].IsNil() { + return out[0].Bool(), nil + } + + return out[0].Bool(), out[1].Interface().(error) +} + +// injectValues returns slice of call arguments for service Init method. +func injectValues(m reflect.Method, s interface{}, cfg Config, c *container) (values []reflect.Value, err error) { + for i := 0; i < m.Type.NumIn(); i ++ { + v := m.Type.In(i) + + switch { + case v.ConvertibleTo(reflect.ValueOf(s).Type()): // service itself + values = append(values, reflect.ValueOf(s)) + + case v.Implements(reflect.TypeOf((*HydrateConfig)(nil)).Elem()): // automatically configured config + if cfg == nil { + // todo: generic value + return nil, noConfig + } + + sc := reflect.New(v.Elem()) + if err := sc.Interface().(HydrateConfig).Hydrate(cfg); err != nil { + return nil, err + } + + values = append(values, sc) + + case v.Implements(reflect.TypeOf((*Config)(nil)).Elem()): // config section + if cfg == nil { + // todo: generic value + return nil, noConfig + } + values = append(values, reflect.ValueOf(cfg)) + + case v.Implements(reflect.TypeOf((*Container)(nil)).Elem()): // container + values = append(values, reflect.ValueOf(c)) + + default: + found := false + + // looking for the service candidate + for _, e := range c.services { + if v.ConvertibleTo(reflect.ValueOf(e.svc).Type()) { + found = true + values = append(values, reflect.ValueOf(e.svc)) + break + } + } + + if !found { + // placeholder (make sure to check inside the method) + values = append(values, reflect.New(v).Elem()) + } + } + } + + return +} + +// verifySignature checks if Init method has valid signature +func verifySignature(m reflect.Method) error { + if m.Type.NumOut() != 2 { + return fmt.Errorf("method Init must have exact 2 return values") + } + + if m.Type.Out(0).Kind() != reflect.Bool { + return fmt.Errorf("first return value of Init method must be bool type") + } + + if !m.Type.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { + return fmt.Errorf("second return value of Init method value must be error type") + } + + return nil +} |