<commit_before>package hamt_test

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/lleo/go-hamt-functional/hamt32"
	"github.com/lleo/go-hamt/key"
	"github.com/lleo/go-hamt/stringkey"
	"github.com/pkg/errors"

	"github.com/lleo/stringutil"
)

type StrVal struct {
	Str string
	Val int
}

//var numKvs int = 512 * 1024
//var numKvs int = 1 * 1024 * 1024
//var numKvs int = 2 * 1024 * 1024
//var numKvs int = 3 * 1024 * 1024
var numKvs int = (3 * 1024 * 1024) + (4 * 1024) // between 3m+2k & 3m+4k

var KVS []key.KeyVal
var SVS []StrVal

var LookupMap map[string]int
var DeleteMap map[string]int

var LookupHamt32 hamt32.Hamt
var DeleteHamt32 hamt32.Hamt

//var LookupHamt64 hamt64.Hamt
//var DeleteHamt64 hamt64.Hamt

var Inc = stringutil.Lower.Inc

var StartTime = make(map[string]time.Time)
var RunTime = make(map[string]time.Duration)

const (
	hybrid   = 0
	fullonly = 1
	componly = 2
)

var cfgStr = []string{"hybrid", "fullonly", "componly"}
var cfgMap = map[string]int{"hybrid": hybrid, "fullonly": fullonly, "componly": componly}

var TYP int
var CFG string

func TestMain(m *testing.M) {
	var fullonlyOpt, componlyOpt, hybridOpt, allOpt bool
	flag.BoolVar(&fullonlyOpt, "F", false, "Use full tables only and exclude C and H Options.")
	flag.BoolVar(&componlyOpt, "C", false, "Use compressed tables only and exclude F and H Options.")
	flag.BoolVar(&hybridOpt, "H", false, "Use compressed tables initially and exclude F and C Options.")
	flag.BoolVar(&allOpt, "A", false, "Run all Tests w/ Options set to hamt32.FullTablesOnly, hamt32.CompTablesOnly, and hamt32.HybridTables; in that order.")

	flag.Parse()

	// If allOpt flag set, ignore fullonlyOpt, componlyOpt, and hybridOpt.
	if !allOpt {

		// only one flag may be set between fullonlyOpt, componlyOpt, and hybridOpt
		if (fullonlyOpt && (componlyOpt || hybridOpt)) ||
			(componlyOpt && (fullonlyOpt || hybridOpt)) ||
			(hybridOpt && (componlyOpt || fullonlyOpt)) {
			flag.PrintDefaults()
			os.Exit(1)
		}
	}

	// If no flags given, run all tests.
	if !(allOpt || fullonlyOpt || componlyOpt || hybridOpt) {
		allOpt = true
		//fullonlyOpt = true
	}

	log.SetFlags(log.Lshortfile)

	var logfile, err = os.Create("test.log")
	if err != nil {
		log.Fatal(errors.Wrap(err, "failed to os.Create(\"test.log\")"))
	}
	defer logfile.Close()

	log.SetOutput(logfile)

	// SETUP
	log.Println("TestMain: and so it begins...")

	KVS, SVS = buildKeyVals(numKvs)

	LookupMap, DeleteMap = buildMaps(numKvs)

	// execute
	var xit int

	if allOpt {
		//for _, TYP = range []int{fullonly, componly, hybrid} {
		for _, TYP = range []int{hybrid, componly, fullonly} {
			CFG = cfgStr[TYP]
			var name = "all tests: " + CFG
			StartTime[name] = time.Now()

			log.Printf("allOpt: for type = %s\n", CFG)

			LookupHamt32 = createHamt32("LookupHamt32", TYP)
			//DeleteHamt32 = createHamt32("DeleteHamt32", TYP)

			log.Println(LookupHamt32.LongString(""))

			fmt.Println("Running all tests:", CFG)
			xit = m.Run()
			if xit != 0 {
				os.Exit(xit)
			}

			RunTime[name] = time.Since(StartTime[name])
			fmt.Printf("RunTime[%q] = %v\n", name, RunTime[name])
		}
	} else {
		var msg string
		var name string
		if hybridOpt {
			TYP = hybrid
			CFG = cfgStr[TYP]
			msg = fmt.Sprintf("hybridOpt: for type = %s", CFG)
			name = "one test: " + CFG
		} else if fullonlyOpt {
			TYP = fullonly
			CFG = cfgStr[TYP]
			msg = fmt.Sprintf("fullonlyOpt: for type = %s", CFG)
			name = "one test: " + CFG
		} else /* if componlyOpt */ {
			TYP = componly
			CFG = cfgStr[TYP]
			msg = fmt.Sprintf("componlyOpt: for type = %s", CFG)
			name = "one test: " + CFG
		}

		StartTime[name] = time.Now()

		setLibrary(TYP)

		log.Println(msg)
		fmt.Println(msg)

		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt32.GradeTables, hamt32.FullTableInit)

		LookupHamt32 = createHamt32("LookupHamt32", TYP)
		//DeleteHamt32 = createHamt32("DeleteHamt32", TYP)

		xit = m.Run()

		RunTime[name] = time.Since(StartTime[name])
		fmt.Printf("RunTime[%q] = %v\n", name, RunTime[name])
	}

	log.Println("\n", RunTimes())
	log.Println("TestMain: the end.")

	// TEARDOWN

	os.Exit(xit)
}

func RunTimes() string {
	var s = ""

	s += "Key                                                               Val\n"
	s += "=================================================================+==========\n"

	for key, val := range RunTime {
		s += fmt.Sprintf("%-65s %s\n", key, val)
	}
	return s
}

var initializeNum int

func setLibrary(typ int) {
	switch typ {
	case hybrid:
		hamt32.GradeTables = true
		hamt32.FullTableInit = false
		//hamt64.GradeTables = true
		//hamt64.FullTableInit = false
	case fullonly:
		hamt32.GradeTables = false
		hamt32.FullTableInit = true
		//hamt64.GradeTables = false
		//hamt64.FullTableInit = true
	case componly:
		hamt32.GradeTables = false
		hamt32.FullTableInit = false
		//hamt64.GradeTables = false
		//hamt64.FullTableInit = false
	default:
		panic(fmt.Sprintf("unknown type %d", typ))
	}
}

func createHamt32(hname string, typ int) hamt32.Hamt {
	var name = "createHamt32:" + hname + ":" + cfgStr[typ]
	setLibrary(typ)
	StartTime[name] = time.Now()

	var h = hamt32.Hamt{}

	for _, kv := range KVS {
		var inserted bool
		h, inserted = h.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to %s.Put(%s, %v)", hname, kv.Key, kv.Val)
		}
	}

	RunTime[name] = time.Since(StartTime[name])

	return h
}

func buildMaps(num int) (lup map[string]int, del map[string]int) {
	var name = "build LookupMap & DeleteMap"
	StartTime[name] = time.Now()

	lup = make(map[string]int, num)
	del = make(map[string]int, num)

	var s = "aaa"
	var v int = 0

	for i := 0; i < num; i++ {
		lup[s] = v
		del[s] = v

		s = Inc(s)
		v++
	}

	RunTime[name] = time.Since(StartTime[name])

	return
}

func buildKeyVals(num int) ([]key.KeyVal, []StrVal) {
	var name = "buildKeyVals"
	StartTime[name] = time.Now()

	var kvs = make([]key.KeyVal, num, num)
	var svs = make([]StrVal, num, num)

	s := "aaa"
	for i := 0; i < num; i++ {
		kvs[i].Key = stringkey.New(s)
		kvs[i].Val = i

		svs[i].Str = s
		svs[i].Val = i

		s = Inc(s)
	}

	RunTime[name] = time.Since(StartTime[name])
	return kvs, svs
}

//First genRandomizedSvs() copies []KeyVal passed in. Then it randomizes that
//copy in-place. Finnally, it returns the randomized copy.
func genRandomizedKvs(kvs []key.KeyVal) []key.KeyVal {
	var name = "genRandomizedKvs"
	StartTime[name] = time.Now()

	var randKvs = make([]key.KeyVal, len(kvs))
	copy(randKvs, kvs)

	//From: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
	var n = len(randKvs) // n is the number of elements
	var limit = n - 1
	for i := 0; i < limit; /* aka i_max = n-2 */ i++ {
		j := n - rand.Intn(i+1) - 1 // i <= j < n
		// j_min = 0   => n - (i_max + 1) - 1 = n - (n-2 + 1) - 1 = n-n+2-1-1 = 0
		// j_max = n-1 => n - 0 - 1 = n - 1
		randKvs[i], randKvs[j] = randKvs[j], randKvs[i]
	}

	RunTime[name] = time.Since(StartTime[name])
	return randKvs
}

//First genRandomizedSvs() copies []StrVal passed in. Then it randomizes that
//copy in-place. Finnally, it returns the randomized copy.
func genRandomizedSvs(svs []StrVal) []StrVal {
	var name = "genRandomizedSvs"
	StartTime[name] = time.Now()

	var randSvs = make([]StrVal, len(svs))
	copy(randSvs, svs)

	//From: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
	var limit = len(randSvs) //n-1
	for i := 0; i < limit; /* aka i_max = n-2 */ i++ {
		j := rand.Intn(i+1) - 1 // i <= j < n; j_min=n-(n-2+1)-1=0; j_max=n-0-1=n-1
		randSvs[i], randSvs[j] = randSvs[j], randSvs[i]
	}

	RunTime[name] = time.Since(StartTime[name])
	return randSvs
}

func BenchmarkMapGet(b *testing.B) {
	var name = "BenchmarkMapGet"
	log.Printf("%s b.N=%d\n", name, b.N)

	var _, ok = LookupMap["aaa"]
	if !ok {
		LookupMap, DeleteMap = buildMaps(numKvs)
	}

	StartTime[name] = time.Now()

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var j = i % numKvs
		var s = SVS[j].Str
		var v = SVS[j].Val
		var val, ok = LookupMap[s]
		if !ok {
			b.Fatalf("LookupMap[%q] does not exist", s)
		}
		if val != v {
			b.Fatalf("LookupMap[%q] != %d", s, KVS[j].Val)
		}
	}

	RunTime[name] = time.Since(StartTime[name])
}

func BenchmarkMapPut(b *testing.B) {
	var name = "BenchmarkMapPut"
	log.Printf("%s b.N=%d\n", name, b.N)
	StartTime[name] = time.Now()
	var m = make(map[string]int)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var j = i % numKvs
		var s = SVS[j].Str
		var v = SVS[j].Val
		m[s] = v
	}

	RunTime[name] = time.Since(StartTime[name])
}

var rebuildDeleteMapNum int

func rebuildDeleteMap(svs []StrVal) {
	var name = fmt.Sprintf("BenchmarkMapPut-%d", rebuildDeleteMapNum)
	rebuildDeleteMapNum++

	StartTime[name] = time.Now()

	for _, sv := range svs {
		var _, ok = DeleteMap[sv.Str]
		if ok {
			break
		}
		//else
		delete(DeleteMap, sv.Str)

		DeleteMap[sv.Str] = sv.Val
	}

	RunTime[name] = time.Since(StartTime[name])
}

func BenchmarkMapDel(b *testing.B) {
	var name = "BenchmarkMapDel"
	log.Printf("%s b.N=%d\n", name, b.N)
	StartTime[name] = time.Now()
	rebuildDeleteMap(SVS)
	RunTime[name] = time.Since(StartTime[name])

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var j = i % numKvs
		var k = SVS[j].Str
		var v = SVS[j].Val

		var val, ok = DeleteMap[k]
		if ok {
			delete(DeleteMap, k)
		} else if val != v {
			b.Fatalf("DeleteMap[%s],%d != %d", k, v, val)
		}

		//b.StopTimer()
		DeleteMap[k] = v
		//b.StartTimer()
	}
}
<commit_msg>* go imports was finding the go-hamt/hamt64 not go-hamt-functional/hamt64 * renamed LookupHamt* to TestHamt* * deleted DeleteHamt* * added a createHamt64() function.<commit_after>package hamt_test

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/lleo/go-hamt-functional/hamt32"
	"github.com/lleo/go-hamt-functional/hamt64"
	"github.com/lleo/go-hamt/key"
	"github.com/lleo/go-hamt/stringkey"
	"github.com/pkg/errors"

	"github.com/lleo/stringutil"
)

type StrVal struct {
	Str string
	Val int
}

//var numKvs int = 512 * 1024
//var numKvs int = 1 * 1024 * 1024
//var numKvs int = 2 * 1024 * 1024
//var numKvs int = 3 * 1024 * 1024
var numKvs int = (3 * 1024 * 1024) + (4 * 1024) // between 3m+2k & 3m+4k

var KVS []key.KeyVal
var SVS []StrVal

var LookupMap map[string]int
var DeleteMap map[string]int

var TestHamt32 hamt32.Hamt
var TestHamt64 hamt64.Hamt

var Inc = stringutil.Lower.Inc

var StartTime = make(map[string]time.Time)
var RunTime = make(map[string]time.Duration)

const (
	hybrid   = 0
	fullonly = 1
	componly = 2
)

var cfgStr = []string{"hybrid", "fullonly", "componly"}
var cfgMap = map[string]int{"hybrid": hybrid, "fullonly": fullonly, "componly": componly}

var TYP int
var CFG string

