func (tbs *tokenBucketSet) debugState() string {
	periods := sort.IntSlice(make([]int, 0, len(tbs.buckets)))
	for period := range tbs.buckets {
		periods = append(periods, int(period))
	}
	sort.Sort(periods)
	bucketRepr := make([]string, 0, len(tbs.buckets))
	for _, period := range periods {
		bucket := tbs.buckets[time.Duration(period)]
		bucketRepr = append(bucketRepr, fmt.Sprintf("{%v: %v}", bucket.period, bucket.availableTokens))
	}
	return strings.Join(bucketRepr, ", ")
}
debugState returns string that reflects the current state of all buckets in
this set. It is intended to be used for debugging and testing only.
package tokenbucket
import (
	"fmt"
	"strings"
	"time"
	"github.com/mailgun/timetools"
	"sort")
type tokenBucketSet struct {
	buckets   map[time.Duration]*tokenBucket
	maxPeriod time.Duration
	clock     timetools.TimeProvider
}
func newTokenBucketSet(rates *RateSet, clock timetools.TimeProvider) *tokenBucketSet {
	tbs := new(tokenBucketSet)
	tbs.clock = clock
	tbs.buckets = make(map[time.Duration]*tokenBucket, len(rates.m))
	for _, rate := range rates.m {
		newBucket := newTokenBucket(rate, clock)
		tbs.buckets[rate.period] = newBucket
		tbs.maxPeriod = maxDuration(tbs.maxPeriod, rate.period)
	}
	return tbs
}
func (tbs *tokenBucketSet) update(rates *RateSet) {
	for _, bucket := range tbs.buckets {
		if rate, ok := rates.m[bucket.period]; ok {
			bucket.update(rate)
		} else {
			delete(tbs.buckets, bucket.period)
		}
	}
	for _, rate := range rates.m {
		if _, ok := tbs.buckets[rate.period]; !ok {
			newBucket := newTokenBucket(rate, tbs.clock)
			tbs.buckets[rate.period] = newBucket
		}
	}
	tbs.maxPeriod = 0
	for _, bucket := range tbs.buckets {
		tbs.maxPeriod = maxDuration(tbs.maxPeriod, bucket.period)
	}
}
func (tbs *tokenBucketSet) consume(tokens int64) (time.Duration, error) {
	var maxDelay time.Duration = UndefinedDelay
	var firstErr error = nil
	for _, tokenBucket := range tbs.buckets {
		delay, err := tokenBucket.consume(tokens)
		if firstErr == nil {
			if err != nil {
				firstErr = err
			} else {
				maxDelay = maxDuration(maxDelay, delay)
			}
		}
	}
	if firstErr != nil || maxDelay > 0 {
		for _, tokenBucket := range tbs.buckets {
			tokenBucket.rollback()
		}
	}
	return maxDelay, firstErr
}