package yetAnotherNewFunc

import (
    "fmt"
    "reflect"
    "sync"
    "testing"
)

type Foo struct{}

func NewFoo() *Foo {
    return &Foo{}
}

func YieldFoo() Foo {
    return Foo{}
}

func SpawnFoo(F func(interface{}) error) chan Foo {
    chFoo := make(chan Foo)
    go func() {
        for {
            x := <-chFoo
            F(x)
        }
    }()
    return chFoo
}

func NSpawnFoo(N int, F func(interface{}) error) []chan Foo {
    NFoo := make([]chan Foo, 0, N)
    for i := 0; i < N; i++ {
        NFoo = append(NFoo, SpawnFoo(F))
    }
    return NFoo
}

func IsFoo(x interface{}) (ret bool) {
    return reflect.TypeOf(Foo{}) == reflect.ValueOf(x).Type()
}

func ExampleFoo() {

    n := 5
    var wg sync.WaitGroup
    wg.Add(n)

    f := func(x interface{}) error { fmt.Printf("%T => %v\n", x, IsFoo(x)); wg.Done(); return nil }
    NSF := NSpawnFoo(n, f)

    for _, ch := range NSF {
        ch <- YieldFoo()
    }
    wg.Wait()

    // Output:
    // yetAnotherNewFunc.Foo => true
    // yetAnotherNewFunc.Foo => true
    // yetAnotherNewFunc.Foo => true
    // yetAnotherNewFunc.Foo => true
    // yetAnotherNewFunc.Foo => true
}

func (f Foo) Bar() {}

type FooBar interface {
    Bar()
}

func MustFooBar(x interface{}) FooBar {
    return x.(FooBar)
}

func TestMustFooBar(t *testing.T) {
    caseTable := []struct {
        Case interface{}
        Want bool
    }{
        {Foo{}, true},
        {struct{}{}, false},
    }

    for _, tc := range caseTable {
        defer func() {
            if e := recover(); e != nil && tc.Want {
                t.Errorf("MustFooBar(%v) panic, %v", tc.Case, e)
            }
        }()
        _ = MustFooBar(tc.Case)
    }
}