func TestMain(m *testing.M) {
	var fullonlyOpt, componlyOpt, hybridOpt, allOpt bool
	flag.BoolVar(&fullonlyOpt, "F", false, "Use full tables only and exclude C and H Options.")
	flag.BoolVar(&componlyOpt, "C", false, "Use compressed tables only and exclude F and H Options.")
	flag.BoolVar(&hybridOpt, "H", false, "Use compressed tables initially and exclude F and C Options.")
	flag.BoolVar(&allOpt, "A", false, "Run all Tests w/ Options set to hamt32.FullTablesOnly, hamt32.CompTablesOnly, and hamt32.HybridTables; in that order.")

	flag.Parse()

	// If allOpt flag set, ignore fullonlyOpt, componlyOpt, and hybridOpt.
	if !allOpt {

		// only one flag may be set between fullonlyOpt, componlyOpt, and hybridOpt
		if (fullonlyOpt && (componlyOpt || hybridOpt)) ||
			(componlyOpt && (fullonlyOpt || hybridOpt)) ||
			(hybridOpt && (componlyOpt || fullonlyOpt)) {
			flag.PrintDefaults()
			os.Exit(1)
		}
	}

	// If no flags given, run all tests.
	if !(allOpt || fullonlyOpt || componlyOpt || hybridOpt) {
		allOpt = true
		//fullonlyOpt = true
	}

	log.SetFlags(log.Lshortfile)

	var logfile, err = os.Create("test.log")
	if err != nil {
		log.Fatal(errors.Wrap(err, "failed to os.Create(\"test.log\")"))
	}
	defer logfile.Close()

	log.SetOutput(logfile)

	// SETUP
	log.Println("TestMain: and so it begins...")

	KVS, SVS = buildKeyVals(numKvs)

	LookupMap, DeleteMap = buildMaps(numKvs)

	// execute
	var xit int

	if allOpt {
		//for _, TYP = range []int{fullonly, componly, hybrid} {
		for _, TYP = range []int{hybrid, componly, fullonly} {
			CFG = cfgStr[TYP]
			var name = "all tests: " + CFG
			StartTime[name] = time.Now()

			log.Printf("allOpt: for type = %s\n", CFG)

			TestHamt32 = createHamt32("TestHamt32", TYP)
			TestHamt64 = createHamt64("TestHamt64", TYP)

			log.Println(TestHamt32.LongString(""))

			fmt.Println("Running all tests:", CFG)
			xit = m.Run()
			if xit != 0 {
				os.Exit(xit)
			}

			RunTime[name] = time.Since(StartTime[name])
			fmt.Printf("RunTime[%q] = %v\n", name, RunTime[name])
		}
	} else {
		var msg string
		var name string
		if hybridOpt {
			TYP = hybrid
			CFG = cfgStr[TYP]
			msg = fmt.Sprintf("hybridOpt: for type = %s", CFG)
			name = "one test: " + CFG
		} else if fullonlyOpt {
			TYP = fullonly
			CFG = cfgStr[TYP]
			msg = fmt.Sprintf("fullonlyOpt: for type = %s", CFG)
			name = "one test: " + CFG
		} else /* if componlyOpt */ {
			TYP = componly
			CFG = cfgStr[TYP]
			msg = fmt.Sprintf("componlyOpt: for type = %s", CFG)
			name = "one test: " + CFG
		}

		StartTime[name] = time.Now()

		setLibrary(TYP)

		log.Println(msg)
		fmt.Println(msg)

		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt32.GradeTables, hamt32.FullTableInit)

		TestHamt32 = createHamt32("TestHamt32", TYP)
		TestHamt64 = createHamt64("TestHamt64", TYP)

		xit = m.Run()

		RunTime[name] = time.Since(StartTime[name])
		fmt.Printf("RunTime[%q] = %v\n", name, RunTime[name])
	}

	log.Println("\n", RunTimes())
	log.Println("TestMain: the end.")

	// TEARDOWN

	os.Exit(xit)
}

func RunTimes() string {
	var s = ""

	s += "Key                                                               Val\n"
	s += "=================================================================+==========\n"

	for key, val := range RunTime {
		s += fmt.Sprintf("%-65s %s\n", key, val)
	}
	return s
}

var initializeNum int

func setLibrary(typ int) {
	switch typ {
	case hybrid:
		hamt32.GradeTables = true
		hamt32.FullTableInit = false
		//hamt64.GradeTables = true
		//hamt64.FullTableInit = false
	case fullonly:
		hamt32.GradeTables = false
		hamt32.FullTableInit = true
		//hamt64.GradeTables = false
		//hamt64.FullTableInit = true
	case componly:
		hamt32.GradeTables = false
		hamt32.FullTableInit = false
		//hamt64.GradeTables = false
		//hamt64.FullTableInit = false
	default:
		panic(fmt.Sprintf("unknown type %d", typ))
	}
}

func createHamt32(hname string, typ int) hamt32.Hamt {
	var name = "createHamt32:" + hname + ":" + cfgStr[typ]
	setLibrary(typ)
	StartTime[name] = time.Now()

	var h = hamt32.Hamt{}

	for _, kv := range KVS {
		var inserted bool
		h, inserted = h.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to %s.Put(%s, %v)", hname, kv.Key, kv.Val)
		}
	}

	RunTime[name] = time.Since(StartTime[name])

	return h
}

func createHamt64(hname string, typ int) hamt64.Hamt {
	var name = "createHamt64:" + hname + ":" + cfgStr[typ]
	setLibrary(typ)
	StartTime[name] = time.Now()

	var h = hamt64.Hamt{}

	for _, kv := range KVS {
		var inserted bool
		h, inserted = h.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to %s.Put(%s, %v)", hname, kv.Key, kv.Val)
		}
	}

	RunTime[name] = time.Since(StartTime[name])

	return h
}

func buildMaps(num int) (lup map[string]int, del map[string]int) {
	var name = "build LookupMap & DeleteMap"
	StartTime[name] = time.Now()

	lup = make(map[string]int, num)
	del = make(map[string]int, num)

	var s = "aaa"
	var v int = 0

	for i := 0; i < num; i++ {
		lup[s] = v
		del[s] = v

		s = Inc(s)
		v++
	}

	RunTime[name] = time.Since(StartTime[name])

	return
}

func buildKeyVals(num int) ([]key.KeyVal, []StrVal) {
	var name = "buildKeyVals"
	StartTime[name] = time.Now()

	var kvs = make([]key.KeyVal, num, num)
	var svs = make([]StrVal, num, num)

	s := "aaa"
	for i := 0; i < num; i++ {
		kvs[i].Key = stringkey.New(s)
		kvs[i].Val = i

		svs[i].Str = s
		svs[i].Val = i

		s = Inc(s)
	}

	RunTime[name] = time.Since(StartTime[name])
	return kvs, svs
}

//First genRandomizedSvs() copies []KeyVal passed in. Then it randomizes that
//copy in-place. Finnally, it returns the randomized copy.
func genRandomizedKvs(kvs []key.KeyVal) []key.KeyVal {
	var name = "genRandomizedKvs"
	StartTime[name] = time.Now()

	var randKvs = make([]key.KeyVal, len(kvs))
	copy(randKvs, kvs)

	//From: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
	var n = len(randKvs) // n is the number of elements
	var limit = n - 1
	for i := 0; i < limit; /* aka i_max = n-2 */ i++ {
		j := n - rand.Intn(i+1) - 1 // i <= j < n
		// j_min = 0   => n - (i_max + 1) - 1 = n - (n-2 + 1) - 1 = n-n+2-1-1 = 0
		// j_max = n-1 => n - 0 - 1 = n - 1
		randKvs[i], randKvs[j] = randKvs[j], randKvs[i]
	}

	RunTime[name] = time.Since(StartTime[name])
	return randKvs
}

//First genRandomizedSvs() copies []StrVal passed in. Then it randomizes that
//copy in-place. Finnally, it returns the randomized copy.
func genRandomizedSvs(svs []StrVal) []StrVal {
	var name = "genRandomizedSvs"
	StartTime[name] = time.Now()

	var randSvs = make([]StrVal, len(svs))
	copy(randSvs, svs)

	//From: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
	var limit = len(randSvs) //n-1
	for i := 0; i < limit; /* aka i_max = n-2 */ i++ {
		j := rand.Intn(i+1) - 1 // i <= j < n; j_min=n-(n-2+1)-1=0; j_max=n-0-1=n-1
		randSvs[i], randSvs[j] = randSvs[j], randSvs[i]
	}

	RunTime[name] = time.Since(StartTime[name])
	return randSvs
}

func BenchmarkMapGet(b *testing.B) {
	var name = "BenchmarkMapGet"
	log.Printf("%s b.N=%d\n", name, b.N)

	var _, ok = LookupMap["aaa"]
	if !ok {
		LookupMap, DeleteMap = buildMaps(numKvs)
	}

	StartTime[name] = time.Now()

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var j = i % numKvs
		var s = SVS[j].Str
		var v = SVS[j].Val
		var val, ok = LookupMap[s]
		if !ok {
			b.Fatalf("LookupMap[%q] does not exist", s)
		}
		if val != v {
			b.Fatalf("LookupMap[%q] != %d", s, KVS[j].Val)
		}
	}

	RunTime[name] = time.Since(StartTime[name])
}

func BenchmarkMapPut(b *testing.B) {
	var name = "BenchmarkMapPut"
	log.Printf("%s b.N=%d\n", name, b.N)
	StartTime[name] = time.Now()
	var m = make(map[string]int)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var j = i % numKvs
		var s = SVS[j].Str
		var v = SVS[j].Val
		m[s] = v
	}

	RunTime[name] = time.Since(StartTime[name])
}

var rebuildDeleteMapNum int

func rebuildDeleteMap(svs []StrVal) {
	var name = fmt.Sprintf("BenchmarkMapPut-%d", rebuildDeleteMapNum)
	rebuildDeleteMapNum++

	StartTime[name] = time.Now()

	for _, sv := range svs {
		var _, ok = DeleteMap[sv.Str]
		if ok {
			break
		}
		//else
		delete(DeleteMap, sv.Str)

		DeleteMap[sv.Str] = sv.Val
	}

	RunTime[name] = time.Since(StartTime[name])
}

func BenchmarkMapDel(b *testing.B) {
	var name = "BenchmarkMapDel"
	log.Printf("%s b.N=%d\n", name, b.N)
	StartTime[name] = time.Now()
	rebuildDeleteMap(SVS)
	RunTime[name] = time.Since(StartTime[name])

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var j = i % numKvs
		var k = SVS[j].Str
		var v = SVS[j].Val

		var val, ok = DeleteMap[k]
		if ok {
			delete(DeleteMap, k)
		} else if val != v {
			b.Fatalf("DeleteMap[%s],%d != %d", k, v, val)
		}

		//b.StopTimer()
		DeleteMap[k] = v
		//b.StartTimer()
	}
}
<|endoftext|>


<commit_before>package models

import(
  "../api"
  log "github.com/Sirupsen/logrus"
  "time"
  "net/url"
  "strings"
  "strconv"
  "fmt"
  "sort"
)

type Module struct {
  Name          string
  Id            string
  Station       *Station
  RfStrength    float64
  Battery       float64
  Measures      []string
  LastData      Timestamp
  DataInterval  int
}

func ModuleFromJson(moduleObj map[string]interface{}) Module{
  // Create a module from a JSON blob
  mod := Module{}

  mod.Name = moduleObj["module_name"].(string)
  mod.Id = moduleObj["_id"].(string)
  if val, ok := moduleObj["rf_status"]; ok {
    mod.RfStrength = val.(float64)
  }
  if val, ok := moduleObj["battery_vp"]; ok {
    mod.Battery = val.(float64)
  }

  mod.DataInterval = 300

  for _, measure := range moduleObj["data_type"].([]interface{}){
    mod.Measures = append(mod.Measures, measure.(string))
  }

  return mod
}

func (self *Module) ModName() string{
  return fmt.Sprintf("%s/%s", self.Station.Name, self.Name)
}

func (self *Module) NextData() time.Time{
  // Return expected next data for module
  // Netatmo API has 10 minute cache time, so update every 11 minutes
  return self.LastData.Time.Add(time.Duration(11)*time.Minute)
}

func (self *Module) Stats() []StatsSet{
  stats := []StatsSet{}

  if self.NextData().Before(time.Now()) {
    sset := NewStatsSet("meta", "module", self.Station.Name, self.Name)
    sset.AddStat("battery", self.Station.LastStatus, self.Battery)
    sset.AddStat("rf", self.Station.LastStatus, self.RfStrength)
    stats = append(stats, sset)

    dataFrom := time.Now().Add(time.Duration(3600*-1)*time.Second)

    // Now the stations actual metrics
    p := url.Values{}
    p.Set("device_id", self.Station.Id)
    p.Set("module_id", self.Id)
    p.Set("scale", "max")
    p.Set("type", strings.Join(self.Measures, ","))
    p.Set("real_time", "true")
    p.Set("optimize", "false")
    p.Set("date_begin", strconv.FormatInt(dataFrom.Unix(),10))
    p.Set("date_end", strconv.FormatInt(time.Now().Unix(),10))
    r := api.Request{
      Path:"getmeasure",
      Params:p,
    }
    err := self.Station.StationList.Api.DoCall(&r)
    if err != nil{
      log.Error(fmt.Sprintf("Error getting stats for %s/%s: %s", self.Station.Name, self.Name, err))
    } else {
      //Measurements
      data := r.Data["body"].
              (map[string]interface{})

      datapoints := self.TimeSeriesData(data)

      logFields := log.Fields{
        "DataInterval": self.DataInterval,
        "LastData": self.LastData.Timestamp,
        "LatestData": datapoints.LatestTimestamp().Timestamp,
        "Module": self.ModName(),
      }

      if self.LastData.Unix >= datapoints.LatestTimestamp().Unix {
        log.WithFields(logFields).Warning("Update due but no new data!")
      } else {
        sset := NewStatsSet("station", self.Station.Name, self.Name)
        self.TimestampStats(&sset, datapoints)

        stats = append(stats, sset)

        self.LastData = datapoints.LatestTimestamp()
        self.DataInterval = datapoints.DataInterval()
        log.WithFields(logFields).Info("Updated")
      }
    }
  }

  return stats
}

