codegangsta/inject

support the variadic function

Opened this issue · 1 comments

Usage like this:

package main

import (
    "fmt"
    "github.com/codegangsta/inject"
)

type SliceInterface interface{}

func Handler1(s string, si ...SliceInterface) {
    fmt.Println("Handler1", s, si)
}

func Handler2(s string, si ...interface{}) {
    fmt.Println("Handler2", s, si)
}

func Handler3(s string, si ...int) {
    fmt.Println("Handler3", s, si)
}

func main() {
    s := "foo"
    si := []SliceInterface{s}
    i := []interface{}{s}
    sint := []int{1, 2, 3}

    ij := inject.New()
    ij.Map(s)
    fmt.Println(ij.Invoke(Handler1))
    fmt.Println(ij.Invoke(Handler2))
    fmt.Println(ij.Invoke(Handler3))

    ij.Map(si).Map(i).Map(sint)
    fmt.Println(ij.Invoke(Handler1))
    fmt.Println(ij.Invoke(Handler2))
    fmt.Println(ij.Invoke(Handler3))
}

I was thinking the code would look something like this:

func InterfaceOf(value interface{}) reflect.Type {
    t := reflect.TypeOf(value)

    for t.Kind() == reflect.Ptr {
        t = t.Elem()
    }

    if t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface {
        return t
    }

    if t.Kind() != reflect.Interface {
        panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil) but got " + t.Kind().String())
    }

    return t
}

func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
    t := reflect.TypeOf(f)

    var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
    for i := 0; i < t.NumIn(); i++ {
        argType := t.In(i)
        val := inj.Get(argType)
        if val.IsValid() {
            in[i] = val
            continue
        }
        if !val.IsValid() && t.IsVariadic() && i == t.NumIn()-1 {
            in[i] = reflect.New(argType).Elem()
            break
        }
        return nil, fmt.Errorf("Value not found for type %v", argType)
    }
    if t.IsVariadic() && len(in) == t.NumIn() {
        return reflect.ValueOf(f).CallSlice(in), nil
    }
    return reflect.ValueOf(f).Call(in), nil
}

I'm down with this idea.