// +build ignore

package main

import (
	"os"
	"text/template"
)

func main() {
	Run(metricsTemplate, metricsTestTemplate, os.Args...)
}

var metricsTemplate = template.Must(template.New("").Parse(`package {{.Package}}

// go generate {{.Args}}
// GENERATED CODE FOLLOWS; DO NOT EDIT.

import (
	"context"
{{if .Type "ET"}}
	"strings"
{{end}}
	"github.com/mesos/mesos-go/api/v1/lib/extras/metrics"
{{range .Imports}}
	{{ printf "%q" . -}}
{{end}}
)

{{.RequireType "E" -}}

// Labeler generates a set of strings that should be associated with metrics that are generated for the given event.
type Labeler func(ctx context.Context, e {{.Type "E"}}) []string

{{if .Type "ET" -}}
var defaultLabels = func() map[{{.Type "ET"}}][]string {
	m := make(map[{{.Type "ET"}}][]string)
	for k, v := range {{.Type "ET"}}_name {
		m[{{.Type "ET"}}(k)] = []string{strings.ToLower(v)}
	}
	return m
}()

func defaultLabeler(ctx context.Context, e {{.Type "E"}}) []string {
	return defaultLabels[e.GetType()]
}

{{end -}}
// Metrics generates a Rule that invokes the given harness for each event, using the labels generated by the Labeler.
// Panics if harness {{if .Type "ET"}}or labeler {{end}}is nil.
func Metrics(harness metrics.Harness, labeler Labeler) Rule {
	if harness == nil {
		panic("harness is a required parameter")
	}
	{{if .Type "ET" -}}
	if labeler == nil {
		labeler = defaultLabeler
	}
	{{else -}}
	if labeler == nil {
		panic("labeler is a required parameter")
	}
	{{end -}}
	return func(ctx context.Context, e {{.Type "E"}}, {{.Arg "Z" "z," -}} err error, ch Chain) (context.Context, {{.Type "E"}}, {{.Arg "Z" "," -}} error) {
		labels := labeler(ctx, e)
		harness(func() error {
			ctx, e, {{.Ref "Z" "z," -}} err = ch(ctx, e, {{.Ref "Z" "z," -}} err)
			return err
		}, labels...)
		return ctx, e, {{.Ref "Z" "z," -}} err
	}
}
`))

var metricsTestTemplate = template.Must(template.New("").Parse(`package {{.Package}}

// go generate {{.Args}}
// GENERATED CODE FOLLOWS; DO NOT EDIT.

import (
	"context"
	"errors"
	"reflect"
	"testing"
{{range .Imports}}
	{{ printf "%q" . -}}
{{end}}
)

{{.RequireType "E" -}}
{{.RequirePrototype "E" -}}
{{.RequirePrototype "Z" -}}
func TestMetrics(t *testing.T) {
	var (
		i   int
		ctx = context.Background()
		p   = {{.Prototype "E"}}
		a   = errors.New("a")
		h   = func(f func() error, _ ...string) error {
			i++
			return f()
		}
	)
	{{if .Type "Z" -}}
	var zp = {{.Prototype "Z"}}
	{{end -}}
	for ti, tc := range []struct {
		ctx context.Context
		e   {{.Type "E"}}
		{{if .Type "Z"}}
		{{- .Arg "Z" "z  "}}
		{{end -}}
		err error
	}{
		{ctx, p, {{.Ref "Z" "zp," -}} a},
		{ctx, p, {{.Ref "Z" "zp," -}} nil},
		{{if .Type "Z"}}
		{ctx, p, {{.Ref "Z" "nil," -}} a},
		{{end -}}
		{ctx, nil, {{.Ref "Z" "zp," -}} a},
	} {
		for ri, r := range []Rule{
			Metrics(h, nil), // default labeler
			Metrics(h, func(_ context.Context, _ {{.Type "E"}}) []string { return nil }), // custom labeler
		} {
			c, e, {{.Ref "Z" "z," -}} err := r.Eval(tc.ctx, tc.e, {{.Ref "Z" "tc.z," -}} tc.err, ChainIdentity)
			if !reflect.DeepEqual(c, tc.ctx) {
				t.Errorf("test case %d: expected context %q instead of %q", ti, tc.ctx, c)
			}
			if !reflect.DeepEqual(e, tc.e) {
				t.Errorf("test case %d: expected event %q instead of %q", ti, tc.e, e)
			}
			{{if .Type "Z" -}}
			if !reflect.DeepEqual(z, tc.z) {
				t.Errorf("expected return object %q instead of %q", z, tc.z)
			}
			{{end -}}
			if !reflect.DeepEqual(err, tc.err) {
				t.Errorf("test case %d: expected error %q instead of %q", ti, tc.err, err)
			}
			if y := (ti * 2) + ri + 1; y != i {
				t.Errorf("test case %d: expected count %q instead of %q", ti, y, i)
			}
		}
	}
	func() {
		defer func() {
			if x := recover(); x != nil {
				t.Log("intercepted expected panic", x)
			}
		}()
		_ = Metrics(nil, nil)
		t.Fatalf("expected a panic because nil harness is not allowed")
	}()
}
`))