func (self *Module) TimeSeriesData(data map[string]interface{}) DataPointList{
  timestamps := []Timestamp{}
  datapoints := DataPointList{}

  for ts := range data{
    timestamps = append(timestamps, NewTimestamp(ts))
  }

  for _,ts := range timestamps{
    datapoint := DataPoint{}
    datapoint.Time = ts
    datapoint.Complete = true
    datapoint.Data = make(map[string]float64)

    rawData := data[ts.String].([]interface{})

    for i,measure := range self.Measures{
      periodData := rawData[i]
      if periodData == nil {
        datapoint.Complete = false
      } else {
        fmt.Println(periodData, measure)
        datapoint.Data[measure] = periodData.(float64)
      }
    }

    datapoints.DataPoints = append(datapoints.DataPoints, datapoint)
  }

  sort.Sort(ByTimestamp(datapoints.DataPoints))

  return datapoints
}

func (self *Module) TimestampStats(sset *StatsSet, datapoints DataPointList){
  for _,point := range datapoints.DataPoints{
    if point.Time.Unix > self.LastData.Unix {
      for _,measure := range self.Measures{
        sset.AddStat(measure, point.Time.Float, point.Data[measure])
      }

      log.WithFields(log.Fields{
        "Module": self.ModName(),
        "Time": point.Time.String,
      }).Debug("Sending Data")
    }
  }
}
<commit_msg>Avoid panic by iterating through known measures<commit_after>package models

import(
  "../api"
  log "github.com/Sirupsen/logrus"
  "time"
  "net/url"
  "strings"
  "strconv"
  "fmt"
  "sort"
)

type Module struct {
  Name          string
  Id            string
  Station       *Station
  RfStrength    float64
  Battery       float64
  Measures      []string
  LastData      Timestamp
  DataInterval  int
}

func ModuleFromJson(moduleObj map[string]interface{}) Module{
  // Create a module from a JSON blob
  mod := Module{}

  mod.Name = moduleObj["module_name"].(string)
  mod.Id = moduleObj["_id"].(string)
  if val, ok := moduleObj["rf_status"]; ok {
    mod.RfStrength = val.(float64)
  }
  if val, ok := moduleObj["battery_vp"]; ok {
    mod.Battery = val.(float64)
  }

  mod.DataInterval = 300

  for _, measure := range moduleObj["data_type"].([]interface{}){
    mod.Measures = append(mod.Measures, measure.(string))
  }

  return mod
}

func (self *Module) ModName() string{
  return fmt.Sprintf("%s/%s", self.Station.Name, self.Name)
}

func (self *Module) NextData() time.Time{
  // Return expected next data for module
  // Netatmo API has 10 minute cache time, so update every 11 minutes
  return self.LastData.Time.Add(time.Duration(11)*time.Minute)
}

func (self *Module) Stats() []StatsSet{
  stats := []StatsSet{}

  if self.NextData().Before(time.Now()) {
    sset := NewStatsSet("meta", "module", self.Station.Name, self.Name)
    sset.AddStat("battery", self.Station.LastStatus, self.Battery)
    sset.AddStat("rf", self.Station.LastStatus, self.RfStrength)
    stats = append(stats, sset)

    dataFrom := time.Now().Add(time.Duration(3600*-1)*time.Second)

    // Now the stations actual metrics
    p := url.Values{}
    p.Set("device_id", self.Station.Id)
    p.Set("module_id", self.Id)
    p.Set("scale", "max")
    p.Set("type", strings.Join(self.Measures, ","))
    p.Set("real_time", "true")
    p.Set("optimize", "false")
    p.Set("date_begin", strconv.FormatInt(dataFrom.Unix(),10))
    p.Set("date_end", strconv.FormatInt(time.Now().Unix(),10))
    r := api.Request{
      Path:"getmeasure",
      Params:p,
    }
    err := self.Station.StationList.Api.DoCall(&r)
    if err != nil{
      log.Error(fmt.Sprintf("Error getting stats for %s/%s: %s", self.Station.Name, self.Name, err))
    } else {
      //Measurements
      data := r.Data["body"].
              (map[string]interface{})

      datapoints := self.TimeSeriesData(data)

      logFields := log.Fields{
        "DataInterval": self.DataInterval,
        "LastData": self.LastData.Timestamp,
        "LatestData": datapoints.LatestTimestamp().Timestamp,
        "Module": self.ModName(),
      }

      if self.LastData.Unix >= datapoints.LatestTimestamp().Unix {
        log.WithFields(logFields).Warning("Update due but no new data!")
      } else {
        sset := NewStatsSet("station", self.Station.Name, self.Name)
        self.TimestampStats(&sset, datapoints)

        stats = append(stats, sset)

        self.LastData = datapoints.LatestTimestamp()
        self.DataInterval = datapoints.DataInterval()
        log.WithFields(logFields).Info("Updated")
      }
    }
  }

  return stats
}

func (self *Module) TimeSeriesData(data map[string]interface{}) DataPointList{
  timestamps := []Timestamp{}
  datapoints := DataPointList{}

  for ts := range data{
    timestamps = append(timestamps, NewTimestamp(ts))
  }

  for _,ts := range timestamps{
    datapoint := DataPoint{}
    datapoint.Time = ts
    datapoint.Complete = true
    datapoint.Data = make(map[string]float64)

    rawData := data[ts.String].([]interface{})

    for i,measure := range self.Measures{
      periodData := rawData[i]
      if periodData == nil {
        datapoint.Complete = false
      } else {
        datapoint.Data[measure] = periodData.(float64)
      }
    }

    datapoints.DataPoints = append(datapoints.DataPoints, datapoint)
  }

  sort.Sort(ByTimestamp(datapoints.DataPoints))

  return datapoints
}

func (self *Module) TimestampStats(sset *StatsSet, datapoints DataPointList){
  for _,point := range datapoints.DataPoints{
    if point.Time.Unix > self.LastData.Unix {
      for name,value := range point.Data{
        sset.AddStat(name, point.Time.Float, value)
      }

      log.WithFields(log.Fields{
        "Module": self.ModName(),
        "Time": point.Time.String,
      }).Debug("Sending Data")
    }
  }
}
<|endoftext|>


<commit_before>package module

import (
	"github.com/name5566/leaf/log"
	"sync"
)

type Module interface {
	OnInit()
	OnDestroy()
	Run(closeSig chan bool)
}

type module struct {
	mi       Module
	closeSig chan bool
	wg       sync.WaitGroup
}

var mods []*module

func Register(mi Module) {
	m := new(module)
	m.mi = mi
	m.closeSig = make(chan bool, 1)

	mods = append(mods, m)
}

func Init() {
	for i := 0; i < len(mods); i++ {
		mods[i].mi.OnInit()
	}

	for i := 0; i < len(mods); i++ {
		go run(mods[i])
	}
}

func Destroy() {
	for i := len(mods) - 1; i >= 0; i-- {
		m := mods[i]
		m.closeSig <- true
		m.wg.Wait()

		func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error("%v", r)
				}
			}()

			m.mi.OnDestroy()
		}()
	}
}

func run(m *module) {
	m.wg.Add(1)
	m.mi.Run(m.closeSig)
	m.wg.Done()
}
<commit_msg>recover module destroy in panicking.<commit_after>package module

import (
	"github.com/name5566/leaf/log"
	"sync"
)

type Module interface {
	OnInit()
	OnDestroy()
	Run(closeSig chan bool)
}

type module struct {
	mi       Module
	closeSig chan bool
	wg       sync.WaitGroup
}

var mods []*module

func Register(mi Module) {
	m := new(module)
	m.mi = mi
	m.closeSig = make(chan bool, 1)

	mods = append(mods, m)
}

func Init() {
	for i := 0; i < len(mods); i++ {
		mods[i].mi.OnInit()
	}

	for i := 0; i < len(mods); i++ {
		go run(mods[i])
	}
}

func Destroy() {
	for i := len(mods) - 1; i >= 0; i-- {
		m := mods[i]
		m.closeSig <- true
		m.wg.Wait()
		destroy(m)
	}
}

func run(m *module) {
	m.wg.Add(1)
	m.mi.Run(m.closeSig)
	m.wg.Done()
}

