<commit_before>package codegen

import (
	"strings"
	"text/template"
)

var headerTemplate *template.Template
var structHeaderTemplate *template.Template
var typedPropertyTemplate *template.Template
var readerTemplate *template.Template
var readSetterTemplate *template.Template
var readSetConfigurerTemplate *template.Template
var enumHeaderTemplate *template.Template
var enumDelegateTemplate *template.Template
var enumItemTemplate *template.Template

func init() {
	funcMap := template.FuncMap{
		"withimmutable": withImmutable,
		"ismutable":     isMutable,
		"verbfortype":   verbForType,
		"firstLetter":   firstLetter,
	}

	headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(headerTemplateText))
	structHeaderTemplate = template.Must(template.New("structHeader").Funcs(funcMap).Parse(structHeaderTemplateText))
	typedPropertyTemplate = template.Must(template.New("typedProperty").Funcs(funcMap).Parse(typedPropertyTemplateText))
	readerTemplate = template.Must(template.New("reader").Funcs(funcMap).Parse(readerTemplateText))
	readSetterTemplate = template.Must(template.New("readsetter").Funcs(funcMap).Parse(readSetterTemplateText))
	readSetConfigurerTemplate = template.Must(template.New("readsetconfigurer").Funcs(funcMap).Parse(readSetConfigurerTemplateText))
	enumHeaderTemplate = template.Must(template.New("enumheader").Funcs(funcMap).Parse(enumHeaderTemplateText))
	enumDelegateTemplate = template.Must(template.New("enumdelegate").Funcs(funcMap).Parse(enumDelegateTemplateText))
	enumItemTemplate = template.Must(template.New("enumitem").Parse(enumItemTemplateText))
}

func withImmutable(in string) string {
	prefix := ""
	rest := in
	parts := strings.Split(in, ".")
	if len(parts) > 1 {
		prefix = strings.Join(parts[:len(parts)-1], ".")
		rest = parts[len(parts)-1]
	}

	if _, needsImmutable := configureTypes[rest]; !needsImmutable {
		return in
	}

	rest = "Immutable" + rest

	if prefix == "" {
		return rest
	}

	return prefix + "." + rest

}

func isMutable(in string) bool {

	for key := range configureTypes {
		if strings.Contains(in, key) {
			return true
		}
	}

	return false
}

var configureTypes = map[string]bool{
	"Stack": true,
	"Timer": true,
	"Board": true,
	"Val":   true,
	"Enum":  true,
}

func verbForType(in string) string {
	_, configure := configureTypes[in]
	if configure {
		return "Configure"
	}
	return "Set"
}

func firstLetter(in string) string {

	if in == "" {
		return ""
	}

	return strings.ToLower(in[:1])
}

const headerTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help certain structs
 * implement boardgame.PropertyReader and friends. It was generated
 * by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}
`

const importText = `import (
	"errors"

	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/enum"
)

`

const structHeaderTemplateText = `// Implementation for {{.StructName}}

var {{.ReaderName}}Props = map[string]boardgame.PropertyType{
	{{range $key, $value := .Fields -}}
		"{{$key}}": boardgame.{{$value.Type.String}},
	{{end}}
}

type {{.ReaderName}} struct {
	data *{{.StructName}}
}

func ({{.FirstLetter}} *{{.ReaderName}}) Props() map[string]boardgame.PropertyType {
	return {{.ReaderName}}Props
}

func ({{.FirstLetter}} *{{.ReaderName}}) Prop(name string) (interface{}, error) {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return nil, errors.New("No such property with that name: " + name)
	}

	{{$firstLetter := .FirstLetter}}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		return {{$firstLetter}}.{{withimmutable $type.Key}}Prop(name)
	{{end}}
	}

	return nil, errors.New("Unexpected property type: " + propType.String())
}

{{if .OutputReadSetter -}}

func ({{.FirstLetter}} *{{.ReaderName}}) PropMutable(name string) bool {
	switch name {
		{{range $key, $val := .Fields -}}
	case "{{$key}}":
		return {{$val.Mutable}}
		{{end -}}
	}

	return false
}

