import (
"context"
"flag"
"fmt"
"time"
)
const (
FIZZBUZZ = iota
FIZZ
BUZZ
NOTFIZZBUZZ
)
type msg struct {
Q int
A int
}
type ring struct {
next *ring
f ringer
}
type ringer interface {
Do(ctx context.Context, out chan<- msg) error
}
type fizzbuzz struct{}
func (fizzbuzz) Do(ctx context.Context, out chan<- msg) error {
return do(ctx, 15, FIZZBUZZ, out)
}
type fizz struct{}
func (f fizz) Do(ctx context.Context, out chan<- msg) error {
return do(ctx, 3, FIZZ, out)
}
type buzz struct{}
func (b buzz) Do(ctx context.Context, out chan<- msg) error {
return do(ctx, 5, BUZZ, out)
}
func do(ctx context.Context, d, a int, out chan<- msg) error {
n := ctx.Value("n")
if n == nil {
return fmt.Errorf("fizzbuzz-request-value is empty")
}
if n.(int)%d == 0 {
select {
case out <- msg{Q: n.(int), A: a}:
case <-ctx.Done():
}
}
return nil
}
type notFizzbuzz struct{}
func (nfb notFizzbuzz) Do(ctx context.Context, out chan<- msg) error {
n := ctx.Value("n")
if n == nil {
return fmt.Errorf("fizzbuzz-request-value is empty")
}
if n.(int)%3 != 0 && n.(int)%5 != 0 {
select {
case out <- msg{Q: n.(int), A: NOTFIZZBUZZ}:
case <-ctx.Done():
}
}
return nil
}
func set() (*ring, int) {
rings := []ring{
{nil, fizzbuzz{}},
{nil, fizz{}},
{nil, buzz{}},
{nil, notFizzbuzz{}},
}
for i := 0; i < len(rings); i++ {
j := i + 1
if j >= len(rings) {
j = 0
}
rings[i].next = &rings[j]
}
return &rings[0], cap(rings)
}
var req chan int = make(chan int)
func circle(cur *ring, m int) {
ctx := context.Background()
for {
i := <-req
ctx := context.WithValue(ctx, "n", i)
go func() {
ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
res := make(chan msg)
go circleline(ctx, cur, m, res)
fmt.Println(recv(ctx, res))
}()
}
}
func circleline(ctx context.Context, cur *ring, m int, out chan<- msg) {
for ; m > 0; m-- {
go cur.f.Do(ctx, out)
cur = cur.next
}
}
func recv(ctx context.Context, res <-chan msg) (buf struct {
Q int
A [4]string
T string
}) {
for {
select {
case x := <-res:
defer func() { buf.Q = x.Q }()
switch x.A {
case FIZZBUZZ:
defer func() { buf.A[FIZZBUZZ] = "fizzbuzz" }()
return
case FIZZ:
defer func() { buf.A[FIZZ] = "fizz" }()
case BUZZ:
defer func() { buf.A[BUZZ] = "buzz" }()
case NOTFIZZBUZZ:
defer func() { buf.A[NOTFIZZBUZZ] = "-" }()
return
}
case <-ctx.Done():
defer func() { buf.T = time.Now().Format(time.StampNano) }()
return
}
}
}
var max = flag.Int("m", 100000, "send numbers")
func main() {
flag.Parse()
term, n := set()
go circle(term, n)
for i := 0; i < *max; i++ {
req <- i
}
time.Sleep(time.Second)
}