func destroy(m *module) {
	defer func() {
		if r := recover(); r != nil {
			log.Error("%v", r)
		}
	}()

	m.mi.OnDestroy()
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package module

import (
	"net/http"

	"github.com/issue9/mux/v2"
)

// Prefix 声明一个 Prefix 实例。
func (m *Module) Prefix(prefix string) *mux.Prefix {
	return m.ms.router.Prefix(prefix)
}

// Handle 添加一个路由项
func (m *Module) Handle(path string, h http.Handler, methods ...string) *Module {
	if err := m.ms.router.Handle(path, h, methods...); err != nil {
		panic(err)
	}

	return m
}

// Get 指定一个 GET 请求
func (m *Module) Get(path string, h http.Handler) *Module {
	return m.Handle(path, h, http.MethodGet)
}

// Post 指定个 POST 请求处理
func (m *Module) Post(path string, h http.Handler) *Module {
	return m.Handle(path, h, http.MethodPost)
}

// Delete 指定个 Delete 请求处理
func (m *Module) Delete(path string, h http.Handler) *Module {
	return m.Handle(path, h, http.MethodDelete)
}

// Put 指定个 Put 请求处理
func (m *Module) Put(path string, h http.Handler) *Module {
	return m.Handle(path, h, http.MethodPut)
}

// Patch 指定个 Patch 请求处理
func (m *Module) Patch(path string, h http.Handler) *Module {
	return m.Handle(path, h, http.MethodPatch)
}

// HandleFunc 指定一个请求
func (m *Module) HandleFunc(path string, h func(w http.ResponseWriter, r *http.Request), methods ...string) *Module {
	return m.Handle(path, http.HandlerFunc(h), methods...)
}

// GetFunc 指定一个 GET 请求
func (m *Module) GetFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *Module {
	return m.HandleFunc(path, h, http.MethodGet)
}

// PostFunc 指定一个 Post 请求
func (m *Module) PostFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *Module {
	return m.HandleFunc(path, h, http.MethodPost)
}

// DeleteFunc 指定一个 Delete 请求
func (m *Module) DeleteFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *Module {
	return m.HandleFunc(path, h, http.MethodDelete)
}

// PutFunc 指定一个 Put 请求
func (m *Module) PutFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *Module {
	return m.HandleFunc(path, h, http.MethodPut)
}

// PatchFunc 指定一个 Patch 请求
func (m *Module) PatchFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *Module {
	return m.HandleFunc(path, h, http.MethodPatch)
}
<commit_msg>改变路由的接口<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package module

import (
	"net/http"

	"github.com/issue9/mux/v2"
)

// Prefix 声明一个 Prefix 实例。
func (m *Module) Prefix(prefix string) *mux.Prefix {
	return m.ms.router.Prefix(prefix)
}

// Handle 添加一个路由项
func (m *Module) Handle(path string, h http.Handler, methods ...string) error {
	return m.ms.router.Handle(path, h, methods...)
}

// Get 指定一个 GET 请求
func (m *Module) Get(path string, h http.Handler) *mux.Prefix {
	return m.ms.router.Get(path, h)
}

// Post 指定个 POST 请求处理
func (m *Module) Post(path string, h http.Handler) *mux.Prefix {
	return m.ms.router.Post(path, h)
}

// Delete 指定个 Delete 请求处理
func (m *Module) Delete(path string, h http.Handler) *mux.Prefix {
	return m.ms.router.Delete(path, h)
}

// Put 指定个 Put 请求处理
func (m *Module) Put(path string, h http.Handler) *mux.Prefix {
	return m.ms.router.Put(path, h)
}

// Patch 指定个 Patch 请求处理
func (m *Module) Patch(path string, h http.Handler) *mux.Prefix {
	return m.ms.router.Patch(path, h)
}

// HandleFunc 指定一个请求
func (m *Module) HandleFunc(path string, h func(w http.ResponseWriter, r *http.Request), methods ...string) error {
	return m.ms.router.Handle(path, http.HandlerFunc(h), methods...)
}

// GetFunc 指定一个 GET 请求
func (m *Module) GetFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *mux.Prefix {
	return m.ms.router.GetFunc(path, h)
}

// PostFunc 指定一个 Post 请求
func (m *Module) PostFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *mux.Prefix {
	return m.ms.router.PostFunc(path, h)
}

// DeleteFunc 指定一个 Delete 请求
func (m *Module) DeleteFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *mux.Prefix {
	return m.ms.router.PostFunc(path, h)
}

// PutFunc 指定一个 Put 请求
func (m *Module) PutFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *mux.Prefix {
	return m.ms.router.PostFunc(path, h)
}

// PatchFunc 指定一个 Patch 请求
func (m *Module) PatchFunc(path string, h func(w http.ResponseWriter, r *http.Request)) *mux.Prefix {
	return m.ms.router.PostFunc(path, h)
}
<|endoftext|>


<commit_before>package parser

import "testing"

func TestProcessSysbenchResult(t *testing.T) {
	cum, trend, att := ProcessSysbenchResult("test.txt")

	if cum != "741.86" {
		t.Error("Cumulative TPS is %v, want 741.86", cum)
	}

	if trend[0] != "772.95" {
		t.Error("History IPS[0] is %v, want 772.95", trend[0])
	}

	if att["test-type"] != "sysbench" {
		t.Error("Attribute[\"test-type\"] is %v, want sysbench", att["test-type"])
	}
}

func TestParsePIDStat(t *testing.T) {
	s, dps := ParsePIDStat("pidstat.txt")

	if s != "mongod" {
		t.Error("Pidstat process-type is " + s + " expecting mongod")
	}

	if dps["cpu"][0].d != "91.01" {
		t.Error("Pidstat cpu[0] is " + dps["cpu"][0].d + " expecting 91.01")
	}

	if dps["mem"][1].d != "23.29" {
		t.Error("Pidstat mem[1] is " + dps["mem"][1].d + " expecting 23.29")
	}
}

func TestParseMongoSIMStat(t *testing.T) {
	r := ProcessMongoSIMResult("mongo-sim.txt")

	if r.AllNodes.Op_per_second != 0 {
		t.Error("mongo-sim op_per_second is ", r.AllNodes.Op_per_second, ", expecting 0")
	}
	if r.Nodes[0]["st_staging_minutes"].Op_count != 43500 {
		t.Error("mongo-sim op_per_second is ", r.Nodes[0]["st_staging_minutes"].Op_count, ", expecting 100")
	}
}
<commit_msg>update parser_test<commit_after>package parser

import (
	"fmt"
	"testing"
)

func TestProcessSysbenchResult(t *testing.T) {
	cum, trend, att := ProcessSysbenchResult("test.txt")

	if cum != "741.86" {
		t.Error("Cumulative TPS is %v, want 741.86", cum)
	}

	if trend[0] != "772.95" {
		t.Error("History IPS[0] is %v, want 772.95", trend[0])
	}

	if att["test-type"] != "sysbench" {
		t.Error("Attribute[\"test-type\"] is %v, want sysbench", att["test-type"])
	}
}

func TestParsePIDStat(t *testing.T) {
	s, dps := ParsePIDStat("pidstat.txt")

	if s != "mongod" {
		t.Error("Pidstat process-type is " + s + " expecting mongod")
	}

	if dps["cpu"][0].d != "91.01" {
		t.Error("Pidstat cpu[0] is " + dps["cpu"][0].d + " expecting 91.01")
	}

	if dps["mem"][1].d != "23.29" {
		t.Error("Pidstat mem[1] is " + dps["mem"][1].d + " expecting 23.29")
	}
}

func TestParseMongoSIMStat(t *testing.T) {
	r := ProcessMongoSIMResult("mongo-sim.txt")

	if r.AllNodes.Op_per_second != 0 {
		t.Error("mongo-sim op_per_second is ", r.AllNodes.Op_per_second, ", expecting 0")
	}
	if r.Nodes[0]["st_staging_minutes"].Op_count != 43500 {
		t.Error("mongo-sim op_per_second is ", r.Nodes[0]["st_staging_minutes"].Op_count, ", expecting 100")
	}
}

func TestParseMongoPerfResult(t *testing.T) {
	r := ProcessMongoPerfResult("mongo-perf.txt")

	if r == nil {
		t.Error("[mongo-perf] return value is nil")
	}

	if len(r) != 12 {
		t.Error("[mongo-perf] receive wrong number of results, received ", len(r), " expecting 12")
	}

	fmt.Println(r)

	if r["Geo.within.center_TH-001"].Result != 928.11 {
		t.Error("[mongo-perf] receive wrong value of results, received ", r["Geo.within.center_TH-001"].Result, " expecting 928.11")
	}

	// FIXME need more test for average and CV
}
<|endoftext|>


<commit_before>/*
  Copyright 2012 Sergey Cherepanov (https://github.com/cheggaaa)

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

package cnst

const (
	VERSION = "0.1.7.2"
	SIGN    = "Anteater " + VERSION
)

<commit_msg>0.1.8<commit_after>/*
  Copyright 2012 Sergey Cherepanov (https://github.com/cheggaaa)

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

package cnst

const (
	VERSION = "0.1.8"
	SIGN    = "Anteater " + VERSION
)

<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "avg calculates the average of the given input\n")
		fmt.Fprintf(os.Stderr, "  - input is accepted via stdin\n")
		fmt.Fprintf(os.Stderr, "  - type of the input is automatically detected (accepted inputs are Go time duration, integer and float)\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, `Usage: echo '15m10s\n3m5s' | avgdur`)
		fmt.Fprintf(os.Stderr, "\n")
	}
	flag.Parse()
	var (
		total float64
		count int64

		firsttype typ
	)
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		line := strings.TrimSpace(s.Text())
		if line == "" {
			continue
		}
		if firsttype == unassigned {
			firsttype = typeof(line)
		}
		switch firsttype {
		case duration:
			dur, _ := time.ParseDuration(line)
			total += float64(dur.Nanoseconds())
			count++
		case integer:
			i, _ := strconv.ParseInt(line, 10, 64)
			total += float64(i)
			count++
		case float:
			f, _ := strconv.ParseFloat(line, 64)
			total += f
			count++
		case unknown:
			log.Fatalf("unrecognized input %q", line)
		}
	}

	if err := s.Err(); err != nil {
		log.Fatal(err)
	}

	switch firsttype {
	case duration:
		fmt.Println((time.Duration(total) / time.Duration(count)).String())
	case integer:
		fmt.Println(int64(total) / count)
	case float:
		fmt.Println(total / float64(count))
	}
}

type typ int

const (
	unassigned typ = iota
	unknown
	duration
	integer
	float
)

func typeof(s string) typ {
	_, err := time.ParseDuration(s)
	if err == nil {
		return duration
	}
	_, err = strconv.ParseInt(s, 10, 64)
	if err == nil {
		return integer
	}
	_, err = strconv.ParseFloat(s, 64)
	if err == nil {
		return float
	}
	return unknown
}
<commit_msg>avg: parse date as well (best effort)<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "avg calculates the average of the given input\n")
		fmt.Fprintf(os.Stderr, "  - input is accepted via stdin\n")
		fmt.Fprintf(os.Stderr, "  - type of the input is automatically detected (accepted inputs are Go time duration, integer and float)\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, `Usage: echo '15m10s\n3m5s' | avgdur`)
		fmt.Fprintf(os.Stderr, "\n")
	}
	flag.Parse()
	var (
		total float64
		count int64

		firsttype typ

		// date parsing
		lastdate   time.Time
		datelayout string
	)
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		line := strings.TrimSpace(s.Text())
		if line == "" {
			continue
		}
		if firsttype == unassigned {
			firsttype = typeof(line)
		}
		switch firsttype {
		case duration:
			dur, _ := time.ParseDuration(line)
			total += float64(dur.Nanoseconds())
			count++
		case integer:
			i, _ := strconv.ParseInt(line, 10, 64)
			total += float64(i)
			count++
		case float:
			f, _ := strconv.ParseFloat(line, 64)
			total += f
			count++
		case date:
			if lastdate.IsZero() {
				lastdate, datelayout = layout(line)
				continue
			}

			t, err := time.Parse(datelayout, line)
			if err != nil {
				log.Fatalf("could not parse %q with layout %q", line, datelayout)
			}
			total += float64(t.Sub(lastdate).Nanoseconds())
			count++
			lastdate = t
		case unknown:
			log.Fatalf("unrecognized input %q", line)
		}
	}

	if err := s.Err(); err != nil {
		log.Fatal(err)
	}

	switch firsttype {
	case duration:
		fmt.Println((time.Duration(total) / time.Duration(count)).String())
	case integer:
		fmt.Println(int64(total) / count)
	case float:
		fmt.Println(total / float64(count))
	case date:
		fmt.Println((time.Duration(total) / time.Duration(count)).String())
	}
}

type typ int

const (
	unassigned typ = iota
	unknown
	duration
	integer
	float
	date
)

func typeof(s string) typ {
	_, err := time.ParseDuration(s)
	if err == nil {
		return duration
	}
	_, err = strconv.ParseInt(s, 10, 64)
	if err == nil {
		return integer
	}
	_, err = strconv.ParseFloat(s, 64)
	if err == nil {
		return float
	}
	_, layout := layout(s)
	if layout != "" {
		return date
	}
	return unknown
}

func layout(s string) (time.Time, string) {
	for _, layout := range layouts {
		t, err := time.Parse(layout, s)
		if err == nil {
			return t, layout
		}
	}
	return time.Time{}, ""
}

var layouts = []string{
	"2006-01-02 15:04:05.999",
	time.RFC3339,
	time.RFC3339Nano,
}
<|endoftext|>


<commit_before>
// LoL Cruncher - A Historical League of Legends Statistics Tracker
// Copyright (C) 2015  Jason Chu (1lann) 1lanncontact@gmail.com

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package crunch

import (
	"cruncher/app/models/dataFormat"
	"time"
	"math"
)

func chomp(playerData *dataFormat.Player, game dataFormat.Game) {
	normalSR := game.Type == "NORMAL"
	rankedSR := (game.Type == "RANKED_SOLO_5x5") ||
		(game.Type == "RANKED_PREMADE_5x5")
	teamSR := (game.Type == "RANKED_TEAM_5x5")
	normalTT := (game.Type == "NORMAL_3x3")
	rankedTT := (game.Type == "RANKED_PREMADE_3x3")
	teamTT := (game.Type == "RANKED_TEAM_3x3")
	if !(normalSR || rankedSR || teamSR || normalTT || rankedTT || teamTT) {
		return
	}

	var parsedType string

	if normalSR {
		parsedType = "Summoner's Rift Normals"
	} else if rankedSR {
		parsedType = "Summoner's Rift Ranked"
	} else if teamSR {
		parsedType = "Summoner's Rift Ranked Team"
	} else if normalTT {
		parsedType = "Twisted Treeline Normals"
	} else if rankedTT {
		parsedType = "Twisted Treeline Ranked"
	} else if teamTT {
		parsedType = "Twisted Treeline Ranked Team"
	}

	allAll := playerData.All.All
	allGameType := playerData.All.GameTypeStats[parsedType]
	allChampion := playerData.All.Champions[game.ChampionId]

	monthlyAll := playerData.MonthlyStats[game.YearMonth].All
	monthlyGameType := playerData.MonthlyStats[game.YearMonth].
		GameTypeStats[parsedType]
	monthlyAllChampion := playerData.MonthlyStats[game.YearMonth].
		Champions[game.ChampionId]

	if game.DidWin {
		allAll.Wins++
		allGameType.Wins++
		allChampion.Wins++

		monthlyAll.Wins++
		monthlyGameType.Wins++
		monthlyAllChampion.Wins++
		if game.IsOnBlue {
			allAll.Blue.Wins++
			allGameType.Blue.Wins++

			monthlyAll.Blue.Wins++
			monthlyGameType.Blue.Wins++
		} else {
			allAll.Red.Wins++
			allGameType.Red.Wins++

			monthlyAll.Red.Wins++
			monthlyGameType.Red.Wins++
		}
	} else {
		allAll.Losses++
		allGameType.Losses++
		allChampion.Losses++
		monthlyAll.Losses++
		monthlyGameType.Losses++
		monthlyAllChampion.Losses++
		if game.IsOnBlue {
			allAll.Blue.Losses++
			allGameType.Blue.Losses++

			monthlyAll.Blue.Losses++
			monthlyGameType.Blue.Losses++
		} else {
			allAll.Red.Losses++
			allGameType.Red.Losses++

			monthlyAll.Red.Losses++
			monthlyGameType.Red.Losses++
		}
	}

	allAll.TimePlayed += game.Duration
	allGameType.TimePlayed += game.Duration
	allChampion.TimePlayed += game.Duration

	monthlyAll.TimePlayed += game.Duration
	monthlyGameType.TimePlayed += game.Duration
	monthlyAllChampion.TimePlayed += game.Duration

	allAll.Kills += game.Kills
	allGameType.Kills += game.Kills
	allChampion.Kills += game.Kills

	monthlyAll.Kills += game.Kills
	monthlyGameType.Kills += game.Kills
	monthlyAllChampion.Kills += game.Kills

	allAll.Assists += game.Assists
	allGameType.Assists += game.Assists
	allChampion.Assists += game.Assists

	monthlyAll.Assists += game.Assists
	monthlyGameType.Assists += game.Assists
	monthlyAllChampion.Assists += game.Assists

	allAll.Deaths += game.Deaths
	allGameType.Deaths += game.Deaths
	allChampion.Deaths += game.Deaths

	monthlyAll.Deaths += game.Deaths
	monthlyGameType.Deaths += game.Deaths
	monthlyAllChampion.Deaths += game.Deaths

	allAll.MinionsKilled += game.MinionsKilled
	allGameType.MinionsKilled += game.MinionsKilled
	allChampion.MinionsKilled += game.MinionsKilled

	monthlyAll.MinionsKilled += game.MinionsKilled
	monthlyGameType.MinionsKilled += game.MinionsKilled
	monthlyAllChampion.MinionsKilled += game.MinionsKilled

	allAll.MonstersKilled += game.MonstersKilled
	allGameType.MonstersKilled += game.MonstersKilled
	allChampion.MonstersKilled += game.MonstersKilled

	monthlyAll.MonstersKilled += game.MonstersKilled
	monthlyGameType.MonstersKilled += game.MonstersKilled
	monthlyAllChampion.MonstersKilled += game.MonstersKilled

	allAll.WardsPlaced += game.WardsPlaced
	allGameType.WardsPlaced += game.WardsPlaced
	allChampion.WardsPlaced += game.WardsPlaced

	monthlyAll.WardsPlaced += game.WardsPlaced
	monthlyGameType.WardsPlaced += game.WardsPlaced
	monthlyAllChampion.WardsPlaced += game.WardsPlaced

	allAll.DoubleKills += game.DoubleKills
	allGameType.DoubleKills += game.DoubleKills

	monthlyAll.DoubleKills += game.DoubleKills
	monthlyGameType.DoubleKills += game.DoubleKills

	allAll.TripleKills += game.TripleKills
	allGameType.TripleKills += game.TripleKills

	monthlyAll.TripleKills += game.TripleKills
	monthlyGameType.TripleKills += game.TripleKills

	allAll.QuadraKills += game.QuadraKills
	allGameType.QuadraKills += game.QuadraKills

	monthlyAll.QuadraKills += game.QuadraKills
	monthlyGameType.QuadraKills += game.QuadraKills

	allAll.PentaKills += game.PentaKills
	allGameType.PentaKills += game.PentaKills

	monthlyAll.PentaKills += game.PentaKills
	monthlyGameType.PentaKills += game.PentaKills

	allAll.GoldEarned += game.GoldEarned
	allGameType.GoldEarned += game.GoldEarned

	monthlyAll.GoldEarned += game.GoldEarned
	monthlyGameType.GoldEarned += game.GoldEarned

	allAll.WardsKilled += game.WardsKilled
	allGameType.WardsKilled += game.WardsKilled

	monthlyAll.WardsKilled += game.WardsKilled
	monthlyGameType.WardsKilled += game.WardsKilled

	// allAll := playerData.All.All
	// allGameType := playerData.All.GameTypeStats[parsedType]
	// allChampion := playerData.All.Champions[game.ChampionId]

	// monthlyAll := playerData.MonthlyStats[game.YearMonth].All
	// monthlyGameType := playerData.MonthlyStats[game.YearMonth].
	// 	GameTypeStats[parsedType]
	// monthlyAllChampion := playerData.MonthlyStats[game.YearMonth].
	// 	Champions[game.ChampionId]

	playerData.All.All = allAll
	if playerData.All.GameTypeStats == nil {
		playerData.All.GameTypeStats = make(map[string]dataFormat.DetailedNumberOf)
	}
	playerData.All.GameTypeStats[parsedType] = allGameType
	if playerData.All.Champions == nil {
		playerData.All.Champions = make(map[string]dataFormat.BasicNumberOf)
	}
	playerData.All.Champions[game.ChampionId] = allChampion

	if playerData.MonthlyStats == nil {
		playerData.MonthlyStats = make(map[string]dataFormat.Stats)
	}

	monthlyCopy := playerData.MonthlyStats[game.YearMonth]
	monthlyCopy.All = monthlyAll

	if monthlyCopy.GameTypeStats == nil {
		monthlyCopy.GameTypeStats = make(map[string]dataFormat.DetailedNumberOf)
	}
	monthlyCopy.GameTypeStats[parsedType] = monthlyGameType

	if monthlyCopy.Champions == nil {
		monthlyCopy.Champions = make(map[string]dataFormat.BasicNumberOf)
	}
	monthlyCopy.Champions[game.ChampionId] = monthlyAllChampion

	playerData.MonthlyStats[game.YearMonth] = monthlyCopy

	return
}

func hasBeenProcessed(games []string, query string) bool {
	for _, game := range games {
		if game == query {
			return true
		}
	}
	return false
}

func GetNextUpdate(games []dataFormat.Game) time.Time {
	checkIndex := int(math.Min(float64(4), float64(len(games) - 1)))
	intervalDuration := time.Since(games[checkIndex].Date)

	if intervalDuration.Hours() > 24 {
		intervalDuration = time.Duration(24) * time.Hour
	}

	return time.Now().Add(intervalDuration)
}

func Crunch(playerData dataFormat.Player,
	games []dataFormat.Game) dataFormat.Player {
	var processedList []string
	for _, game := range games {
		processedList = append(processedList, game.Id)
		if !hasBeenProcessed(playerData.ProcessedGames, game.Id) {
			chomp(&playerData, game)
		}
	}

	playerData.ProcessedGames = processedList

	return playerData
}
<commit_msg>Increased update rate<commit_after>
// LoL Cruncher - A Historical League of Legends Statistics Tracker
// Copyright (C) 2015  Jason Chu (1lann) 1lanncontact@gmail.com

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package crunch

import (
	"cruncher/app/models/dataFormat"
	"time"
	"math"
)

func chomp(playerData *dataFormat.Player, game dataFormat.Game) {
	normalSR := game.Type == "NORMAL"
	rankedSR := (game.Type == "RANKED_SOLO_5x5") ||
		(game.Type == "RANKED_PREMADE_5x5")
	teamSR := (game.Type == "RANKED_TEAM_5x5")
	normalTT := (game.Type == "NORMAL_3x3")
	rankedTT := (game.Type == "RANKED_PREMADE_3x3")
	teamTT := (game.Type == "RANKED_TEAM_3x3")
	if !(normalSR || rankedSR || teamSR || normalTT || rankedTT || teamTT) {
		return
	}

	var parsedType string

	if normalSR {
		parsedType = "Summoner's Rift Normals"
	} else if rankedSR {
		parsedType = "Summoner's Rift Ranked"
	} else if teamSR {
		parsedType = "Summoner's Rift Ranked Team"
	} else if normalTT {
		parsedType = "Twisted Treeline Normals"
	} else if rankedTT {
		parsedType = "Twisted Treeline Ranked"
	} else if teamTT {
		parsedType = "Twisted Treeline Ranked Team"
	}

	allAll := playerData.All.All
	allGameType := playerData.All.GameTypeStats[parsedType]
	allChampion := playerData.All.Champions[game.ChampionId]

	monthlyAll := playerData.MonthlyStats[game.YearMonth].All
	monthlyGameType := playerData.MonthlyStats[game.YearMonth].
		GameTypeStats[parsedType]
	monthlyAllChampion := playerData.MonthlyStats[game.YearMonth].
		Champions[game.ChampionId]

	if game.DidWin {
		allAll.Wins++
		allGameType.Wins++
		allChampion.Wins++

		monthlyAll.Wins++
		monthlyGameType.Wins++
		monthlyAllChampion.Wins++
		if game.IsOnBlue {
			allAll.Blue.Wins++
			allGameType.Blue.Wins++

			monthlyAll.Blue.Wins++
			monthlyGameType.Blue.Wins++
		} else {
			allAll.Red.Wins++
			allGameType.Red.Wins++

			monthlyAll.Red.Wins++
			monthlyGameType.Red.Wins++
		}
	} else {
		allAll.Losses++
		allGameType.Losses++
		allChampion.Losses++
		monthlyAll.Losses++
		monthlyGameType.Losses++
		monthlyAllChampion.Losses++
		if game.IsOnBlue {
			allAll.Blue.Losses++
			allGameType.Blue.Losses++

			monthlyAll.Blue.Losses++
			monthlyGameType.Blue.Losses++
		} else {
			allAll.Red.Losses++
			allGameType.Red.Losses++

			monthlyAll.Red.Losses++
			monthlyGameType.Red.Losses++
		}
	}

	allAll.TimePlayed += game.Duration
	allGameType.TimePlayed += game.Duration
	allChampion.TimePlayed += game.Duration

	monthlyAll.TimePlayed += game.Duration
	monthlyGameType.TimePlayed += game.Duration
	monthlyAllChampion.TimePlayed += game.Duration

	allAll.Kills += game.Kills
	allGameType.Kills += game.Kills
	allChampion.Kills += game.Kills

	monthlyAll.Kills += game.Kills
	monthlyGameType.Kills += game.Kills
	monthlyAllChampion.Kills += game.Kills

	allAll.Assists += game.Assists
	allGameType.Assists += game.Assists
	allChampion.Assists += game.Assists

	monthlyAll.Assists += game.Assists
	monthlyGameType.Assists += game.Assists
	monthlyAllChampion.Assists += game.Assists

	allAll.Deaths += game.Deaths
	allGameType.Deaths += game.Deaths
	allChampion.Deaths += game.Deaths

	monthlyAll.Deaths += game.Deaths
	monthlyGameType.Deaths += game.Deaths
	monthlyAllChampion.Deaths += game.Deaths

	allAll.MinionsKilled += game.MinionsKilled
	allGameType.MinionsKilled += game.MinionsKilled
	allChampion.MinionsKilled += game.MinionsKilled

	monthlyAll.MinionsKilled += game.MinionsKilled
	monthlyGameType.MinionsKilled += game.MinionsKilled
	monthlyAllChampion.MinionsKilled += game.MinionsKilled

	allAll.MonstersKilled += game.MonstersKilled
	allGameType.MonstersKilled += game.MonstersKilled
	allChampion.MonstersKilled += game.MonstersKilled

	monthlyAll.MonstersKilled += game.MonstersKilled
	monthlyGameType.MonstersKilled += game.MonstersKilled
	monthlyAllChampion.MonstersKilled += game.MonstersKilled

	allAll.WardsPlaced += game.WardsPlaced
	allGameType.WardsPlaced += game.WardsPlaced
	allChampion.WardsPlaced += game.WardsPlaced

	monthlyAll.WardsPlaced += game.WardsPlaced
	monthlyGameType.WardsPlaced += game.WardsPlaced
	monthlyAllChampion.WardsPlaced += game.WardsPlaced

	allAll.DoubleKills += game.DoubleKills
	allGameType.DoubleKills += game.DoubleKills

	monthlyAll.DoubleKills += game.DoubleKills
	monthlyGameType.DoubleKills += game.DoubleKills

	allAll.TripleKills += game.TripleKills
	allGameType.TripleKills += game.TripleKills

	monthlyAll.TripleKills += game.TripleKills
	monthlyGameType.TripleKills += game.TripleKills

	allAll.QuadraKills += game.QuadraKills
	allGameType.QuadraKills += game.QuadraKills

	monthlyAll.QuadraKills += game.QuadraKills
	monthlyGameType.QuadraKills += game.QuadraKills

	allAll.PentaKills += game.PentaKills
	allGameType.PentaKills += game.PentaKills

	monthlyAll.PentaKills += game.PentaKills
	monthlyGameType.PentaKills += game.PentaKills

	allAll.GoldEarned += game.GoldEarned
	allGameType.GoldEarned += game.GoldEarned

	monthlyAll.GoldEarned += game.GoldEarned
	monthlyGameType.GoldEarned += game.GoldEarned

	allAll.WardsKilled += game.WardsKilled
	allGameType.WardsKilled += game.WardsKilled

	monthlyAll.WardsKilled += game.WardsKilled
	monthlyGameType.WardsKilled += game.WardsKilled

	// allAll := playerData.All.All
	// allGameType := playerData.All.GameTypeStats[parsedType]
	// allChampion := playerData.All.Champions[game.ChampionId]

	// monthlyAll := playerData.MonthlyStats[game.YearMonth].All
	// monthlyGameType := playerData.MonthlyStats[game.YearMonth].
	// 	GameTypeStats[parsedType]
	// monthlyAllChampion := playerData.MonthlyStats[game.YearMonth].
	// 	Champions[game.ChampionId]

	playerData.All.All = allAll
	if playerData.All.GameTypeStats == nil {
		playerData.All.GameTypeStats = make(map[string]dataFormat.DetailedNumberOf)
	}
	playerData.All.GameTypeStats[parsedType] = allGameType
	if playerData.All.Champions == nil {
		playerData.All.Champions = make(map[string]dataFormat.BasicNumberOf)
	}
	playerData.All.Champions[game.ChampionId] = allChampion

	if playerData.MonthlyStats == nil {
		playerData.MonthlyStats = make(map[string]dataFormat.Stats)
	}

	monthlyCopy := playerData.MonthlyStats[game.YearMonth]
	monthlyCopy.All = monthlyAll

	if monthlyCopy.GameTypeStats == nil {
		monthlyCopy.GameTypeStats = make(map[string]dataFormat.DetailedNumberOf)
	}
	monthlyCopy.GameTypeStats[parsedType] = monthlyGameType

	if monthlyCopy.Champions == nil {
		monthlyCopy.Champions = make(map[string]dataFormat.BasicNumberOf)
	}
	monthlyCopy.Champions[game.ChampionId] = monthlyAllChampion

	playerData.MonthlyStats[game.YearMonth] = monthlyCopy

	return
}

func hasBeenProcessed(games []string, query string) bool {
	for _, game := range games {
		if game == query {
			return true
		}
	}
	return false
}

func GetNextUpdate(games []dataFormat.Game) time.Time {
	checkIndex := int(math.Min(float64(2), float64(len(games) - 1)))
	intervalDuration := time.Since(games[checkIndex].Date)

	if intervalDuration.Hours() > 24 {
		intervalDuration = time.Duration(24) * time.Hour
	}

	return time.Now().Add(intervalDuration)
}

func Crunch(playerData dataFormat.Player,
	games []dataFormat.Game) dataFormat.Player {
	var processedList []string
	for _, game := range games {
		processedList = append(processedList, game.Id)
		if !hasBeenProcessed(playerData.ProcessedGames, game.Id) {
			chomp(&playerData, game)
		}
	}

	playerData.ProcessedGames = processedList

	return playerData
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

type Backend interface {
	IsDown() bool
	Verify()
}

type BackendGroup interface {
	SelectBackend() Backend
	VerifyBackends()
}

type BaseBackend struct {
	Ip             net.IP
	Port           int16
	Down           bool
	LastValidCheck time.Time
}

type BaseBackendGroup struct {
	Group []BaseBackend
}

func NewBaseBackend(ip_string string, port int16) (*BaseBackend, error) {
	ip := net.ParseIP(ip_string)
	if port < 0 || ip == nil {
		return nil, fmt.Errorf("IP and Port can't have negative values!")
	}
	return &BaseBackend{Ip: ip, Port: port}, nil
}

func (bb *BaseBackend) IsDown() bool {
	return bb.Down
}

func (bb *BaseBackend) Verify() {
	_, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", bb.Ip, bb.Port), time.Second*10)
	// Timeout... Set it as down!
	if err != nil {
		log.Println(err)
		if bb.Down == false {
			bb.LastValidCheck = time.Now()
		}
		bb.Down = true
		return
	}
	// No error
	bb.LastValidCheck = time.Now()
	return
}
<commit_msg>Added some new functions to Backend groups<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

type Backend interface {
	IsDown() bool
	Verify()
}

type BackendGroup interface {
	SelectBackend() Backend
	VerifyBackends()
	AddBackend(Backend) error
	RemoveBackend() error
}

type BaseBackend struct {
	Ip             net.IP
	Port           int16
	Down           bool
	LastValidCheck time.Time
}

type BaseBackendGroup struct {
	Group  []BaseBackend
	Method string
}

func NewBaseBackend(ip_string string, port int16) (*BaseBackend, error) {
	ip := net.ParseIP(ip_string)
	if port < 0 || ip == nil {
		return nil, fmt.Errorf("IP and Port can't have negative values!")
	}
	return &BaseBackend{Ip: ip, Port: port}, nil
}

func (bb *BaseBackend) IsDown() bool {
	return bb.Down
}

func (bb *BaseBackend) Verify() {
	_, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", bb.Ip, bb.Port), time.Second*10)
	// Timeout... Set it as down!
	if err != nil {
		log.Println(err)
		if bb.Down == false {
			bb.LastValidCheck = time.Now()
		}
		bb.Down = true
		return
	}
	// No error
	bb.LastValidCheck = time.Now()
	return
}
<|endoftext|>


<commit_before>package stager

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"sync"
	"text/template"
	"time"
)

// A backend represents one possible instance we can be proxying to.
// The public properties Port and Name can be used as configuration data.
type Backend struct {
	Port    int
	Name    string
	LastReq time.Time
	url     *url.URL
	proxy   *httputil.ReverseProxy
	state   State
	command *exec.Cmd
	notify  chan *Backend
}

// initialize starts the backend running.
func (b *Backend) initialize(command []string) {
	// setup prerequisite vars
	environ := os.Environ()
	environ = append(environ, fmt.Sprintf("STAGER_PORT=%d", b.Port), fmt.Sprintf("STAGER_NAME=%s", b.Name))

	// Build the command
	cmd := exec.Command(command[0], command[1:]...)
	cmd.Env = environ
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	if err != nil {
		panic(err)
	}
	b.command = cmd
	b.transition(StateStarted)
	go b.waiter()
}

func (b *Backend) transition(state State) {
	b.state = state
	b.notify <- b
}

// waiter runs in a goroutine waiting for process to end.
func (b *Backend) waiter() {
	for b.state == StateStarted {
		time.Sleep(300 * time.Millisecond)
		resp, err := http.Head(b.url.String())
		if err != nil {
			fmt.Printf("Backend %s didn't connect yet")
		} else if resp.StatusCode >= 500 {
			fmt.Printf("Backend got a ")
		} else {
			b.transition(StateRunning)
		}
	}
	b.transition(StateRunning)
	b.command.Wait()
	b.transition(StateFinished)
	b.command = nil
	fmt.Printf("Backend %s on port %d exited.\n", b.Name, b.Port)
}

// backendManager manages backends, allocating ports and backends as needed.
// Use the function newBackendManager to initialize properly.
type backendManager struct {
	sync.Mutex
	backends     map[string]*Backend
	suffixLength int
	availPorts   []int
	proxyPrefix  *template.Template
	initCommand  []string
	notify       chan *Backend
}

func (m *backendManager) get(domain string) (b *Backend, err error) {
	name := domain[:len(domain)-m.suffixLength]
	b = m.backends[name]
	if b == nil {
		var port int
		port, err = m.allocatePort()
		if err != nil {
			return
		}
		m.Lock()
		b = &Backend{
			Name:   name,
			Port:   port,
			notify: m.notify,
		}
		m.backends[name] = b
		m.Unlock()

		buf := &bytes.Buffer{}
		err = m.proxyPrefix.Execute(buf, b)
		if err != nil {
			return
		}
		rawurl := string(buf.Bytes())
		b.url, err = url.Parse(rawurl)
		if err != nil {
			return
		}
		fmt.Printf("making new instance %s on port %d with backend url %s\n", name, b.Port, rawurl)

		b.proxy = httputil.NewSingleHostReverseProxy(b.url)

		go b.initialize(m.initCommand)

	}
	return
}

/* Allocate a port number to be used for another backend. */
func (m *backendManager) allocatePort() (int, error) {
	m.Lock()
	defer m.Unlock()
	l := len(m.availPorts)
	if l > 0 {
		port := m.availPorts[l-1]
		m.availPorts = m.availPorts[:l-1]
		return port, nil
	} else {
		return 0, errors.New("Not enough ports remain")
	}
}

// Return a port number to the available ports slice
func (m *backendManager) returnPort(portNum int) {
	m.Lock()
	m.availPorts = append(m.availPorts, portNum)
	m.Unlock()
}

// watcher watches on the channel for things which happen
func (m *backendManager) watcher() {
	for backend := range m.notify {
		if backend.state == StateFinished {
			backend.state = StateReaped
			fmt.Printf("Got state finished transition\n")
			m.Lock()
			delete(m.backends, backend.Name)
			m.Unlock()
			m.returnPort(backend.Port)
		} else {
			fmt.Printf("Backend %s, state %d\n", backend.Name, backend.state)
		}
	}
}

func newBackendManager(config *Configuration) *backendManager {
	// Make a slice of all available ports
	ports := make([]int, 0, config.MaxInstances)
	for i := config.BasePort + config.MaxInstances - 1; i >= config.BasePort; i-- {
		ports = append(ports, i)
	}

	manager := &backendManager{
		backends:     make(map[string]*Backend),
		suffixLength: len(config.DomainSuffix),
		availPorts:   ports,
		proxyPrefix:  template.Must(template.New("p").Parse(config.ProxyFormat)),
		initCommand:  config.InitCommand,
		notify:       make(chan *Backend),
	}
	go manager.watcher()
	return manager
}
<commit_msg>Better logging<commit_after>package stager

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"sync"
	"text/template"
	"time"
)