func ({{.FirstLetter}} *{{.ReaderName}}) SetProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	{{if $type.IsInterface -}}
	case boardgame.Type{{$type.Key}}:
		return errors.New("SetProp does not allow setting mutable types; use ConfigureProp instead")
	{{- else -}}
	case boardgame.Type{{$type.Key}}:
		val, ok := value.({{$type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type.Key}}{{$type.Key}}Prop(name, val)
	{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}

{{if .OutputReadSetConfigurer -}}
func ({{.FirstLetter}} *{{.ReaderName}}) ConfigureProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		{{if $type.IsInterface -}}
		if {{$firstLetter}}.PropMutable(name) {
			//Mutable variant
			val, ok := value.({{$type.MutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.MutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type.Key}}{{$type.Key}}Prop(name, val)
		}
		//Immutable variant
		val, ok := value.({{withimmutable $type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{withimmutable $type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type.Key}}{{withimmutable $type.Key}}Prop(name, val)
		{{- else -}}
			val, ok := value.({{$type.ImmutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type.Key}}{{$type.Key}}Prop(name, val)
		{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}
`

const typedPropertyTemplateText = `func ({{.FirstLetter}} *{{.ReaderName}}) {{withimmutable .PropType.Key}}Prop(name string) ({{.PropType.ImmutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				return {{$firstLetter}}.data.{{.Name}}, nil
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{if .OutputReadSetConfigurer -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- else -}}
				return boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{withimmutable .PropType.Key}}Prop(name string, value {{withimmutable .PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return boardgame.ErrPropertyImmutable
			{{- else -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{withimmutable .PropType.Key}} prop: " + name)

}

{{end}}
{{end}}

{{if .OutputReadSetter -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) {{.PropType.Key}}Prop(name string) ({{.PropType.MutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{$zeroValue := .PropType.ZeroValue}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return {{$firstLetter}}.data.{{.Name}}, nil
			{{- else -}}
				return {{$zeroValue}}, boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{else}}
func ({{.FirstLetter}} *{{.ReaderName}}) Set{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				{{$firstLetter}}.data.{{.Name}} = value
				return nil
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{end}}
{{end}}
`

const readerTemplateText = `//Reader returns an autp-generated boardgame.PropertyReader for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) Reader() boardgame.PropertyReader {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetterTemplateText = `//ReadSetter returns an autp-generated boardgame.PropertyReadSetter for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetter() boardgame.PropertyReadSetter {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetConfigurerTemplateText = `//ReadSetConfigurer returns an autp-generated boardgame.PropertyReadSetConfigurer for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetConfigurer() boardgame.PropertyReadSetConfigurer {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`
const enumHeaderTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help configure enums.
 * It was generated by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}

import (
	"github.com/jkomoros/boardgame/enum"
)

var enums = enum.NewSet()

`

const enumDelegateTemplateText = `//ConfigureEnums simply returns enums, the auto-generated Enums variable. This
//is output because {{.delegateName}} appears to be a struct that implements
//boardgame.GameDelegate, and does not already have a ConfigureEnums
//explicitly defined.
func ({{firstLetter .delegateName}} *{{.delegateName}}) ConfigureEnums() *enum.Set {
	return enums
}

`

const enumItemTemplateText = `{{if .firstNewKey}}
//Implicitly created constants for {{.prefix}}
const (
	{{.firstNewKey}} = iota - 9223372036854775808
{{range .restNewKeys -}}
	{{.}}
{{- end -}}
)

{{ end -}}
//{{.prefix}}Enum is the enum.Enum for {{.prefix}}
var {{.prefix}}Enum = enums.MustAdd{{if .parents}}Tree{{end}}("{{.prefix}}", map[int]string{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .values -}}
	{{$name}}: "{{$value}}",
	{{end}}
{{if .parents -}} }, map[int]int{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .parents -}}
	{{$name}}: {{$value}},
	{{end}}
{{end -}}
})

`
<commit_msg>Make verbForType not use a custom map of type names and instead be based on Type.IsInterface(), which is less error prone if property types change. Part of #746.<commit_after>package codegen

import (
	"strings"
	"text/template"

	"github.com/jkomoros/boardgame"
)

var headerTemplate *template.Template
var structHeaderTemplate *template.Template
var typedPropertyTemplate *template.Template
var readerTemplate *template.Template
var readSetterTemplate *template.Template
var readSetConfigurerTemplate *template.Template
var enumHeaderTemplate *template.Template
var enumDelegateTemplate *template.Template
var enumItemTemplate *template.Template

func init() {
	funcMap := template.FuncMap{
		"withimmutable": withImmutable,
		"ismutable":     isMutable,
		"verbfortype":   verbForType,
		"firstLetter":   firstLetter,
	}

	headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(headerTemplateText))
	structHeaderTemplate = template.Must(template.New("structHeader").Funcs(funcMap).Parse(structHeaderTemplateText))
	typedPropertyTemplate = template.Must(template.New("typedProperty").Funcs(funcMap).Parse(typedPropertyTemplateText))
	readerTemplate = template.Must(template.New("reader").Funcs(funcMap).Parse(readerTemplateText))
	readSetterTemplate = template.Must(template.New("readsetter").Funcs(funcMap).Parse(readSetterTemplateText))
	readSetConfigurerTemplate = template.Must(template.New("readsetconfigurer").Funcs(funcMap).Parse(readSetConfigurerTemplateText))
	enumHeaderTemplate = template.Must(template.New("enumheader").Funcs(funcMap).Parse(enumHeaderTemplateText))
	enumDelegateTemplate = template.Must(template.New("enumdelegate").Funcs(funcMap).Parse(enumDelegateTemplateText))
	enumItemTemplate = template.Must(template.New("enumitem").Parse(enumItemTemplateText))
}

func withImmutable(in string) string {
	prefix := ""
	rest := in
	parts := strings.Split(in, ".")
	if len(parts) > 1 {
		prefix = strings.Join(parts[:len(parts)-1], ".")
		rest = parts[len(parts)-1]
	}

	if _, needsImmutable := configureTypes[rest]; !needsImmutable {
		return in
	}

	rest = "Immutable" + rest

	if prefix == "" {
		return rest
	}

	return prefix + "." + rest

}

func isMutable(in string) bool {

	for key := range configureTypes {
		if strings.Contains(in, key) {
			return true
		}
	}

	return false
}

var configureTypes = map[string]bool{
	"Stack": true,
	"Timer": true,
	"Board": true,
	"Val":   true,
	"Enum":  true,
}

func verbForType(in boardgame.PropertyType) string {
	if in.IsInterface() {
		return "Configure"
	}
	return "Set"
}

func firstLetter(in string) string {

	if in == "" {
		return ""
	}

	return strings.ToLower(in[:1])
}

const headerTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help certain structs
 * implement boardgame.PropertyReader and friends. It was generated
 * by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}
`

const importText = `import (
	"errors"

	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/enum"
)

`

const structHeaderTemplateText = `// Implementation for {{.StructName}}

var {{.ReaderName}}Props = map[string]boardgame.PropertyType{
	{{range $key, $value := .Fields -}}
		"{{$key}}": boardgame.{{$value.Type.String}},
	{{end}}
}

type {{.ReaderName}} struct {
	data *{{.StructName}}
}

func ({{.FirstLetter}} *{{.ReaderName}}) Props() map[string]boardgame.PropertyType {
	return {{.ReaderName}}Props
}

func ({{.FirstLetter}} *{{.ReaderName}}) Prop(name string) (interface{}, error) {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return nil, errors.New("No such property with that name: " + name)
	}

	{{$firstLetter := .FirstLetter}}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		return {{$firstLetter}}.{{withimmutable $type.Key}}Prop(name)
	{{end}}
	}

	return nil, errors.New("Unexpected property type: " + propType.String())
}

{{if .OutputReadSetter -}}

func ({{.FirstLetter}} *{{.ReaderName}}) PropMutable(name string) bool {
	switch name {
		{{range $key, $val := .Fields -}}
	case "{{$key}}":
		return {{$val.Mutable}}
		{{end -}}
	}

	return false
}

func ({{.FirstLetter}} *{{.ReaderName}}) SetProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	{{if $type.IsInterface -}}
	case boardgame.Type{{$type.Key}}:
		return errors.New("SetProp does not allow setting mutable types; use ConfigureProp instead")
	{{- else -}}
	case boardgame.Type{{$type.Key}}:
		val, ok := value.({{$type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
	{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}

{{if .OutputReadSetConfigurer -}}
func ({{.FirstLetter}} *{{.ReaderName}}) ConfigureProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		{{if $type.IsInterface -}}
		if {{$firstLetter}}.PropMutable(name) {
			//Mutable variant
			val, ok := value.({{$type.MutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.MutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
		}
		//Immutable variant
		val, ok := value.({{withimmutable $type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{withimmutable $type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type}}{{withimmutable $type.Key}}Prop(name, val)
		{{- else -}}
			val, ok := value.({{$type.ImmutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
		{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}
`

const typedPropertyTemplateText = `func ({{.FirstLetter}} *{{.ReaderName}}) {{withimmutable .PropType.Key}}Prop(name string) ({{.PropType.ImmutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				return {{$firstLetter}}.data.{{.Name}}, nil
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{if .OutputReadSetConfigurer -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- else -}}
				return boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{withimmutable .PropType.Key}}Prop(name string, value {{withimmutable .PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return boardgame.ErrPropertyImmutable
			{{- else -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{withimmutable .PropType.Key}} prop: " + name)

}

{{end}}
{{end}}

{{if .OutputReadSetter -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) {{.PropType.Key}}Prop(name string) ({{.PropType.MutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{$zeroValue := .PropType.ZeroValue}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return {{$firstLetter}}.data.{{.Name}}, nil
			{{- else -}}
				return {{$zeroValue}}, boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{else}}
func ({{.FirstLetter}} *{{.ReaderName}}) Set{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				{{$firstLetter}}.data.{{.Name}} = value
				return nil
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{end}}
{{end}}
`

const readerTemplateText = `//Reader returns an autp-generated boardgame.PropertyReader for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) Reader() boardgame.PropertyReader {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetterTemplateText = `//ReadSetter returns an autp-generated boardgame.PropertyReadSetter for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetter() boardgame.PropertyReadSetter {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetConfigurerTemplateText = `//ReadSetConfigurer returns an autp-generated boardgame.PropertyReadSetConfigurer for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetConfigurer() boardgame.PropertyReadSetConfigurer {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`
const enumHeaderTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help configure enums.
 * It was generated by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}

import (
	"github.com/jkomoros/boardgame/enum"
)

var enums = enum.NewSet()

`

const enumDelegateTemplateText = `//ConfigureEnums simply returns enums, the auto-generated Enums variable. This
//is output because {{.delegateName}} appears to be a struct that implements
//boardgame.GameDelegate, and does not already have a ConfigureEnums
//explicitly defined.
func ({{firstLetter .delegateName}} *{{.delegateName}}) ConfigureEnums() *enum.Set {
	return enums
}

`

const enumItemTemplateText = `{{if .firstNewKey}}
//Implicitly created constants for {{.prefix}}
const (
	{{.firstNewKey}} = iota - 9223372036854775808
{{range .restNewKeys -}}
	{{.}}
{{- end -}}
)

{{ end -}}
//{{.prefix}}Enum is the enum.Enum for {{.prefix}}
var {{.prefix}}Enum = enums.MustAdd{{if .parents}}Tree{{end}}("{{.prefix}}", map[int]string{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .values -}}
	{{$name}}: "{{$value}}",
	{{end}}
{{if .parents -}} }, map[int]int{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .parents -}}
	{{$name}}: {{$value}},
	{{end}}
{{end -}}
})

`
<|endoftext|>


<commit_before>package upkeep

import (
	pxy "github.com/jeffjen/ambd/ambctl/arg"

	log "github.com/Sirupsen/logrus"
	docker "github.com/fsouza/go-dockerclient"
	dri "github.com/jeffjen/docker-monitor/driver"
	disc "github.com/jeffjen/go-discovery"
	node "github.com/jeffjen/go-discovery/info"

	etcd "github.com/coreos/etcd/client"
	ctx "golang.org/x/net/context"

	"fmt"
	"os"
	"path"
	"time"
)

func init() {
	var level = os.Getenv("LOG_LEVEL")
	switch level {
	case "DEBUG":
		log.SetLevel(log.DebugLevel)
		break
	case "INFO":
		log.SetLevel(log.InfoLevel)
		break
	case "WARNING":
		log.SetLevel(log.WarnLevel)
		break
	case "ERROR":
		log.SetLevel(log.ErrorLevel)
		break
	case "FATAL":
		log.SetLevel(log.FatalLevel)
		break
	case "PANIC":
		log.SetLevel(log.PanicLevel)
		break
	default:
		log.SetLevel(log.InfoLevel)
		break
	}
}

const (
	UpkeepTimeout = 1 * time.Second

	ProbeTimeout = 100 * time.Millisecond

	MaxFailAttemps = 3
)

type Service struct {
	Hb  time.Duration `json: "Heartbeat"`
	TTL time.Duration `json: "TTL"`

	PHb           time.Duration `json: "ProbeHeartbeat"`
	ProbeType     string        `json: "ProbeType"`
	ProbeEndpoint string        `json: "ProbeEndpoint"`

	Id       string           `json: "ContainerID"`
	Srv      string           `json: "Service"`
	Port     string           `json: "Port"`
	Net      []docker.APIPort `json: "Net"`
	Proxy    []pxy.Info       `json: "Proxy"`
	ProxyCfg string           `json: "ProxyCfg"`
}

type srv struct {
	*Service // embedded type

	key []string

	kAPI etcd.KeysAPI
	opts *etcd.SetOptions

	driver dri.Driver
}

func (serv *srv) keep(c ctx.Context) (err error) {
	output := make(chan error, 1)
	go func() {
		for _, k := range serv.key {
			_, krr := serv.kAPI.Set(c, k, node.MetaData, serv.opts)
			if krr != nil {
				serv.opts.PrevExist = etcd.PrevIgnore
				output <- err
				return // break out
			} else {
				serv.opts.PrevExist = etcd.PrevExist
			}
		}
		output <- nil // all good
	}()
	select {
	case <-c.Done():
		err = c.Err()
	case e := <-output:
		err = e
	}
	return
}

func (serv *srv) probe(c ctx.Context) (err error) {
	output := make(chan error, 1)
	go func() {
		prr := serv.driver.Probe(c)
		if err != nil {
			output <- prr
		} else {
			output <- nil
		}
		log.WithFields(log.Fields{"err": err, "srv": serv.Srv}).Debug("probe")
	}()
	select {
	case <-c.Done():
		err = c.Err()
	case e := <-output:
		err = e
	}
	return
}

func Get(iden string) (s *Service) {
	s, _ = rec.Get(iden).(*Service)
	return
}

func Place(service *Service) {
	if r, ok := Record[service.Id]; ok {
		r.Abort()
	}
	rec.Set(service.Id, service)
	Register(service)
}

func Register(service *Service) {
	alloc := AllocHelper(service.ProbeType)

	serv := &srv{
		service,
		make([]string, 0),
		etcd.NewKeysAPI(disc.NewDiscovery()),
		&etcd.SetOptions{TTL: service.TTL},
		nil,
	}

	logger := log.WithFields(log.Fields{
		"ID": serv.Id[:12], "srv": serv.Srv, "heartbeat": serv.Hb, "ttl": serv.TTL,
	})

	// Advertise key on the discovery service
	if serv.Port != "" {
		serv.key = append(serv.key, fmt.Sprintf("%s/%s:%s", serv.Srv, Advertise, serv.Port))
	} else if len(serv.Net) > 0 {
		serv.key = make([]string, 0)
		for _, p := range serv.Net {
			if p.PublicPort != 0 && p.IP == "0.0.0.0" {
				serv.key = append(serv.key, fmt.Sprintf("%s/%s:%d", serv.Srv, Advertise, p.PublicPort))
			}
		}
	}

	var endpoint string
	if serv.ProbeEndpoint == "" {
		endpoint = path.Base(serv.key[0])
	} else {
		endpoint = fmt.Sprintf("%s/%s", path.Base(serv.key[0]), serv.ProbeEndpoint)
	}

	// TODO:  setup driver for probing
	driver, drr := alloc(endpoint)
	if drr != nil {
		logger.WithFields(log.Fields{"err": drr}).Error("-register")
		return
	}
	serv.driver = driver
	logger.Debug("+register")

	wk, abort := ctx.WithCancel(RootContext)
	go func() {
		defer serv.driver.Close()

		// Request to establish proxy port to ambassador
		openProxyConfig(serv.ProxyCfg, serv.Proxy)

		// setup work cycle
		heartbeat, probe := time.NewTicker(serv.Hb), time.NewTicker(serv.PHb)
		defer func() {
			heartbeat.Stop()
			probe.Stop()
		}()

		logger.Info("start")
		func() {
			d, abort := ctx.WithTimeout(wk, UpkeepTimeout)
			if err := serv.keep(d); err != nil {
				logger.WithFields(log.Fields{"err": err}).Error("-up")
			} else {
				logger.Info("+up")
			}
			abort() // release
		}()

		var chk = NewFail(MaxFailAttemps)
		for yay := true; yay; {
			select {
			case <-heartbeat.C:
				d, abort := ctx.WithTimeout(wk, UpkeepTimeout)
				if !chk.Pass() {
					serv.opts.PrevExist = etcd.PrevIgnore
					logger.Error("!up")
				} else {
					if err := serv.keep(d); err != nil {
						logger.WithFields(log.Fields{"err": err}).Error("-up")
					} else {
						logger.Info("+up")
					}
				}
				abort() // release

			case <-probe.C:
				d, abort := ctx.WithTimeout(wk, ProbeTimeout)
				if err := serv.probe(d); err != nil {
					count := chk.Bad()
					logger.WithFields(log.Fields{"err": err, "fail": count}).Warning("-probe")
				} else {
					chk.Good()
					logger.Debug("+probe")
				}
				abort() // release

			case <-wk.Done():
				logger.Warning("down")
				yay = false
			}
		}
	}()

	Record[serv.Id] = &RunningRecord{serv.Srv, abort} // register abort function for this service
}

func Suspend(iden string) {
	if r, ok := Record[iden]; ok {
		r.Abort()
	}
}

func Unregister(iden string) {
	if r, ok := Record[iden]; ok {
		delete(Record, iden)
		r.Abort()
		rec.Del(iden)
		log.WithFields(log.Fields{"ID": iden[:12], "srv": r.Srv}).Warning("die")
	}
}
<commit_msg>Revert "TMP: FIX: extend UpkeepTimeout for discovery update"<commit_after>package upkeep

import (
	pxy "github.com/jeffjen/ambd/ambctl/arg"

	log "github.com/Sirupsen/logrus"
	docker "github.com/fsouza/go-dockerclient"
	dri "github.com/jeffjen/docker-monitor/driver"
	disc "github.com/jeffjen/go-discovery"
	node "github.com/jeffjen/go-discovery/info"

	etcd "github.com/coreos/etcd/client"
	ctx "golang.org/x/net/context"

	"fmt"
	"os"
	"path"
	"time"
)

func init() {
	var level = os.Getenv("LOG_LEVEL")
	switch level {
	case "DEBUG":
		log.SetLevel(log.DebugLevel)
		break
	case "INFO":
		log.SetLevel(log.InfoLevel)
		break
	case "WARNING":
		log.SetLevel(log.WarnLevel)
		break
	case "ERROR":
		log.SetLevel(log.ErrorLevel)
		break
	case "FATAL":
		log.SetLevel(log.FatalLevel)
		break
	case "PANIC":
		log.SetLevel(log.PanicLevel)
		break
	default:
		log.SetLevel(log.InfoLevel)
		break
	}
}

const (
	UpkeepTimeout = 250 * time.Millisecond

	ProbeTimeout = 100 * time.Millisecond

	MaxFailAttemps = 3
)

type Service struct {
	Hb  time.Duration `json: "Heartbeat"`
	TTL time.Duration `json: "TTL"`

	PHb           time.Duration `json: "ProbeHeartbeat"`
	ProbeType     string        `json: "ProbeType"`
	ProbeEndpoint string        `json: "ProbeEndpoint"`

	Id       string           `json: "ContainerID"`
	Srv      string           `json: "Service"`
	Port     string           `json: "Port"`
	Net      []docker.APIPort `json: "Net"`
	Proxy    []pxy.Info       `json: "Proxy"`
	ProxyCfg string           `json: "ProxyCfg"`
}

type srv struct {
	*Service // embedded type

	key []string

	kAPI etcd.KeysAPI
	opts *etcd.SetOptions

	driver dri.Driver
}

func (serv *srv) keep(c ctx.Context) (err error) {
	output := make(chan error, 1)
	go func() {
		for _, k := range serv.key {
			_, krr := serv.kAPI.Set(c, k, node.MetaData, serv.opts)
			if krr != nil {
				serv.opts.PrevExist = etcd.PrevIgnore
				output <- err
				return // break out
			} else {
				serv.opts.PrevExist = etcd.PrevExist
			}
		}
		output <- nil // all good
	}()
	select {
	case <-c.Done():
		err = c.Err()
	case e := <-output:
		err = e
	}
	return
}

func (serv *srv) probe(c ctx.Context) (err error) {
	output := make(chan error, 1)
	go func() {
		prr := serv.driver.Probe(c)
		if err != nil {
			output <- prr
		} else {
			output <- nil
		}
		log.WithFields(log.Fields{"err": err, "srv": serv.Srv}).Debug("probe")
	}()
	select {
	case <-c.Done():
		err = c.Err()
	case e := <-output:
		err = e
	}
	return
}

func Get(iden string) (s *Service) {
	s, _ = rec.Get(iden).(*Service)
	return
}

func Place(service *Service) {
	if r, ok := Record[service.Id]; ok {
		r.Abort()
	}
	rec.Set(service.Id, service)
	Register(service)
}

func Register(service *Service) {
	alloc := AllocHelper(service.ProbeType)

	serv := &srv{
		service,
		make([]string, 0),
		etcd.NewKeysAPI(disc.NewDiscovery()),
		&etcd.SetOptions{TTL: service.TTL},
		nil,
	}

	logger := log.WithFields(log.Fields{
		"ID": serv.Id[:12], "srv": serv.Srv, "heartbeat": serv.Hb, "ttl": serv.TTL,
	})

	// Advertise key on the discovery service
	if serv.Port != "" {
		serv.key = append(serv.key, fmt.Sprintf("%s/%s:%s", serv.Srv, Advertise, serv.Port))
	} else if len(serv.Net) > 0 {
		serv.key = make([]string, 0)
		for _, p := range serv.Net {
			if p.PublicPort != 0 && p.IP == "0.0.0.0" {
				serv.key = append(serv.key, fmt.Sprintf("%s/%s:%d", serv.Srv, Advertise, p.PublicPort))
			}
		}
	}

	var endpoint string
	if serv.ProbeEndpoint == "" {
		endpoint = path.Base(serv.key[0])
	} else {
		endpoint = fmt.Sprintf("%s/%s", path.Base(serv.key[0]), serv.ProbeEndpoint)
	}

	// TODO:  setup driver for probing
	driver, drr := alloc(endpoint)
	if drr != nil {
		logger.WithFields(log.Fields{"err": drr}).Error("-register")
		return
	}
	serv.driver = driver
	logger.Debug("+register")

	wk, abort := ctx.WithCancel(RootContext)
	go func() {
		defer serv.driver.Close()

		// Request to establish proxy port to ambassador
		openProxyConfig(serv.ProxyCfg, serv.Proxy)

		// setup work cycle
		heartbeat, probe := time.NewTicker(serv.Hb), time.NewTicker(serv.PHb)
		defer func() {
			heartbeat.Stop()
			probe.Stop()
		}()

		logger.Info("start")
		func() {
			d, abort := ctx.WithTimeout(wk, UpkeepTimeout)
			if err := serv.keep(d); err != nil {
				logger.WithFields(log.Fields{"err": err}).Error("-up")
			} else {
				logger.Info("+up")
			}
			abort() // release
		}()

		var chk = NewFail(MaxFailAttemps)
		for yay := true; yay; {
			select {
			case <-heartbeat.C:
				d, abort := ctx.WithTimeout(wk, UpkeepTimeout)
				if !chk.Pass() {
					serv.opts.PrevExist = etcd.PrevIgnore
					logger.Error("!up")
				} else {
					if err := serv.keep(d); err != nil {
						logger.WithFields(log.Fields{"err": err}).Error("-up")
					} else {
						logger.Info("+up")
					}
				}
				abort() // release

			case <-probe.C:
				d, abort := ctx.WithTimeout(wk, ProbeTimeout)
				if err := serv.probe(d); err != nil {
					count := chk.Bad()
					logger.WithFields(log.Fields{"err": err, "fail": count}).Warning("-probe")
				} else {
					chk.Good()
					logger.Debug("+probe")
				}
				abort() // release

			case <-wk.Done():
				logger.Warning("down")
				yay = false
			}
		}
	}()

	Record[serv.Id] = &RunningRecord{serv.Srv, abort} // register abort function for this service
}

func Suspend(iden string) {
	if r, ok := Record[iden]; ok {
		r.Abort()
	}
}

func Unregister(iden string) {
	if r, ok := Record[iden]; ok {
		delete(Record, iden)
		r.Abort()
		rec.Del(iden)
		log.WithFields(log.Fields{"ID": iden[:12], "srv": r.Srv}).Warning("die")
	}
}
<|endoftext|>


<commit_before>package rpcwallet

import (
	"fmt"
	"time"

	"github.com/lightningnetwork/lnd/lncfg"
)

// HealthCheck returns a health check function for the given remote signing
// configuration.
func HealthCheck(cfg *lncfg.RemoteSigner, timeout time.Duration) func() error {
	return func() error {
		_, err := connectRPC(
			cfg.RPCHost, cfg.TLSCertPath, cfg.MacaroonPath, timeout,
		)
		if err != nil {
			return fmt.Errorf("error connecting to the remote "+
				"signing node through RPC: %v", err)
		}

		return nil
	}
}
<commit_msg>rpcwallet: fix RPC wallet health check connection leak<commit_after>package rpcwallet

import (
	"fmt"
	"time"

	"github.com/lightningnetwork/lnd/lncfg"
)

// HealthCheck returns a health check function for the given remote signing
// configuration.
func HealthCheck(cfg *lncfg.RemoteSigner, timeout time.Duration) func() error {
	return func() error {
		conn, err := connectRPC(
			cfg.RPCHost, cfg.TLSCertPath, cfg.MacaroonPath, timeout,
		)
		if err != nil {
			return fmt.Errorf("error connecting to the remote "+
				"signing node through RPC: %v", err)
		}

		defer func() {
			err = conn.Close()
			if err != nil {
				log.Warnf("Failed to close health check "+
					"connection to remote signing node: %v",
					err)
			}
		}()

		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bytes

import "os"

// Efficient construction of large strings and byte arrays.
// Implements io.Reader and io.Writer.

// A Buffer provides efficient construction of large strings
// and slices of bytes.  It implements io.Reader and io.Writer.
// Appends (writes) are efficient.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	blk	[]block;
	len	int;
	oneByte	[1]byte;
}

// There are two kinds of block: a string or a []byte.
// When the user writes big strings, we add string blocks;
// when the user writes big byte slices, we add []byte blocks.
// Small writes are coalesced onto the end of the last block,
// whatever it is.
// This strategy is intended to reduce unnecessary allocation.
type block interface {
	Len()	int;
	String()	string;
	appendBytes(s []byte);
	appendString(s string);
	setSlice(m, n int);
}

// stringBlocks represent strings. We use pointer receivers
// so append and setSlice can overwrite the receiver.
type stringBlock string

func (b *stringBlock) Len() int {
	return len(*b)
}

func (b *stringBlock) String() string {
	return string(*b)
}

func (b *stringBlock) appendBytes(s []byte) {
	*b += stringBlock(s)
}

func (b *stringBlock) appendString(s string) {
	*b = stringBlock(s)
}

func (b *stringBlock) setSlice(m, n int) {
	*b = (*b)[m:n]
}

// byteBlock represent slices of bytes.  We use pointer receivers
// so append and setSlice can overwrite the receiver.
type byteBlock []byte

func (b *byteBlock) Len() int {
	return len(*b)
}

func (b *byteBlock) String() string {
	return string(*b)
}

func (b *byteBlock) resize(max int) {
	by := []byte(*b);
	if cap(by) >= max {
		by = by[0:max];
	} else {
		nby := make([]byte, max, 3*(max+10)/2);
		copyBytes(nby, 0, by);
		by = nby;
	}
	*b = by;
}

func (b *byteBlock) appendBytes(s []byte) {
	curLen := b.Len();
	b.resize(curLen + len(s));
	copyBytes([]byte(*b), curLen, s);
}

func (b *byteBlock) appendString(s string) {
	curLen := b.Len();
	b.resize(curLen + len(s));
	copyString([]byte(*b), curLen, s);
}

func (b *byteBlock) setSlice(m, n int) {
	*b = (*b)[m:n]
}

// Because the user may overwrite the contents of byte slices, we need
// to make a copy.  Allocation strategy: leave some space on the end so
// small subsequent writes can avoid another allocation.  The input
// is known to be non-empty.
func newByteBlock(s []byte) *byteBlock {
	l := len(s);
	// Capacity with room to grow.  If small, allocate a mininum.  If medium,
	// double the size.  If huge, use the size plus epsilon (room for a newline,
	// at least).
	c := l;
	switch {
	case l < 32:
		c = 64
	case l < 1<<18:
		c *= 2;
	default:
		c += 8
	}
	b := make([]byte, l, c);
	copyBytes(b, 0, s);
	return &b;
}

// Copy from block to byte array at offset doff.  Assume there's room.
func copy(dst []byte, doff int, src block) {
	switch s := src.(type) {
	case *stringBlock:
		copyString(dst, doff, string(*s));
	case *byteBlock:
		copyBytes(dst, doff, []byte(*s));
	}
}

// Copy from string to byte array at offset doff.  Assume there's room.
func copyString(dst []byte, doff int, str string) {
	for soff := 0; soff < len(str); soff++ {
		dst[doff] = str[soff];
		doff++;
	}
}

// Copy from bytes to byte array at offset doff.  Assume there's room.
func copyBytes(dst []byte, doff int, src []byte) {
	for soff := 0; soff < len(src); soff++ {
		dst[doff] = src[soff];
		doff++;
	}
}

// Bytes returns the contents of the unread portion of the buffer
// as a byte array.
func (b *Buffer) Bytes() []byte {
	n := b.len;
	bytes := make([]byte, n);
	nbytes := 0;
	for _, s := range b.blk {
		copy(bytes, nbytes, s);
		nbytes += s.Len();
	}
	return bytes;
}

// String returns the contents of the unread portion of the buffer
// as a string.
func (b *Buffer) String() string {
	if len(b.blk) == 1 {	// important special case
		return b.blk[0].String()
	}
	return string(b.Bytes())
}

// Len returns the number of bytes in the unread portion of the buffer;
// b.Len() == len(b.Bytes()) == len(b.String()).
func (b *Buffer) Len() int {
	return b.len
}

// Truncate discards all but the first n unread bytes from the buffer.
func (b *Buffer) Truncate(n int) {
	b.len = 0;	// recompute during scan.
	for i, s := range b.blk {
		if n <= 0 {
			b.blk = b.blk[0:i];
			break;
		}
		if l := s.Len(); n < l {
			b.blk[i].setSlice(0, n);
			b.len += n;
			n = 0;
		} else {
			b.len += l;
			n -= l;
		}
	}
}

// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset() {
	b.blk = b.blk[0:0];
	b.len = 0;
}

// Can n bytes be appended efficiently to the end of the final string?
func (b *Buffer) canCombine(n int) bool {
	return len(b.blk) > 0 && n+b.blk[len(b.blk)-1].Len() <= 64
}

// WriteString appends string s to the buffer.  The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err os.Error) {
	n = len(s);
	if n == 0 {
		return
	}
	b.len += n;
	numStr := len(b.blk);
	// Special case: If the last piece is short and this one is short,
	// combine them and avoid growing the list.
	if b.canCombine(n) {
		b.blk[numStr-1].appendString(s);
		return
	}
	if cap(b.blk) == numStr {
		nstr := make([]block, numStr, 3*(numStr+10)/2);
		for i, s := range b.blk {
			nstr[i] = s;
		}
		b.blk = nstr;
	}
	b.blk = b.blk[0:numStr+1];
	// The string is immutable; no need to make a copy.
	b.blk[numStr] = (*stringBlock)(&s);
	return
}

// Write appends the contents of p to the buffer.  The return
// value n is the length of p; err is always nil.
func (b *Buffer) Write(p []byte) (n int, err os.Error) {
	n = len(p);
	if n == 0 {
		return
	}
	b.len += n;
	numStr := len(b.blk);
	// Special case: If the last piece is short and this one is short,
	// combine them and avoid growing the list.
	if b.canCombine(n) {
		b.blk[numStr-1].appendBytes(p);
		return
	}
	if cap(b.blk) == numStr {
		nstr := make([]block, numStr, 3*(numStr+10)/2);
		for i, s := range b.blk {
			nstr[i] = s;
		}
		b.blk = nstr;
	}
	b.blk = b.blk[0:numStr+1];
	// Need to copy the data - user might overwrite the data.
	b.blk[numStr] = newByteBlock(p);
	return
}

// WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included
// to match bufio.Writer's WriteByte.
func (b *Buffer) WriteByte(c byte) os.Error {
	b.oneByte[0] = c;
	// For WriteByte, canCombine is almost always true so it's worth
	// doing here.
	if b.canCombine(1) {
		b.blk[len(b.blk)-1].appendBytes(&b.oneByte);
		b.len++;
		return nil
	}
	b.Write(&b.oneByte);
	return nil;
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained.  The return value n is the number of bytes read.  If the
// buffer has no data to return, err is os.EOF even if len(p) is zero;
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err os.Error) {
	if len(b.blk) == 0 {
		return 0, os.EOF
	}
	for len(b.blk) > 0 {
		blk := b.blk[0];
		m := len(p) - n;
		if l := blk.Len(); m >= l {
			// consume all of this string.
			copy(p, n, blk);
			n += l;
			b.blk = b.blk[1:len(b.blk)];
		} else {
			// consume some of this block; it's the last piece.
			switch b := blk.(type) {
			case *stringBlock:
				copyString(p, n, string(*b)[0:m]);
			case *byteBlock:
				copyBytes(p, n, []byte(*b)[0:m]);
			}
			n += m;
			b.blk[0].setSlice(m, l);
			break;
		}
	}
	b.len -= n;
	return
}

// ReadByte reads and returns the next byte from the buffer.
// If no byte is available, it returns error os.EOF.
func (b *Buffer) ReadByte() (c byte, err os.Error) {
	if _, err := b.Read(&b.oneByte); err != nil {
		return 0, err
	}
	return b.oneByte[0], nil
}

// NewBufferString creates and initializes a new Buffer
// using a string as its initial contents.
func NewBufferString(str string) *Buffer {
	b := new(Buffer);
	if len(str) > 0 {
		b.blk = make([]block, 1, 10);	// room to grow
		b.blk[0] = (*stringBlock)(&str);
	}
	b.len = len(str);
	return b;
}

// NewBuffer creates and initializes a new Buffer
// using a byte slice as its initial contents.
func NewBuffer(by []byte) *Buffer {
	b := new(Buffer);
	if len(by) > 0 {
		b.blk = make([]block, 1, 10);	// room to grow
		b.blk[0] = (*byteBlock)(&by);
	}
	b.len = len(by);
	return b;
}
<commit_msg>restore the old algorithm. the new one is more memory efficient in large cases but too slow across the board.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bytes

// Simple byte buffer for marshaling data.

import (
	"os";
)

// Copy from string to byte array at offset doff.  Assume there's room.
func copyString(dst []byte, doff int, str string) {
	for soff := 0; soff < len(str); soff++ {
		dst[doff] = str[soff];
		doff++;
	}
}

// Copy from bytes to byte array at offset doff.  Assume there's room.
func copyBytes(dst []byte, doff int, src []byte) {
	for soff := 0; soff < len(src); soff++ {
		dst[doff] = src[soff];
		doff++;
	}
}

// A Buffer is a variable-sized buffer of bytes
// with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf	[]byte;	// contents are the bytes buf[off : len(buf)]
	off	int;	// read at &buf[off], write at &buf[len(buf)]
	oneByte	[]byte;	// avoid allocation of slice on each WriteByte
}

// Bytes returns the contents of the unread portion of the buffer;
// len(b.Bytes()) == b.Len().
func (b *Buffer) Bytes() []byte {
	return b.buf[b.off : len(b.buf)]
}

// String returns the contents of the unread portion of the buffer
// as a string.
func (b *Buffer) String() string {
	return string(b.buf[b.off : len(b.buf)])
}

// Len returns the number of bytes of the unread portion of the buffer;
// b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int {
	return len(b.buf) - b.off
}

// Truncate discards all but the first n unread bytes from the buffer.
// It is an error to call b.Truncate(n) with n > b.Len().
func (b *Buffer) Truncate(n int) {
	if n == 0 {
		// Reuse buffer space.
		b.off = 0;
	}
	b.buf = b.buf[0 : b.off + n];
}

// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset() {
	b.Truncate(0);
}

// Write appends the contents of p to the buffer.  The return
// value n is the length of p; err is always nil.
func (b *Buffer) Write(p []byte) (n int, err os.Error) {
	m := b.Len();
	n = len(p);

	if len(b.buf) + n > cap(b.buf) {
		// not enough space at end
		buf := b.buf;
		if m + n > cap(b.buf) {
			// not enough space anywhere
			buf = make([]byte, 2*cap(b.buf) + n)
		}
		copyBytes(buf, 0, b.buf[b.off:b.off+m]);
		b.buf = buf;
		b.off = 0
	}

	b.buf = b.buf[0 : b.off + m + n];
	copyBytes(b.buf, b.off + m, p);
	return n, nil
}

// WriteString appends the contents of s to the buffer.  The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err os.Error) {
	m := b.Len();
	n = len(s);

	if len(b.buf) + n > cap(b.buf) {
		// not enough space at end
		buf := b.buf;
		if m + n > cap(b.buf) {
			// not enough space anywhere
			buf = make([]byte, 2*cap(b.buf) + n)
		}
		copyBytes(buf, 0, b.buf[b.off:b.off+m]);
		b.buf = buf;
		b.off = 0
	}

	b.buf = b.buf[0 : b.off + m + n];
	copyString(b.buf, b.off+m, s);
	return n, nil
}

// WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included
// to match bufio.Writer's WriteByte.
func (b *Buffer) WriteByte(c byte) os.Error {
	if b.oneByte == nil {
		// Only happens once per Buffer, and then we have a slice.
		b.oneByte = make([]byte, 1);
	}
	b.oneByte[0] = c;
	b.Write(b.oneByte);
	return nil;
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained.  The return value n is the number of bytes read.  If the
// buffer has no data to return, err is os.EOF even if len(p) is zero;
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err os.Error) {
	if b.off >= len(b.buf) {
		return 0, os.EOF
	}
	m := b.Len();
	n = len(p);

	if n > m {
		// more bytes requested than available
		n = m
	}

	copyBytes(p, 0, b.buf[b.off:b.off+n]);
	b.off += n;
	return n, err
}

// ReadByte reads and returns the next byte from the buffer.
// If no byte is available, it returns error os.EOF.
func (b *Buffer) ReadByte() (c byte, err os.Error) {
	if b.off >= len(b.buf) {
		return 0, os.EOF;
	}
	c = b.buf[b.off];
	b.off++;
	return c, nil;
}

// NewBuffer creates and initializes a new Buffer
// using buf as its initial contents.
func NewBuffer(buf []byte) *Buffer {
	return &Buffer{buf: buf};
}

// NewBufferString creates and initializes a new Buffer
// using string s as its initial contents.
func NewBufferString(s string) *Buffer {
	buf := make([]byte, len(s));
	copyString(buf, 0, s);
	return &Buffer{buf: buf};
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree.  The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code.  The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path.  That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
//	GOPATH=/home/user/gocode
//
//	/home/user/gocode/
//	    src/
//	        foo/
//	            bar/               (go code in package bar)
//	                x.go
//	            quux/              (go code in package main)
//	                y.go
//	    bin/
//	        quux                   (installed command)
//	    pkg/
//	        linux_amd64/
//	            foo/
//	                bar.a          (installed package object)
//
// Build Constraints
//
// A build constraint, also known as a build tag, is a line comment that begins
//
//	// +build
//
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments. These rules mean that in Go
// files a build constraint must appear before the package clause.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
//	// +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
//	(linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
//	// +build linux darwin
//	// +build 386
//
// corresponds to the boolean formula:
//
//	(linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
//	- the target operating system, as spelled by runtime.GOOS
//	- the target architecture, as spelled by runtime.GOARCH
//	- the compiler being used, either "gc" or "gccgo"
//	- "cgo", if ctxt.CgoEnabled is true
//	- "go1.1", from Go version 1.1 onward
//	- "go1.2", from Go version 1.2 onward
//	- any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches any of the following patterns:
//	*_GOOS
// 	*_GOARCH
// 	*_GOOS_GOARCH
// (example: source_windows_amd64.go) or the literals:
//	GOOS
// 	GOARCH
// (example: windows.go) where GOOS and GOARCH represent any known operating
// system and architecture values respectively, then the file is considered to
// have an implicit build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
//	// +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
//	// +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
//	// +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
<commit_msg>go/build: update doc.go for go1.3 build tag.<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree.  The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code.  The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path.  That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
//	GOPATH=/home/user/gocode
//
//	/home/user/gocode/
//	    src/
//	        foo/
//	            bar/               (go code in package bar)
//	                x.go
//	            quux/              (go code in package main)
//	                y.go
//	    bin/
//	        quux                   (installed command)
//	    pkg/
//	        linux_amd64/
//	            foo/
//	                bar.a          (installed package object)
//
// Build Constraints
//
// A build constraint, also known as a build tag, is a line comment that begins
//
//	// +build
//
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments. These rules mean that in Go
// files a build constraint must appear before the package clause.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
//	// +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
//	(linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
//	// +build linux darwin
//	// +build 386
//
// corresponds to the boolean formula:
//
//	(linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
//	- the target operating system, as spelled by runtime.GOOS
//	- the target architecture, as spelled by runtime.GOARCH
//	- the compiler being used, either "gc" or "gccgo"
//	- "cgo", if ctxt.CgoEnabled is true
//	- "go1.1", from Go version 1.1 onward
//	- "go1.2", from Go version 1.2 onward
//	- "go1.3", from Go version 1.3 onward
//	- any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches any of the following patterns:
//	*_GOOS
// 	*_GOARCH
// 	*_GOOS_GOARCH
// (example: source_windows_amd64.go) or the literals:
//	GOOS
// 	GOARCH
// (example: windows.go) where GOOS and GOARCH represent any known operating
// system and architecture values respectively, then the file is considered to
// have an implicit build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
//	// +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
//	// +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
//	// +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sync

// An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers
// or a single writer.
// RWMutexes can be created as part of other
// structures; the zero value for a RWMutex is
// an unlocked mutex.
//
// Writers take priority over Readers: no new RLocks
// are granted while a blocked Lock call is waiting.
type RWMutex struct {
	w           Mutex  // held if there are pending readers or writers
	r           Mutex  // held if the w is being rd
	readerCount uint32 // number of pending readers
}

// RLock locks rw for reading.
// If the lock is already locked for writing or there is a writer already waiting
// to r the lock, RLock blocks until the writer has released the lock.
func (rw *RWMutex) RLock() {
	// Use rw.r.Lock() to block granting the RLock if a goroutine
	// is waiting for its Lock. This is the prevent starvation of W in
	// this situation:
	//   A: rw.RLock()  // granted
	//   W: rw.Lock()   // waiting for rw.w().Lock()
	//   B: rw.RLock()  // granted
	//   C: rw.RLock()  // granted
	//   B: rw.RUnlock()
	//   ... (new readers come and go indefinitely, W is starving)
	rw.r.Lock()
	if xadd(&rw.readerCount, 1) == 1 {
		// The first reader locks rw.w, so writers will be blocked
		// while the readers have the RLock.
		rw.w.Lock()
	}
	rw.r.Unlock()
}

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
	if xadd(&rw.readerCount, -1) == 0 {
		// last reader finished, enable writers
		rw.w.Unlock()
	}
}

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
// To ensure that the lock eventually becomes available,
// a blocked Lock call excludes new readers from acquiring
// the lock.
func (rw *RWMutex) Lock() {
	rw.r.Lock()
	rw.w.Lock()
	rw.r.Unlock()
}

// Unlock unlocks rw for writing.
// It is a run-time error if rw is not locked for writing
// on entry to Unlock.
//
// Like for Mutexes,
// a locked RWMutex is not associated with a particular goroutine.
// It is allowed for one goroutine to RLock (Lock) an RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() { rw.w.Unlock() }
<commit_msg>sync: fix typo in documentation<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sync

// An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers
// or a single writer.
// RWMutexes can be created as part of other
// structures; the zero value for a RWMutex is
// an unlocked mutex.
//
// Writers take priority over Readers: no new RLocks
// are granted while a blocked Lock call is waiting.
type RWMutex struct {
	w           Mutex  // held if there are pending readers or writers
	r           Mutex  // held if the w is being rd
	readerCount uint32 // number of pending readers
}

// RLock locks rw for reading.
// If the lock is already locked for writing or there is a writer already waiting
// to release the lock, RLock blocks until the writer has released the lock.
func (rw *RWMutex) RLock() {
	// Use rw.r.Lock() to block granting the RLock if a goroutine
	// is waiting for its Lock. This is the prevent starvation of W in
	// this situation:
	//   A: rw.RLock()  // granted
	//   W: rw.Lock()   // waiting for rw.w().Lock()
	//   B: rw.RLock()  // granted
	//   C: rw.RLock()  // granted
	//   B: rw.RUnlock()
	//   ... (new readers come and go indefinitely, W is starving)
	rw.r.Lock()
	if xadd(&rw.readerCount, 1) == 1 {
		// The first reader locks rw.w, so writers will be blocked
		// while the readers have the RLock.
		rw.w.Lock()
	}
	rw.r.Unlock()
}

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
	if xadd(&rw.readerCount, -1) == 0 {
		// last reader finished, enable writers
		rw.w.Unlock()
	}
}

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
// To ensure that the lock eventually becomes available,
// a blocked Lock call excludes new readers from acquiring
// the lock.
func (rw *RWMutex) Lock() {
	rw.r.Lock()
	rw.w.Lock()
	rw.r.Unlock()
}

// Unlock unlocks rw for writing.
// It is a run-time error if rw is not locked for writing
// on entry to Unlock.
//
// Like for Mutexes,
// a locked RWMutex is not associated with a particular goroutine.
// It is allowed for one goroutine to RLock (Lock) an RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() { rw.w.Unlock() }
<|endoftext|>


<commit_before>// Copyright 2014 Erlend Johannessen.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package count

import (
	"fmt"
	"sort"
	"strings"

	"utils"
)

const (
	thousandsSeparator = ' '
	// formatString consists of "filetype", "#files", "#lines", "line%", "size", "size%"
	formatString       = "%-11s %10s %12s %6s %13s %6s\n"
	formatStringLength = 11 + 1 + 10 + 1 + 12 + 1 + 6 + 1 + 13 + 1 + 6
)

// Entry
type Entry struct {
	ExtensionName string
	IsBinary      bool
	NumberOfFiles int
	NumberOfLines int
	Filesize      int64
}

// Result
type Result struct {
	Directory          string
	Extensions         map[string]*Entry
	TotalNumberOfFiles int
	TotalNumberOfLines int
	TotalSize          int64
}

// ------------------------------------------
// PrintAnalyticsHeader
// ------------------------------------------
func PrintAnalyticsHeader(showDirectories, showFiles, showOnlyIncluded, showOnlyExcluded bool) {
	if showDirectories || showFiles {
		fmt.Println()
	}

	if showDirectories {
		if showOnlyIncluded {
			fmt.Printf("Shows included directories.\n")
		}
		if showOnlyExcluded {
			fmt.Printf("Shows excluded directories.\n")
		}
	}

	if showFiles {
		if showOnlyIncluded {
			fmt.Printf("Shows included files.\n")
		}
		if showOnlyExcluded {
			fmt.Printf("Shows excluded files.\n")
		}
	}

	if showDirectories || showFiles {
		fmt.Printf("---------------------------\n")
	}
}

// ------------------------------------------
// PrintResult
// ------------------------------------------
func PrintResult(root string, result Result, bigFiles FileSizes) {
	// Show result header
	printHeader(root)

	// Sorting keys for presentation
	var keys, binaryKeys = getKeys(result.Extensions)

	// Show result for sourcecode, only for extensions found
	for _, ext := range keys {
		printEntry(result.Extensions[ext], result.TotalNumberOfLines, result.TotalSize)
	}

	// For convenience, show result for binaries separately
	for _, ext := range binaryKeys {
		printEntry(result.Extensions[ext], 0, result.TotalSize)
	}

	// Show footer
	printFooter(result)

	if showBigFiles > 0 {
		sort.Sort(bigFiles)
		fmt.Printf("\n\nThe %d biggest files are:\n", showBigFiles)
		fmt.Printf("-------------------------------------------------\n")
		for i := 0; i < len(bigFiles) && i < 50; i++ {
			fmt.Printf("%-42s %6d\n", bigFiles[i].Name, bigFiles[i].Lines)
		}
	}
}

// ------------------------------------------
// getKeys
// ------------------------------------------
func getKeys(extensions map[string]*Entry) ([]string, []string) {
	var keys []string
	var binaryKeys []string

	for k, v := range extensions {
		if v.NumberOfFiles > 0 {
			if v.IsBinary {
				binaryKeys = append(binaryKeys, k)
			} else {
				keys = append(keys, k)
			}
		}
	}

	sort.Strings(keys)
	sort.Strings(binaryKeys)

	return keys, binaryKeys
}

// ------------------------------------------
// printHeader
// ------------------------------------------
func printHeader(root string) {
	fmt.Printf("\nDirectory processed:\n")
	fmt.Printf("%v\n", root)
	fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
	fmt.Printf(formatString, "filetype", "#files", "#lines", "line%", "size", "size%")
	fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
}

// ------------------------------------------
// printEntry
// ------------------------------------------
func printEntry(entry *Entry, totalNumberOfLines int, totalSize int64) {
	var numberOfLinesString = ""
	var percentageString = ""

	if !entry.IsBinary {
		numberOfLinesString = utils.IntToString(entry.NumberOfLines, thousandsSeparator)

		// Show percentage
		if totalNumberOfLines > 0 {
			var percentage = float64(entry.NumberOfLines) * float64(100) / float64(totalNumberOfLines)
			percentageString = fmt.Sprintf("%.1f", utils.Round(percentage, 1))
		}
	}

	var sizePercentage = float64(entry.Filesize) * float64(100) / float64(totalSize)
	var sizePercentageString = fmt.Sprintf("%.1f", utils.Round(sizePercentage, 1))

	fmt.Printf(
		formatString,
		entry.ExtensionName,
		utils.IntToString(entry.NumberOfFiles, thousandsSeparator),
		numberOfLinesString,
		percentageString,
		utils.Int64ToString(entry.Filesize, thousandsSeparator),
		sizePercentageString,
	)
}

// ------------------------------------------
// printFooter
// ------------------------------------------
func printFooter(result Result) {
	// Show footer
	if result.TotalNumberOfFiles == 0 {
		fmt.Printf("No files found.\n\nCheck given directory,or maybe \ncheck extensions in config file.\n")
		fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
	} else {
		fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
		fmt.Printf(
			formatString,
			"Total:",
			utils.IntToString(result.TotalNumberOfFiles, thousandsSeparator),
			utils.IntToString(result.TotalNumberOfLines, thousandsSeparator),
			"100.0",
			utils.Int64ToString(result.TotalSize, thousandsSeparator),
			"100.0",
		)
	}
}
<commit_msg>Fixing criteria error, and a typo<commit_after>// Copyright 2014 Erlend Johannessen.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package count

import (
	"fmt"
	"sort"
	"strings"

	"utils"
)

const (
	thousandsSeparator = ' '
	// formatString consists of "filetype", "#files", "#lines", "line%", "size", "size%"
	formatString       = "%-11s %10s %12s %6s %13s %6s\n"
	formatStringLength = 11 + 1 + 10 + 1 + 12 + 1 + 6 + 1 + 13 + 1 + 6
)

// Entry
type Entry struct {
	ExtensionName string
	IsBinary      bool
	NumberOfFiles int
	NumberOfLines int
	Filesize      int64
}

// Result
type Result struct {
	Directory          string
	Extensions         map[string]*Entry
	TotalNumberOfFiles int
	TotalNumberOfLines int
	TotalSize          int64
}

// ------------------------------------------
// PrintAnalyticsHeader
// ------------------------------------------
func PrintAnalyticsHeader(showDirectories, showFiles, showOnlyIncluded, showOnlyExcluded bool) {
	if showDirectories || showFiles {
		fmt.Println()
	}

	if showDirectories {
		if showOnlyIncluded {
			fmt.Printf("Shows included directories.\n")
		}
		if showOnlyExcluded {
			fmt.Printf("Shows excluded directories.\n")
		}
	}

	if showFiles {
		if showOnlyIncluded {
			fmt.Printf("Shows included files.\n")
		}
		if showOnlyExcluded {
			fmt.Printf("Shows excluded files.\n")
		}
	}

	if showDirectories || showFiles {
		fmt.Printf("---------------------------\n")
	}
}

// ------------------------------------------
// PrintResult
// ------------------------------------------
func PrintResult(root string, result Result, bigFiles FileSizes) {
	// Show result header
	printHeader(root)

	// Sorting keys for presentation
	var keys, binaryKeys = getKeys(result.Extensions)

	// Show result for sourcecode, only for extensions found
	for _, ext := range keys {
		printEntry(result.Extensions[ext], result.TotalNumberOfLines, result.TotalSize)
	}

	// For convenience, show result for binaries separately
	for _, ext := range binaryKeys {
		printEntry(result.Extensions[ext], 0, result.TotalSize)
	}

	// Show footer
	printFooter(result)

	if showBigFiles > 0 {
		sort.Sort(bigFiles)
		fmt.Printf("\n\nThe %3d largest files are:                 #lines\n", showBigFiles)
		fmt.Printf("-------------------------------------------------\n")
		for i := 0; i < showBigFiles; i++ {
			fmt.Printf("%-42s %6d\n", bigFiles[i].Name, bigFiles[i].Lines)
		}
	}
}

// ------------------------------------------
// getKeys
// ------------------------------------------
func getKeys(extensions map[string]*Entry) ([]string, []string) {
	var keys []string
	var binaryKeys []string

	for k, v := range extensions {
		if v.NumberOfFiles > 0 {
			if v.IsBinary {
				binaryKeys = append(binaryKeys, k)
			} else {
				keys = append(keys, k)
			}
		}
	}

	sort.Strings(keys)
	sort.Strings(binaryKeys)

	return keys, binaryKeys
}

// ------------------------------------------
// printHeader
// ------------------------------------------
func printHeader(root string) {
	fmt.Printf("\nDirectory processed:\n")
	fmt.Printf("%v\n", root)
	fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
	fmt.Printf(formatString, "filetype", "#files", "#lines", "line%", "size", "size%")
	fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
}

// ------------------------------------------
// printEntry
// ------------------------------------------
func printEntry(entry *Entry, totalNumberOfLines int, totalSize int64) {
	var numberOfLinesString = ""
	var percentageString = ""

	if !entry.IsBinary {
		numberOfLinesString = utils.IntToString(entry.NumberOfLines, thousandsSeparator)

		// Show percentage
		if totalNumberOfLines > 0 {
			var percentage = float64(entry.NumberOfLines) * float64(100) / float64(totalNumberOfLines)
			percentageString = fmt.Sprintf("%.1f", utils.Round(percentage, 1))
		}
	}

	var sizePercentage = float64(entry.Filesize) * float64(100) / float64(totalSize)
	var sizePercentageString = fmt.Sprintf("%.1f", utils.Round(sizePercentage, 1))

	fmt.Printf(
		formatString,
		entry.ExtensionName,
		utils.IntToString(entry.NumberOfFiles, thousandsSeparator),
		numberOfLinesString,
		percentageString,
		utils.Int64ToString(entry.Filesize, thousandsSeparator),
		sizePercentageString,
	)
}

// ------------------------------------------
// printFooter
// ------------------------------------------
func printFooter(result Result) {
	// Show footer
	if result.TotalNumberOfFiles == 0 {
		fmt.Printf("No files found.\n\nCheck given directory,or maybe \ncheck extensions in config file.\n")
		fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
	} else {
		fmt.Printf("%s\n", strings.Repeat("-", formatStringLength))
		fmt.Printf(
			formatString,
			"Total:",
			utils.IntToString(result.TotalNumberOfFiles, thousandsSeparator),
			utils.IntToString(result.TotalNumberOfLines, thousandsSeparator),
			"100.0",
			utils.Int64ToString(result.TotalSize, thousandsSeparator),
			"100.0",
		)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * 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.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 */

package oauth2_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/julienschmidt/httprouter"
	"github.com/ory/fosite"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/oauth2"
)

func newCookieJar() http.CookieJar {
	c, _ := cookiejar.New(nil)
	return c
}

func noopHandler(t *testing.T) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		w.WriteHeader(http.StatusNotImplemented)
	}
}

func TestAuthCodeSuite(t *testing.T) {
	// Regular request without any previous authentication
	// Regular request with previous authentication
	// Regular request with previous authentication and prompt login/consent
	// Regular request with previous authentication and prompt none
	// Regular request with previous authentication and prompt none and proper id_token_hint
	// Regular request without authentication and prompt none (fail)
	// Regular request with previous authentication and prompt none and very low max_age (fail)
	// Regular request with previous authentication and prompt none and mismatching id_token_hint (fail)
	// Regular request fails if login is denied
	// Regular request fails if consent is denied

	var callbackHandler *httprouter.Handle
	router.GET("/callback", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		(*callbackHandler)(w, r, ps)
	})
	m := sync.Mutex{}

	var code string
	for k, tc := range []struct {
		d                         string
		cb                        func(t *testing.T) httprouter.Handle
		authURL                   string
		shouldPassConsentStrategy bool
		expectOAuthAuthError      bool
		expectOAuthTokenError     bool
		authTime                  time.Time
		requestTime               time.Time
	}{
		{
			d:                         "should pass request if no previous authN or authZ exists",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state"),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					require.NotEmpty(t, code)
					w.Write([]byte(r.URL.Query().Get("code")))
				}
			},
		},
		{
			d:                         "should fail because prompt=none and max_age > auth_time",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state") + "&prompt=none&max_age=1",
			authTime:                  time.Now().UTC().Add(-time.Minute),
			requestTime:               time.Now().UTC(),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					err := r.URL.Query().Get("error")
					require.Empty(t, code)
					require.EqualValues(t, fosite.ErrLoginRequired.Error(), err)
				}
			},
			expectOAuthAuthError: true,
		},
		{
			d:                         "should pass because prompt=none and max_age < auth_time",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state") + "&prompt=none&max_age=10",
			authTime:                  time.Now().UTC().Add(-time.Second * 5),
			requestTime:               time.Now().UTC(),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					require.NotEmpty(t, code)
					w.Write([]byte(r.URL.Query().Get("code")))
				}
			},
		},
		{
			d:                         "should fail because prompt=none but auth_time suggests recent authentication",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state") + "&prompt=none",
			authTime:                  time.Now().UTC().Add(time.Minute),
			requestTime:               time.Now().UTC(),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					err := r.URL.Query().Get("error")
					require.Empty(t, code)
					require.EqualValues(t, fosite.ErrLoginRequired.Error(), err)
				}
			},
			expectOAuthAuthError: true,
		},
		{
			d:                         "should fail because consent strategy fails",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state"),
			expectOAuthAuthError:      true,
			shouldPassConsentStrategy: false,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					require.Empty(t, r.URL.Query().Get("code"))
					assert.Equal(t, fosite.ErrRequestForbidden.Error(), r.URL.Query().Get("error"))
				}
			},
		},
	} {
		t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
			m.Lock()
			defer m.Unlock()
			if tc.cb == nil {
				tc.cb = noopHandler
			}

			consentStrategy.deny = !tc.shouldPassConsentStrategy
			consentStrategy.authTime = tc.authTime
			consentStrategy.requestTime = tc.requestTime

			cb := tc.cb(t)
			callbackHandler = &cb

			req, err := http.NewRequest("GET", tc.authURL, nil)
			require.NoError(t, err)

			resp, err := (&http.Client{Jar: newCookieJar()}).Do(req)
			require.NoError(t, err)
			defer resp.Body.Close()

			if tc.expectOAuthAuthError {
				return
			}

			require.NotEmpty(t, code)

			token, err := oauthConfig.Exchange(oauth2.NoContext, code)

			if tc.expectOAuthTokenError {
				require.Error(t, err)
				return
			}

			require.NoError(t, err, code)

			t.Run("case=userinfo", func(t *testing.T) {

				var makeRequest = func(req *http.Request) *http.Response {
					resp, err = http.DefaultClient.Do(req)
					require.NoError(t, err)
					return resp
				}

				var testSuccess = func(response *http.Response) {
					defer resp.Body.Close()

					require.Equal(t, http.StatusOK, resp.StatusCode)

					var claims map[string]interface{}
					require.NoError(t, json.NewDecoder(resp.Body).Decode(&claims))
					assert.Equal(t, "foo", claims["sub"])
				}

				req, err = http.NewRequest("GET", ts.URL+"/userinfo", nil)
				req.Header.Add("Authorization", "bearer "+token.AccessToken)
				testSuccess(makeRequest(req))

				req, err = http.NewRequest("POST", ts.URL+"/userinfo", nil)
				req.Header.Add("Authorization", "bearer "+token.AccessToken)
				testSuccess(makeRequest(req))

				req, err = http.NewRequest("POST", ts.URL+"/userinfo", bytes.NewBuffer([]byte("access_token="+token.AccessToken)))
				req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
				testSuccess(makeRequest(req))

				req, err = http.NewRequest("GET", ts.URL+"/userinfo", nil)
				req.Header.Add("Authorization", "bearer asdfg")
				resp := makeRequest(req)
				require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
			})

			res, err := testRefresh(t, token)
			require.NoError(t, err)
			assert.Equal(t, http.StatusOK, res.StatusCode)

			t.Run("duplicate code exchange fails", func(t *testing.T) {
				token, err := oauthConfig.Exchange(oauth2.NoContext, code)
				require.Error(t, err)
				require.Nil(t, token)
			})

			code = ""
		})
	}
}

func testRefresh(t *testing.T, token *oauth2.Token) (*http.Response, error) {
	req, err := http.NewRequest("POST", oauthClientConfig.TokenURL, strings.NewReader(url.Values{
		"grant_type":    []string{"refresh_token"},
		"refresh_token": []string{token.RefreshToken},
	}.Encode()))
	require.NoError(t, err)

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.SetBasicAuth(oauthClientConfig.ClientID, oauthClientConfig.ClientSecret)

	return http.DefaultClient.Do(req)
}
<commit_msg>consent: Resolves timing issues in slow tests<commit_after>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * 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.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 */

package oauth2_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/julienschmidt/httprouter"
	"github.com/ory/fosite"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/oauth2"
)

func newCookieJar() http.CookieJar {
	c, _ := cookiejar.New(nil)
	return c
}

func noopHandler(t *testing.T) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		w.WriteHeader(http.StatusNotImplemented)
	}
}

func TestAuthCodeSuite(t *testing.T) {
	// Regular request without any previous authentication
	// Regular request with previous authentication
	// Regular request with previous authentication and prompt login/consent
	// Regular request with previous authentication and prompt none
	// Regular request with previous authentication and prompt none and proper id_token_hint
	// Regular request without authentication and prompt none (fail)
	// Regular request with previous authentication and prompt none and very low max_age (fail)
	// Regular request with previous authentication and prompt none and mismatching id_token_hint (fail)
	// Regular request fails if login is denied
	// Regular request fails if consent is denied

	var callbackHandler *httprouter.Handle
	router.GET("/callback", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		(*callbackHandler)(w, r, ps)
	})
	m := sync.Mutex{}

	var code string
	for k, tc := range []struct {
		d                         string
		cb                        func(t *testing.T) httprouter.Handle
		authURL                   string
		shouldPassConsentStrategy bool
		expectOAuthAuthError      bool
		expectOAuthTokenError     bool
		authTime                  time.Time
		requestTime               time.Time
	}{
		{
			d:                         "should pass request if no previous authN or authZ exists",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state"),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					require.NotEmpty(t, code)
					w.Write([]byte(r.URL.Query().Get("code")))
				}
			},
		},
		{
			d:                         "should fail because prompt=none and max_age > auth_time",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state") + "&prompt=none&max_age=1",
			authTime:                  time.Now().UTC().Add(-time.Minute),
			requestTime:               time.Now().UTC(),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					err := r.URL.Query().Get("error")
					require.Empty(t, code)
					require.EqualValues(t, fosite.ErrLoginRequired.Error(), err)
				}
			},
			expectOAuthAuthError: true,
		},
		{
			d:                         "should pass because prompt=none and max_age < auth_time",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state") + "&prompt=none&max_age=3600",
			authTime:                  time.Now().UTC().Add(-time.Minute),
			requestTime:               time.Now().UTC(),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					require.NotEmpty(t, code)
					w.Write([]byte(r.URL.Query().Get("code")))
				}
			},
		},
		{
			d:                         "should fail because prompt=none but auth_time suggests recent authentication",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state") + "&prompt=none",
			authTime:                  time.Now().UTC().Add(time.Minute),
			requestTime:               time.Now().UTC(),
			shouldPassConsentStrategy: true,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					code = r.URL.Query().Get("code")
					err := r.URL.Query().Get("error")
					require.Empty(t, code)
					require.EqualValues(t, fosite.ErrLoginRequired.Error(), err)
				}
			},
			expectOAuthAuthError: true,
		},
		{
			d:                         "should fail because consent strategy fails",
			authURL:                   oauthConfig.AuthCodeURL("some-foo-state"),
			expectOAuthAuthError:      true,
			shouldPassConsentStrategy: false,
			cb: func(t *testing.T) httprouter.Handle {
				return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
					require.Empty(t, r.URL.Query().Get("code"))
					assert.Equal(t, fosite.ErrRequestForbidden.Error(), r.URL.Query().Get("error"))
				}
			},
		},
	} {
		t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
			m.Lock()
			defer m.Unlock()
			if tc.cb == nil {
				tc.cb = noopHandler
			}

			consentStrategy.deny = !tc.shouldPassConsentStrategy
			consentStrategy.authTime = tc.authTime
			consentStrategy.requestTime = tc.requestTime

			cb := tc.cb(t)
			callbackHandler = &cb

			req, err := http.NewRequest("GET", tc.authURL, nil)
			require.NoError(t, err)

			resp, err := (&http.Client{Jar: newCookieJar()}).Do(req)
			require.NoError(t, err)
			defer resp.Body.Close()

			if tc.expectOAuthAuthError {
				return
			}

			require.NotEmpty(t, code)

			token, err := oauthConfig.Exchange(oauth2.NoContext, code)

			if tc.expectOAuthTokenError {
				require.Error(t, err)
				return
			}

			require.NoError(t, err, code)

			t.Run("case=userinfo", func(t *testing.T) {

				var makeRequest = func(req *http.Request) *http.Response {
					resp, err = http.DefaultClient.Do(req)
					require.NoError(t, err)
					return resp
				}

				var testSuccess = func(response *http.Response) {
					defer resp.Body.Close()

					require.Equal(t, http.StatusOK, resp.StatusCode)

					var claims map[string]interface{}
					require.NoError(t, json.NewDecoder(resp.Body).Decode(&claims))
					assert.Equal(t, "foo", claims["sub"])
				}

				req, err = http.NewRequest("GET", ts.URL+"/userinfo", nil)
				req.Header.Add("Authorization", "bearer "+token.AccessToken)
				testSuccess(makeRequest(req))

				req, err = http.NewRequest("POST", ts.URL+"/userinfo", nil)
				req.Header.Add("Authorization", "bearer "+token.AccessToken)
				testSuccess(makeRequest(req))

				req, err = http.NewRequest("POST", ts.URL+"/userinfo", bytes.NewBuffer([]byte("access_token="+token.AccessToken)))
				req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
				testSuccess(makeRequest(req))

				req, err = http.NewRequest("GET", ts.URL+"/userinfo", nil)
				req.Header.Add("Authorization", "bearer asdfg")
				resp := makeRequest(req)
				require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
			})

			res, err := testRefresh(t, token)
			require.NoError(t, err)
			assert.Equal(t, http.StatusOK, res.StatusCode)

			t.Run("duplicate code exchange fails", func(t *testing.T) {
				token, err := oauthConfig.Exchange(oauth2.NoContext, code)
				require.Error(t, err)
				require.Nil(t, token)
			})

			code = ""
		})
	}
}

func testRefresh(t *testing.T, token *oauth2.Token) (*http.Response, error) {
	req, err := http.NewRequest("POST", oauthClientConfig.TokenURL, strings.NewReader(url.Values{
		"grant_type":    []string{"refresh_token"},
		"refresh_token": []string{token.RefreshToken},
	}.Encode()))
	require.NoError(t, err)

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.SetBasicAuth(oauthClientConfig.ClientID, oauthClientConfig.ClientSecret)

	return http.DefaultClient.Do(req)
}
<|endoftext|>