// A backend represents one possible instance we can be proxying to.
// The public properties Port and Name can be used as configuration data.
type Backend struct {
	Port    int
	Name    string
	LastReq time.Time
	url     *url.URL
	proxy   *httputil.ReverseProxy
	state   State
	command *exec.Cmd
	notify  chan *Backend
}

// initialize starts the backend running.
func (b *Backend) initialize(command []string) {
	// setup prerequisite vars
	environ := os.Environ()
	environ = append(environ, fmt.Sprintf("STAGER_PORT=%d", b.Port), fmt.Sprintf("STAGER_NAME=%s", b.Name))

	// Build the command
	cmd := exec.Command(command[0], command[1:]...)
	cmd.Env = environ
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	if err != nil {
		panic(err)
	}
	b.command = cmd
	b.transition(StateStarted)
	go b.waiter()
}

func (b *Backend) transition(state State) {
	b.state = state
	b.notify <- b
}

// waiter runs in a goroutine waiting for process to end.
func (b *Backend) waiter() {
	for b.state == StateStarted {
		time.Sleep(300 * time.Millisecond)
		resp, err := http.Head(b.url.String())
		if err != nil {
			fmt.Printf("Backend %s didn't connect yet", b.Name)
		} else if resp.StatusCode >= 500 {
			fmt.Printf("Backend %s got a >5xx status code", b.Name)
		} else {
			b.transition(StateRunning)
		}
	}
	b.transition(StateRunning)
	b.command.Wait()
	b.transition(StateFinished)
	b.command = nil
	fmt.Printf("Backend %s on port %d exited.\n", b.Name, b.Port)
}

// backendManager manages backends, allocating ports and backends as needed.
// Use the function newBackendManager to initialize properly.
type backendManager struct {
	sync.Mutex
	backends     map[string]*Backend
	suffixLength int
	availPorts   []int
	proxyPrefix  *template.Template
	initCommand  []string
	notify       chan *Backend
}

func (m *backendManager) get(domain string) (b *Backend, err error) {
	name := domain[:len(domain)-m.suffixLength]
	b = m.backends[name]
	if b == nil {
		var port int
		port, err = m.allocatePort()
		if err != nil {
			return
		}
		m.Lock()
		b = &Backend{
			Name:   name,
			Port:   port,
			notify: m.notify,
		}
		m.backends[name] = b
		m.Unlock()

		buf := &bytes.Buffer{}
		err = m.proxyPrefix.Execute(buf, b)
		if err != nil {
			return
		}
		rawurl := string(buf.Bytes())
		b.url, err = url.Parse(rawurl)
		if err != nil {
			return
		}
		fmt.Printf("making new instance %s on port %d with backend url %s\n", name, b.Port, rawurl)

		b.proxy = httputil.NewSingleHostReverseProxy(b.url)

		go b.initialize(m.initCommand)

	}
	return
}

/* Allocate a port number to be used for another backend. */
func (m *backendManager) allocatePort() (int, error) {
	m.Lock()
	defer m.Unlock()
	l := len(m.availPorts)
	if l > 0 {
		port := m.availPorts[l-1]
		m.availPorts = m.availPorts[:l-1]
		return port, nil
	} else {
		return 0, errors.New("Not enough ports remain")
	}
}

// Return a port number to the available ports slice
func (m *backendManager) returnPort(portNum int) {
	m.Lock()
	m.availPorts = append(m.availPorts, portNum)
	m.Unlock()
}

// watcher watches on the channel for things which happen
func (m *backendManager) watcher() {
	for backend := range m.notify {
		if backend.state == StateFinished {
			backend.state = StateReaped
			fmt.Printf("Got state finished transition\n")
			m.Lock()
			delete(m.backends, backend.Name)
			m.Unlock()
			m.returnPort(backend.Port)
		} else {
			fmt.Printf("Backend %s, state %d\n", backend.Name, backend.state)
		}
	}
}

func newBackendManager(config *Configuration) *backendManager {
	// Make a slice of all available ports
	ports := make([]int, 0, config.MaxInstances)
	for i := config.BasePort + config.MaxInstances - 1; i >= config.BasePort; i-- {
		ports = append(ports, i)
	}

	manager := &backendManager{
		backends:     make(map[string]*Backend),
		suffixLength: len(config.DomainSuffix),
		availPorts:   ports,
		proxyPrefix:  template.Must(template.New("p").Parse(config.ProxyFormat)),
		initCommand:  config.InitCommand,
		notify:       make(chan *Backend),
	}
	go manager.watcher()
	return manager
}
<|endoftext|>


<commit_before>package persistence

type LocationType int64

const (
	LocationUnused LocationType = iota
	LocationFunction
	LocationBasicBlock
	LocationString
	LocationUnknown
)

func (l LocationType) String() string {
	switch l {
	case LocationFunction:
		return "LocationFunction"
	case LocationBasicBlock:
		return "LocationBasicBlock"
	case LocationString:
		return "LocationString"
	default:
		panic("unknown type")
	}
}

// AddressDataType
const (
	AddressDataUnused AddressDataType = iota
	LocationData
	FunctionData
	BasicBlockData
)

func (l AddressDataType) String() string {
	switch l {
	case LocationData:
		return "LocationData"
	case FunctionData:
		return "FunctionData"
	case BasicBlockData:
		return "BasicBlock"
	default:
		panic("unknown type")
	}
}

// AddressDataKeyString
const (
	KeyUnusedS AddressDataKeyS = iota
	FunctionName
)

func (l AddressDataKeyS) String() string {
	switch l {
	case FunctionName:
		return "FunctionName"
	default:
		panic("unknown type")
	}
}

// AddressDataKeyNumber
const (
	KeyUnusedI AddressDataKeyI = iota
	FunctionStackDelta
	TypeOfLocation
)

func (l AddressDataKeyI) String() string {
	switch l {
	case FunctionStackDelta:
		return "FunctionStackDelta"
	case TypeOfLocation:
		return "TypeOfLocation"
	default:
		panic("unknown type")
	}
}

// EdgeDataType
const (
	EdgeDataUnused EdgeDataType = iota
	// from basic block to basic block
	CodeXrefData
	// from instruction to VA (mem read/write),
	// or VA to VA (pointer
	DataXrefData
	// from function to function
	CallGraphData
)

func (l EdgeDataType) String() string {
	switch l {
	case CodeXrefData:
		return "CodeXrefData"
	case DataXrefData:
		return "DataXrefData"
	case CallGraphData:
		return "CallGraphData"
	default:
		panic("unknown type")
	}
}

// EdgeDataKeyString
const (
	EdgeKeyUnusedS EdgeDataKeyS = iota
	XrefName
)

func (l EdgeDataKeyS) String() string {
	switch l {
	case XrefName:
		return "XrefName"
	default:
		panic("unknown type")
	}
}

// EdgeDataKeyNumber
const (
	EdgeKeyUnusedI EdgeDataKeyI = iota
	XrefBranchType              // this some fake value so we can test
	XrefJumpType
)

func (l EdgeDataKeyI) String() string {
	switch l {
	case XrefBranchType:
		return "XrefBranchType"
	default:
		panic("unknown type")
	}
}

type JumpType int64

// JumpType defines the possible types of intra-function edges.
const (
	JumpTypeUnused JumpType = iota
	// JumpTypeCondTrue is the JumpType that represents the True
	//  edge of a conditional branch.
	JumpTypeCondTrue
	// JumpTypeCondFalse is the JumpType that represents the False
	//  edge of a conditional branch.
	JumpTypeCondFalse
	// JumpTypeUncond is the JumpType that represents the edge of
	//  an unconditional branch.
	JumpTypeUncond
	JumpTypeSwitch
)

func (t JumpType) String() string {
	switch t {
	case JumpTypeCondTrue:
		return "JumpTypeCondTrue"
	case JumpTypeCondFalse:
		return "JumpTypeCondFalse"
	case JumpTypeUncond:
		return "JumpTypeUncond"
	case JumpTypeSwitch:
		return "JumpTypeSwitch"
	default:
		panic("unexpected JumpType")
	}
}
<commit_msg>const: add stringer for XrefJumpType<commit_after>package persistence

type LocationType int64

const (
	LocationUnused LocationType = iota
	LocationFunction
	LocationBasicBlock
	LocationString
	LocationUnknown
)

func (l LocationType) String() string {
	switch l {
	case LocationFunction:
		return "LocationFunction"
	case LocationBasicBlock:
		return "LocationBasicBlock"
	case LocationString:
		return "LocationString"
	default:
		panic("unknown type")
	}
}

// AddressDataType
const (
	AddressDataUnused AddressDataType = iota
	LocationData
	FunctionData
	BasicBlockData
)

func (l AddressDataType) String() string {
	switch l {
	case LocationData:
		return "LocationData"
	case FunctionData:
		return "FunctionData"
	case BasicBlockData:
		return "BasicBlock"
	default:
		panic("unknown type")
	}
}

// AddressDataKeyString
const (
	KeyUnusedS AddressDataKeyS = iota
	FunctionName
)

func (l AddressDataKeyS) String() string {
	switch l {
	case FunctionName:
		return "FunctionName"
	default:
		panic("unknown type")
	}
}

// AddressDataKeyNumber
const (
	KeyUnusedI AddressDataKeyI = iota
	FunctionStackDelta
	TypeOfLocation
)

func (l AddressDataKeyI) String() string {
	switch l {
	case FunctionStackDelta:
		return "FunctionStackDelta"
	case TypeOfLocation:
		return "TypeOfLocation"
	default:
		panic("unknown type")
	}
}

// EdgeDataType
const (
	EdgeDataUnused EdgeDataType = iota
	// from basic block to basic block
	CodeXrefData
	// from instruction to VA (mem read/write),
	// or VA to VA (pointer
	DataXrefData
	// from function to function
	CallGraphData
)

func (l EdgeDataType) String() string {
	switch l {
	case CodeXrefData:
		return "CodeXrefData"
	case DataXrefData:
		return "DataXrefData"
	case CallGraphData:
		return "CallGraphData"
	default:
		panic("unknown type")
	}
}

// EdgeDataKeyString
const (
	EdgeKeyUnusedS EdgeDataKeyS = iota
	XrefName
)

func (l EdgeDataKeyS) String() string {
	switch l {
	case XrefName:
		return "XrefName"
	default:
		panic("unknown type")
	}
}

// EdgeDataKeyNumber
const (
	EdgeKeyUnusedI EdgeDataKeyI = iota
	XrefBranchType              // this some fake value so we can test
	XrefJumpType
)

func (l EdgeDataKeyI) String() string {
	switch l {
	case XrefBranchType:
		return "XrefBranchType"
	case XrefJumpType:
		return "XrefJumpType"
	default:
		panic("unknown type")
	}
}

type JumpType int64

// JumpType defines the possible types of intra-function edges.
const (
	JumpTypeUnused JumpType = iota
	// JumpTypeCondTrue is the JumpType that represents the True
	//  edge of a conditional branch.
	JumpTypeCondTrue
	// JumpTypeCondFalse is the JumpType that represents the False
	//  edge of a conditional branch.
	JumpTypeCondFalse
	// JumpTypeUncond is the JumpType that represents the edge of
	//  an unconditional branch.
	JumpTypeUncond
	JumpTypeSwitch
)

func (t JumpType) String() string {
	switch t {
	case JumpTypeCondTrue:
		return "JumpTypeCondTrue"
	case JumpTypeCondFalse:
		return "JumpTypeCondFalse"
	case JumpTypeUncond:
		return "JumpTypeUncond"
	case JumpTypeSwitch:
		return "JumpTypeSwitch"
	default:
		panic("unexpected JumpType")
	}
}
<|endoftext|>


<commit_before>package stripe

import (
	"net/url"
	"strconv"
)

// Customer encapsulates details about a Customer registered in Stripe.
//
// see https://stripe.com/docs/api#customer_object
type Customer struct {
	ID            string            `json:"id"`
	Description   string            `json:"description,omitempty"`
	Email         string            `json:"email,omitempty"`
	Created       UnixTime          `json:"created"`
	Balance       int               `json:"account_balance,omitempty"`
	Currency      string            `json:"currency"`
	Delinquent    bool              `json:"delinquent,omitempty"`
	Cards         *CardList         `json:"cards,omitempty"`
	Discount      *Discount         `json:"discount,omitempty"`
	Subscriptions *SubscriptionList `json:"subscriptions,omitempty"`
	Livemode      bool              `json:"livemode"`
	DefaultCard   string            `json:"default_card"`
	Metadata      map[string]string `json:"metadata,omitempty"`
}

type ListObject struct {
	Count int  `json:"total_count"`
	More  bool `json:"has_more"`
}

type SubscriptionList struct {
	ListObject
	Data []*Subscription `json:"data"`
}

type CardList struct {
	ListObject
	Data []*Card `json:"data"`
}

// Discount represents the actual application of a coupon to a particular
// customer.
//
// see https://stripe.com/docs/api#discount_object
type Discount struct {
	Customer     string    `json:"customer"`
	Start        UnixTime  `json:"start"`
	End          *UnixTime `json:"end,omitempty"`
	Coupon       *Coupon   `json:"coupon"`
	Subscription string    `json:"subscription,omitempty"`
}

// CustomerParams encapsulates options for creating and updating Customers.
type CustomerParams struct {
	// (Optional) The customer's email address.
	Email string

	// (Optional) An arbitrary string which you can attach to a customer object.
	Description string

	// (Optional) Customer's Active Credit Card
	Card *CardParams

	// (Optional) Customer's Active Credid Card, using a Card Token
	Token string

	// (Optional) If you provide a coupon code, the customer will have a
	// discount applied on all recurring charges.
	Coupon string

	// (Optional) The identifier of the plan to subscribe the customer to. If
	// provided, the returned customer object has a 'subscription' attribute
	// describing the state of the customer's subscription.
	Plan string

	// (Optional) The quantity you’d like to apply to the subscription you’re creating.
	Quantity int

	// (Optional) timestamp representing the end of the trial period
	// the customer will get before being charged for the first time.
	TrialEnd *UnixTime

	// (Optional) Customer's account balance. Negative is credit, positive is added to the next invoice.
	Balance *int

	// (Optional) Customer's default card id.
	DefaultCard string

	// (Optional) Metadata.
	Metadata map[string]string
}

// CustomerClient encapsulates operations for creating, updating, deleting and
// querying customers using the Stripe REST API.
type CustomerClient struct{}

// Creates a new Customer.
//
// see https://stripe.com/docs/api#create_customer
func (c *CustomerClient) Create(cust *CustomerParams) (*Customer, error) {
	customer := Customer{}
	params := make(url.Values)
	appendCustomerParams(params, cust)

	err := query("POST", "/customers", params, &customer)
	return &customer, err
}

// Retrieves a Customer with the given ID.
//
// see https://stripe.com/docs/api#retrieve_customer
func (c *CustomerClient) Retrieve(id string) (*Customer, error) {
	customer := Customer{}
	path := "/customers/" + url.QueryEscape(id)
	err := query("GET", path, nil, &customer)
	return &customer, err
}

// Updates a Customer with the given ID.
//
// see https://stripe.com/docs/api#update_customer
func (c *CustomerClient) Update(id string, cust *CustomerParams) (*Customer, error) {
	customer := Customer{}
	params := make(url.Values)
	appendCustomerParams(params, cust)

	err := query("POST", "/customers/"+url.QueryEscape(id), params, &customer)
	return &customer, err
}

// Deletes a Customer (permanently) with the given ID.
//
// see https://stripe.com/docs/api#delete_customer
func (c *CustomerClient) Delete(id string) (bool, error) {
	resp := DeleteResp{}
	path := "/customers/" + url.QueryEscape(id)
	if err := query("DELETE", path, nil, &resp); err != nil {
		return false, err
	}
	return resp.Deleted, nil
}

// Returns a list of your Customers.
//
// see https://stripe.com/docs/api#list_customers
func (c *CustomerClient) List() ([]*Customer, error) {
	return c.ListN(10, 0)
}

// Returns a list of your Customers at the specified range.
//
// see https://stripe.com/docs/api#list_customers
func (c *CustomerClient) ListN(count int, offset int) ([]*Customer, error) {
	// define a wrapper function for the Customer List, so that we can
	// cleanly parse the JSON
	type listCustomerResp struct{ Data []*Customer }
	resp := listCustomerResp{}

	// add the count and offset to the list of url values
	values := url.Values{
		"count":  {strconv.Itoa(count)},
		"offset": {strconv.Itoa(offset)},
	}

	err := query("GET", "/customers", values, &resp)
	if err != nil {
		return nil, err
	}
	return resp.Data, nil
}

////////////////////////////////////////////////////////////////////////////////
// Helper Function(s)

func appendCustomerParams(values url.Values, c *CustomerParams) {
	// add optional parameters, if specified
	if c.Email != "" {
		values.Add("email", c.Email)
	}
	if c.Description != "" {
		values.Add("description", c.Description)
	}
	if c.Coupon != "" {
		values.Add("coupon", c.Coupon)
	}
	if c.Plan != "" {
		values.Add("plan", c.Plan)
	}
	if c.TrialEnd != nil {
		values.Add("trial_end", strconv.FormatInt(c.TrialEnd.Unix(), 10))
	}
	if c.Balance != nil {
		values.Add("account_balance", strconv.Itoa(*c.Balance))
	}
	if c.DefaultCard != "" {
		values.Add("default_card", c.DefaultCard)
	}
	appendMetadata(values, c.Metadata)

	// add optional credit card details, if specified
	if c.Card != nil {
		appendCardParams(values, c.Card)
	} else if c.Token != "" {
		values.Add("card", c.Token)
	}
}

func appendCardParams(values url.Values, c *CardParams) {
	if c.Number != "" {
		values.Add("card[number]", c.Number)
	}
	if c.ExpMonth != 0 {
		values.Add("card[exp_month]", strconv.Itoa(c.ExpMonth))
	}
	if c.ExpMonth != 0 {
		values.Add("card[exp_year]", strconv.Itoa(c.ExpYear))
	}
	if c.Name != "" {
		values.Add("card[name]", c.Name)
	}
	if c.CVC != "" {
		values.Add("card[cvc]", c.CVC)
	}
	if c.Address1 != "" {
		values.Add("card[address_line1]", c.Address1)
	}
	if c.Address2 != "" {
		values.Add("card[address_line2]", c.Address2)
	}
	if c.AddressZip != "" {
		values.Add("card[address_zip]", c.AddressZip)
	}
	if c.AddressState != "" {
		values.Add("card[address_state]", c.AddressState)
	}
	if c.AddressCountry != "" {
		values.Add("card[address_country]", c.AddressCountry)
	}
}
<commit_msg>Add Create/Update/Delete customer card<commit_after>package stripe

import (
	"fmt"
	"net/url"
	"strconv"
)

// Customer encapsulates details about a Customer registered in Stripe.
//
// see https://stripe.com/docs/api#customer_object
type Customer struct {
	ID            string            `json:"id"`
	Description   string            `json:"description,omitempty"`
	Email         string            `json:"email,omitempty"`
	Created       UnixTime          `json:"created"`
	Balance       int               `json:"account_balance,omitempty"`
	Currency      string            `json:"currency"`
	Delinquent    bool              `json:"delinquent,omitempty"`
	Cards         *CardList         `json:"cards,omitempty"`
	Discount      *Discount         `json:"discount,omitempty"`
	Subscriptions *SubscriptionList `json:"subscriptions,omitempty"`
	Livemode      bool              `json:"livemode"`
	DefaultCard   string            `json:"default_card"`
	Metadata      map[string]string `json:"metadata,omitempty"`
}

type ListObject struct {
	Count int  `json:"total_count"`
	More  bool `json:"has_more"`
}

type SubscriptionList struct {
	ListObject
	Data []*Subscription `json:"data"`
}

type CardList struct {
	ListObject
	Data []*Card `json:"data"`
}

// Discount represents the actual application of a coupon to a particular
// customer.
//
// see https://stripe.com/docs/api#discount_object
type Discount struct {
	Customer     string    `json:"customer"`
	Start        UnixTime  `json:"start"`
	End          *UnixTime `json:"end,omitempty"`
	Coupon       *Coupon   `json:"coupon"`
	Subscription string    `json:"subscription,omitempty"`
}

// CustomerParams encapsulates options for creating and updating Customers.
type CustomerParams struct {
	// (Optional) The customer's email address.
	Email string

	// (Optional) An arbitrary string which you can attach to a customer object.
	Description string

	// (Optional) Customer's Active Credit Card
	Card *CardParams

	// (Optional) Customer's Active Credid Card, using a Card Token
	Token string

	// (Optional) If you provide a coupon code, the customer will have a
	// discount applied on all recurring charges.
	Coupon string

	// (Optional) The identifier of the plan to subscribe the customer to. If
	// provided, the returned customer object has a 'subscription' attribute
	// describing the state of the customer's subscription.
	Plan string

	// (Optional) The quantity you’d like to apply to the subscription you’re creating.
	Quantity int

	// (Optional) timestamp representing the end of the trial period
	// the customer will get before being charged for the first time.
	TrialEnd *UnixTime

	// (Optional) Customer's account balance. Negative is credit, positive is added to the next invoice.
	Balance *int

	// (Optional) Customer's default card id.
	DefaultCard string

	// (Optional) Metadata.
	Metadata map[string]string
}

// CustomerClient encapsulates operations for creating, updating, deleting and
// querying customers using the Stripe REST API.
type CustomerClient struct{}

// Creates a new Customer.
//
// see https://stripe.com/docs/api#create_customer
func (c *CustomerClient) Create(cust *CustomerParams) (*Customer, error) {
	customer := Customer{}
	params := make(url.Values)
	appendCustomerParams(params, cust)

	err := query("POST", "/customers", params, &customer)
	return &customer, err
}

// Retrieves a Customer with the given ID.
//
// see https://stripe.com/docs/api#retrieve_customer
func (c *CustomerClient) Retrieve(id string) (*Customer, error) {
	customer := Customer{}
	path := "/customers/" + url.QueryEscape(id)
	err := query("GET", path, nil, &customer)
	return &customer, err
}

// Updates a Customer with the given ID.
//
// see https://stripe.com/docs/api#update_customer
func (c *CustomerClient) Update(id string, cust *CustomerParams) (*Customer, error) {
	customer := Customer{}
	params := make(url.Values)
	appendCustomerParams(params, cust)

	err := query("POST", "/customers/"+url.QueryEscape(id), params, &customer)
	return &customer, err
}

func (c *CustomerClient) CreateCard(customerID, token string, card *CardParams) (*Card, error) {
	params := make(url.Values)
	if token != "" {
		params.Add("card", token)
	} else {
		appendCardParams(params, card)
	}
	res := &Card{}
	return res, query("POST", fmt.Sprintf("/customers/%s/cards", url.QueryEscape(customerID)), params, res)
}

func (c *CustomerClient) UpdateCard(customerID, cardID string, card *CardParams) (*Card, error) {
	params := make(url.Values)
	appendCardParams(params, card)
	res := &Card{}
	return res, query("POST", fmt.Sprintf("/customers/%s/cards/%s", url.QueryEscape(customerID), url.QueryEscape(cardID)), params, res)
}

func (c *CustomerClient) DeleteCard(customerID, cardID string) (bool, error) {
	res := &DeleteResp{}
	err := query("DELETE", fmt.Sprintf("/customers/%s/cards/%s", url.QueryEscape(customerID), url.QueryEscape(cardID)), nil, res)
	return res.Deleted, err
}

// Deletes a Customer (permanently) with the given ID.
//
// see https://stripe.com/docs/api#delete_customer
func (c *CustomerClient) Delete(id string) (bool, error) {
	resp := DeleteResp{}
	err := query("DELETE", "/customers/"+url.QueryEscape(id), nil, &resp)
	return resp.Deleted, err
}

// Returns a list of your Customers.
//
// see https://stripe.com/docs/api#list_customers
func (c *CustomerClient) List() ([]*Customer, error) {
	return c.ListN(10, 0)
}

// Returns a list of your Customers at the specified range.
//
// see https://stripe.com/docs/api#list_customers
func (c *CustomerClient) ListN(count int, offset int) ([]*Customer, error) {
	// define a wrapper function for the Customer List, so that we can
	// cleanly parse the JSON
	type listCustomerResp struct{ Data []*Customer }
	resp := listCustomerResp{}

	// add the count and offset to the list of url values
	values := url.Values{
		"count":  {strconv.Itoa(count)},
		"offset": {strconv.Itoa(offset)},
	}

	err := query("GET", "/customers", values, &resp)
	if err != nil {
		return nil, err
	}
	return resp.Data, nil
}

////////////////////////////////////////////////////////////////////////////////
// Helper Function(s)

func appendCustomerParams(values url.Values, c *CustomerParams) {
	// add optional parameters, if specified
	if c.Email != "" {
		values.Add("email", c.Email)
	}
	if c.Description != "" {
		values.Add("description", c.Description)
	}
	if c.Coupon != "" {
		values.Add("coupon", c.Coupon)
	}
	if c.Plan != "" {
		values.Add("plan", c.Plan)
	}
	if c.TrialEnd != nil {
		values.Add("trial_end", strconv.FormatInt(c.TrialEnd.Unix(), 10))
	}
	if c.Balance != nil {
		values.Add("account_balance", strconv.Itoa(*c.Balance))
	}
	if c.DefaultCard != "" {
		values.Add("default_card", c.DefaultCard)
	}
	appendMetadata(values, c.Metadata)

	// add optional credit card details, if specified
	if c.Card != nil {
		appendCardParams(values, c.Card)
	} else if c.Token != "" {
		values.Add("card", c.Token)
	}
}

func appendCardParams(values url.Values, c *CardParams) {
	if c.Number != "" {
		values.Add("card[number]", c.Number)
	}
	if c.ExpMonth != 0 {
		values.Add("card[exp_month]", strconv.Itoa(c.ExpMonth))
	}
	if c.ExpMonth != 0 {
		values.Add("card[exp_year]", strconv.Itoa(c.ExpYear))
	}
	if c.Name != "" {
		values.Add("card[name]", c.Name)
	}
	if c.CVC != "" {
		values.Add("card[cvc]", c.CVC)
	}
	if c.Address1 != "" {
		values.Add("card[address_line1]", c.Address1)
	}
	if c.Address2 != "" {
		values.Add("card[address_line2]", c.Address2)
	}
	if c.AddressZip != "" {
		values.Add("card[address_zip]", c.AddressZip)
	}
	if c.AddressState != "" {
		values.Add("card[address_state]", c.AddressState)
	}
	if c.AddressCountry != "" {
		values.Add("card[address_country]", c.AddressCountry)
	}
}
<|endoftext|>


<commit_before>package datastore

import (
	"log"
)

// Here are some basic datastore implementations.

// MapDatastore uses a standard Go map for internal storage.
type keyMap map[Key]interface{}
type MapDatastore struct {
	values keyMap
}

func NewMapDatastore() (d *MapDatastore) {
	return &MapDatastore{
		values: keyMap{},
	}
}

func (d *MapDatastore) Put(key Key, value interface{}) (err error) {
	d.values[key] = value
	return nil
}

func (d *MapDatastore) Get(key Key) (value interface{}, err error) {
	val, found := d.values[key]
	if !found {
		return nil, ErrNotFound
	}
	return val, nil
}

func (d *MapDatastore) Has(key Key) (exists bool, err error) {
	_, found := d.values[key]
	return found, nil
}

func (d *MapDatastore) Delete(key Key) (err error) {
	delete(d.values, key)
	return nil
}

// NullDatastore stores nothing, but conforms to the API.
// Useful to test with.
type NullDatastore struct {
}

func NewNullDatastore() *NullDatastore {
	return &NullDatastore{}
}

func (d *NullDatastore) Put(key Key, value interface{}) (err error) {
	return nil
}

func (d *NullDatastore) Get(key Key) (value interface{}, err error) {
	return nil, nil
}

func (d *NullDatastore) Has(key Key) (exists bool, err error) {
	return false, nil
}

func (d *NullDatastore) Delete(key Key) (err error) {
	return nil
}

// LogDatastore logs all accesses through the datastore.
type LogDatastore struct {
	Name string
	Child Datastore
}

func NewLogDatastore(ds Datastore, name string) *LogDatastore {
	if len(name) < 1 {
		name = "LogDatastore"
	}
	return &LogDatastore{Name: name, Child: ds}
}

func (d *LogDatastore) Put(key Key, value interface{}) (err error) {
	log.Printf("%s: Put %s", d.Name, key)
	log.Printf("%s: Put %s ```%s```", d.Name, key, value)
	return d.Child.Put(key, value)
}

func (d *LogDatastore) Get(key Key) (value interface{}, err error) {
	log.Printf("%s: Get %s", d.Name, key)
	return d.Child.Get(key)
}

func (d *LogDatastore) Has(key Key) (exists bool, err error) {
	log.Printf("%s: Has %s", d.Name, key)
	return d.Child.Has(key)
}

func (d *LogDatastore) Delete(key Key) (err error) {
	log.Printf("%s: Delete %s", d.Name, key)
	return d.Child.Delete(key)
}
<commit_msg>log: dont print val<commit_after>package datastore

import (
	"log"
)

// Here are some basic datastore implementations.

// MapDatastore uses a standard Go map for internal storage.
type keyMap map[Key]interface{}
type MapDatastore struct {
	values keyMap
}

func NewMapDatastore() (d *MapDatastore) {
	return &MapDatastore{
		values: keyMap{},
	}
}

func (d *MapDatastore) Put(key Key, value interface{}) (err error) {
	d.values[key] = value
	return nil
}

func (d *MapDatastore) Get(key Key) (value interface{}, err error) {
	val, found := d.values[key]
	if !found {
		return nil, ErrNotFound
	}
	return val, nil
}

func (d *MapDatastore) Has(key Key) (exists bool, err error) {
	_, found := d.values[key]
	return found, nil
}

func (d *MapDatastore) Delete(key Key) (err error) {
	delete(d.values, key)
	return nil
}

// NullDatastore stores nothing, but conforms to the API.
// Useful to test with.
type NullDatastore struct {
}

func NewNullDatastore() *NullDatastore {
	return &NullDatastore{}
}

func (d *NullDatastore) Put(key Key, value interface{}) (err error) {
	return nil
}

func (d *NullDatastore) Get(key Key) (value interface{}, err error) {
	return nil, nil
}

func (d *NullDatastore) Has(key Key) (exists bool, err error) {
	return false, nil
}

func (d *NullDatastore) Delete(key Key) (err error) {
	return nil
}

// LogDatastore logs all accesses through the datastore.
type LogDatastore struct {
	Name string
	Child Datastore
}

func NewLogDatastore(ds Datastore, name string) *LogDatastore {
	if len(name) < 1 {
		name = "LogDatastore"
	}
	return &LogDatastore{Name: name, Child: ds}
}

func (d *LogDatastore) Put(key Key, value interface{}) (err error) {
	log.Printf("%s: Put %s", d.Name, key)
	// log.Printf("%s: Put %s ```%s```", d.Name, key, value)
	return d.Child.Put(key, value)
}

func (d *LogDatastore) Get(key Key) (value interface{}, err error) {
	log.Printf("%s: Get %s", d.Name, key)
	return d.Child.Get(key)
}

func (d *LogDatastore) Has(key Key) (exists bool, err error) {
	log.Printf("%s: Has %s", d.Name, key)
	return d.Child.Has(key)
}

func (d *LogDatastore) Delete(key Key) (err error) {
	log.Printf("%s: Delete %s", d.Name, key)
	return d.Child.Delete(key)
}
<|endoftext|>


<commit_before>package gondole

import (
	"testing"
	"github.com/stretchr/testify/assert"
	"reflect"
)

func TestNewApp(t *testing.T) {
	g, err := NewApp("foo", "bar")
	assert.NoError(t, err, "no error")
	assert.Equal(t, reflect.TypeOf(&Gondole{}), reflect.TypeOf(g), "should be Gondole")

	assert.Equal(t, "foo", g.Name, "should be equal")
}
<commit_msg>Proper testing requires "mock"-ing the API call… Later I guess.<commit_after>package gondole

import (
	"testing"
	//"github.com/stretchr/testify/assert"
)

func TestNewApp(t *testing.T) {
	//g, err := NewApp("gondole-cli", ourScopes, NoRedirect)
	//assert.NoError(t, err, "no error")
	//assert.Equal(t, "gondole-cli", g.Name, "should be equal")
}
<|endoftext|>