<commit_before>package gorma

import (
	"path/filepath"
	"strings"
	"unicode"

	"github.com/qor/inflection"
	"github.com/raphael/goa/design"
	"github.com/raphael/goa/goagen/codegen"
)

// CamelToSnake converts a given string to snake case
func CamelToSnake(s string) string {
	var result string
	var words []string
	var lastPos int
	rs := []rune(s)

	for i := 0; i < len(rs); i++ {
		if i > 0 && unicode.IsUpper(rs[i]) {
			if initialism := startsWithInitialism(s[lastPos:]); initialism != "" {
				words = append(words, initialism)

				i += len(initialism) - 1
				lastPos = i
				continue
			}

			words = append(words, s[lastPos:i])
			lastPos = i
		}
	}

	// append the last word
	if s[lastPos:] != "" {
		words = append(words, s[lastPos:])
	}

	for k, word := range words {
		if k > 0 {
			result += "_"
		}

		result += strings.ToLower(word)
	}

	return result
}

// JSONSchemaDir is the path to the directory where the schema controller is generated.
func ModelDir() string {
	return filepath.Join(codegen.OutputDir, "models")
}

func DeModel(s string) string {
	return strings.Replace(s, "Model", "", -1)
}

func Lower(s string) string {
	return strings.ToLower(s)
}
func Upper(s string) string {
	return strings.ToUpper(s)
}

func StorageDefinition(res *design.UserTypeDefinition) string {
	//List(ctx *app.List{{demodel $typeName}}Context) []{{$typeName}}
	// Add(ctx *app.Create{{demodel $typeName}}Context) ({{$typeName}}, error)
	//Delete(ctx *app.Delete{{demodel $typeName}}Context) (error)
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			if DeModel(res.TypeName) == pieces[1] {
				associations = associations + "List" + pieces[0] + "(ctx *app.List" + strings.ToLower(pieces[0]) + res.TypeName + "Context) []" + pieces[1] + "\n"
				associations = associations + "Add" + pieces[1] + "(ctx *app.Create" + strings.ToLower(pieces[1]) + res.TypeName + "Context) " + pieces[1] + " error\n"
				associations = associations + "Delete" + pieces[1] + "(ctx *app.Create" + strings.ToLower(pieces[1]) + res.TypeName + "Context) error "
			}
		}
	}
	return associations
}
func IncludeForeignKey(res *design.UserTypeDefinition) string {
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#belongsto"]; ok {
		return assoc + "ID int\n"
	}
	return ""
}
func IncludeChildren(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasmany"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + inflection.Plural(child) + " []" + child + "\n"
		}
	}
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasone"]; ok {
		children := strings.Split(assoc, ",")
		for _, child := range children {
			associations = associations + child + " " + child + "\n"
			associations = associations + child + "ID " + "*sql.NullInt64\n"
		}
	}
	return associations
}

func IncludeMany2Many(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			associations = associations + pieces[0] + "\t" + pieces[1] + "\t" + "`gorm:\"many2many:" + pieces[2] + ";\"`\n"
		}
	}
	return associations
}
func Authboss(res *design.UserTypeDefinition) string {
	if _, ok := res.Metadata["github.com/bketelsen/gorma#authboss"]; ok {
		fields := `	// Auth
	Email    string

	// OAuth2
	Oauth2Uid      string
	Oauth2Provider string
	Oauth2Token    string
	Oauth2Refresh  string
	Oauth2Expiry   time.Time

	// Confirm
	ConfirmToken string
	Confirmed    bool

	// Lock
	AttemptNumber int64
	AttemptTime   time.Time
	Locked        time.Time

	// Recover
	RecoverToken       string
	RecoverTokenExpiry time.Time
	`
		return fields
	}
	return ""
}

func Split(s string, sep string) []string {

	return strings.Split(s, sep)
}

func MakeModelDef(s string, res *design.UserTypeDefinition) string {

	start := s[0:strings.Index(s, "{")+1] + "\n  	ID        int `gorm:\"primary_key\"`\nCreatedAt time.Time\nUpdatedAt time.Time\nDeletedAt *time.Time\n" + IncludeMany2Many(res) + IncludeForeignKey(res) + IncludeChildren(res) + Authboss(res) + s[strings.Index(s, "{")+2:]
	newstrings := make([]string, 0)
	chunks := strings.Split(start, "\n")
	// Good lord, shoot me for this hack - remove the ID field in the model if it exists
	for _, chunk := range chunks {
		var isId, isEmail, isAuthboss bool
		if strings.HasPrefix(chunk, "\tID ") {
			isId = true
		}
		if _, ok := res.Metadata["github.com/bketelsen/gorma#authboss"]; ok && strings.HasPrefix(chunk, "\tEmail") {
			isAuthboss = true
			isEmail = true
		}
		if isAuthboss {
			if !isEmail && !isId {
				newstrings = append(newstrings, chunk)
			}
		} else {
			if !isId {
				newstrings = append(newstrings, chunk)
			}
		}

	}
	return strings.Join(newstrings, "\n")
}

// Is c an ASCII lower-case letter?
func isASCIILower(c byte) bool {
	return 'a' <= c && c <= 'z'
}

// Is c an ASCII digit?
func isASCIIDigit(c byte) bool {
	return '0' <= c && c <= '9'
}

func unexport(s string) string {
	return strings.ToLower(s[0:1]) + s[1:]
}

// startsWithInitialism returns the initialism if the given string begins with it
func startsWithInitialism(s string) string {
	var initialism string
	// the longest initialism is 5 char, the shortest 2
	for i := 1; i <= 5; i++ {
		if len(s) > i-1 && commonInitialisms[s[:i]] {
			initialism = s[:i]
		}
	}
	return initialism
}

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/3d26dc39376c307203d3a221bada26816b3073cf/lint.go#L482
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
}
<commit_msg>add association methods<commit_after>package gorma

import (
	"fmt"
	"path/filepath"
	"strings"
	"unicode"

	"github.com/qor/inflection"
	"github.com/raphael/goa/design"
	"github.com/raphael/goa/goagen/codegen"
)

// CamelToSnake converts a given string to snake case
func CamelToSnake(s string) string {
	var result string
	var words []string
	var lastPos int
	rs := []rune(s)

	for i := 0; i < len(rs); i++ {
		if i > 0 && unicode.IsUpper(rs[i]) {
			if initialism := startsWithInitialism(s[lastPos:]); initialism != "" {
				words = append(words, initialism)

				i += len(initialism) - 1
				lastPos = i
				continue
			}

			words = append(words, s[lastPos:i])
			lastPos = i
		}
	}

	// append the last word
	if s[lastPos:] != "" {
		words = append(words, s[lastPos:])
	}

	for k, word := range words {
		if k > 0 {
			result += "_"
		}

		result += strings.ToLower(word)
	}

	return result
}

// JSONSchemaDir is the path to the directory where the schema controller is generated.
func ModelDir() string {
	return filepath.Join(codegen.OutputDir, "models")
}

func DeModel(s string) string {
	return strings.Replace(s, "Model", "", -1)
}

func Lower(s string) string {
	return strings.ToLower(s)
}
func Upper(s string) string {
	return strings.ToUpper(s)
}

func StorageDefinition(res *design.UserTypeDefinition) string {

	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			fmt.Println("child")
			pieces := strings.Split(child, ":")
			fmt.Println(pieces)
			if DeModel(res.TypeName) == pieces[1] {
				associations = associations + "List" + pieces[0] + "(ctx *app.List" + strings.ToLower(pieces[0]) + res.TypeName + "Context) []" + pieces[1] + "\n"
				associations = associations + "Add" + pieces[1] + "(ctx *app.Create" + strings.ToLower(pieces[1]) + res.TypeName + "Context) " + pieces[1] + " error\n"
				associations = associations + "Delete" + pieces[1] + "(ctx *app.Create" + strings.ToLower(pieces[1]) + res.TypeName + "Context) error "
			}
		}
	}
	return associations
}
func IncludeForeignKey(res *design.UserTypeDefinition) string {
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#belongsto"]; ok {
		return assoc + "ID int\n"
	}
	return ""
}
func IncludeChildren(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasmany"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + inflection.Plural(child) + " []" + child + "\n"
		}
	}
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasone"]; ok {
		children := strings.Split(assoc, ",")
		for _, child := range children {
			associations = associations + child + " " + child + "\n"
			associations = associations + child + "ID " + "*sql.NullInt64\n"
		}
	}
	return associations
}

func IncludeMany2Many(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			associations = associations + pieces[0] + "\t" + pieces[1] + "\t" + "`gorm:\"many2many:" + pieces[2] + ";\"`\n"
		}
	}
	return associations
}
func Authboss(res *design.UserTypeDefinition) string {
	if _, ok := res.Metadata["github.com/bketelsen/gorma#authboss"]; ok {
		fields := `	// Auth
	Email    string

	// OAuth2
	Oauth2Uid      string
	Oauth2Provider string
	Oauth2Token    string
	Oauth2Refresh  string
	Oauth2Expiry   time.Time

	// Confirm
	ConfirmToken string
	Confirmed    bool

	// Lock
	AttemptNumber int64
	AttemptTime   time.Time
	Locked        time.Time

	// Recover
	RecoverToken       string
	RecoverTokenExpiry time.Time
	`
		return fields
	}
	return ""
}

func Split(s string, sep string) []string {

	return strings.Split(s, sep)
}

func MakeModelDef(s string, res *design.UserTypeDefinition) string {

	start := s[0:strings.Index(s, "{")+1] + "\n  	ID        int `gorm:\"primary_key\"`\nCreatedAt time.Time\nUpdatedAt time.Time\nDeletedAt *time.Time\n" + IncludeMany2Many(res) + IncludeForeignKey(res) + IncludeChildren(res) + Authboss(res) + s[strings.Index(s, "{")+2:]
	newstrings := make([]string, 0)
	chunks := strings.Split(start, "\n")
	// Good lord, shoot me for this hack - remove the ID field in the model if it exists
	for _, chunk := range chunks {
		var isId, isEmail, isAuthboss bool
		if strings.HasPrefix(chunk, "\tID ") {
			isId = true
		}
		if _, ok := res.Metadata["github.com/bketelsen/gorma#authboss"]; ok && strings.HasPrefix(chunk, "\tEmail") {
			isAuthboss = true
			isEmail = true
		}
		if isAuthboss {
			if !isEmail && !isId {
				newstrings = append(newstrings, chunk)
			}
		} else {
			if !isId {
				newstrings = append(newstrings, chunk)
			}
		}

	}
	return strings.Join(newstrings, "\n")
}

// Is c an ASCII lower-case letter?
func isASCIILower(c byte) bool {
	return 'a' <= c && c <= 'z'
}

// Is c an ASCII digit?
func isASCIIDigit(c byte) bool {
	return '0' <= c && c <= '9'
}

func unexport(s string) string {
	return strings.ToLower(s[0:1]) + s[1:]
}

// startsWithInitialism returns the initialism if the given string begins with it
func startsWithInitialism(s string) string {
	var initialism string
	// the longest initialism is 5 char, the shortest 2
	for i := 1; i <= 5; i++ {
		if len(s) > i-1 && commonInitialisms[s[:i]] {
			initialism = s[:i]
		}
	}
	return initialism
}

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/3d26dc39376c307203d3a221bada26816b3073cf/lint.go#L482
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/config"
)

// ResourceProvisionerConfig is used to pair a provisioner
// with its provided configuration. This allows us to use singleton
// instances of each ResourceProvisioner and to keep the relevant
// configuration instead of instantiating a new Provisioner for each
// resource.
type ResourceProvisionerConfig struct {
	Type        string
	Provisioner ResourceProvisioner
	Config      *ResourceConfig
	RawConfig   *config.RawConfig
	ConnInfo    *config.RawConfig
}

// Resource encapsulates a resource, its configuration, its provider,
// its current state, and potentially a desired diff from the state it
// wants to reach.
type Resource struct {
	Id           string
	Config       *ResourceConfig
	Diff         *ResourceDiff
	Provider     ResourceProvider
	State        *ResourceState
	Provisioners []*ResourceProvisionerConfig
	Tainted      bool
}

// Vars returns the mapping of variables that should be replaced in
// configuration based on the attributes of this resource.
func (r *Resource) Vars() map[string]string {
	if r.State == nil {
		return nil
	}

	vars := make(map[string]string)
	for ak, av := range r.State.Attributes {
		vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
	}

	return vars
}

// ResourceConfig holds the configuration given for a resource. This is
// done instead of a raw `map[string]interface{}` type so that rich
// methods can be added to it to make dealing with it easier.
type ResourceConfig struct {
	ComputedKeys []string
	Raw          map[string]interface{}
	Config       map[string]interface{}

	raw *config.RawConfig
}

// NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
	result := &ResourceConfig{raw: c}
	result.interpolate(nil)
	return result
}

// CheckSet checks that the given list of configuration keys is
// properly set. If not, errors are returned for each unset key.
//
// This is useful to be called in the Validate method of a ResourceProvider.
func (c *ResourceConfig) CheckSet(keys []string) []error {
	var errs []error

	for _, k := range keys {
		if !c.IsSet(k) {
			errs = append(errs, fmt.Errorf("%s must be set", k))
		}
	}

	return errs
}

// Get looks up a configuration value by key and returns the value.
//
// The second return value is true if the get was successful. Get will
// not succeed if the value is being computed.
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
	result, ok := c.get(k, c.Config)
	if ok {
		return result, ok
	}

	return c.get(k, c.Raw)
}

// IsSet checks if the key in the configuration is set. A key is set if
// it has a value or the value is being computed (is unknown currently).
//
// This function should be used rather than checking the keys of the
// raw configuration itself, since a key may be omitted from the raw
// configuration if it is being computed.
func (c *ResourceConfig) IsSet(k string) bool {
	if c == nil {
		return false
	}

	for _, ck := range c.ComputedKeys {
		if ck == k {
			return true
		}
	}

	if _, ok := c.Get(k); ok {
		return true
	}

	return false
}

func (c *ResourceConfig) get(
	k string, raw map[string]interface{}) (interface{}, bool) {
	parts := strings.Split(k, ".")

	var current interface{} = raw
	for _, part := range parts {
		if current == nil {
			return nil, false
		}

		cv := reflect.ValueOf(current)
		switch cv.Kind() {
		case reflect.Map:
			v := cv.MapIndex(reflect.ValueOf(part))
			if !v.IsValid() {
				return nil, false
			}
			current = v.Interface()
		case reflect.Slice:
			if part == "#" {
				current = cv.Len()
			} else {
				i, err := strconv.ParseInt(part, 0, 0)
				if err != nil {
					return nil, false
				}
				current = cv.Index(int(i)).Interface()
			}
		default:
			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
		}
	}

	return current, true
}

func (c *ResourceConfig) interpolate(ctx *Context) error {
	if c == nil {
		return nil
	}

	if ctx != nil {
		if err := ctx.computeVars(c.raw); err != nil {
			return err
		}
	}

	if c.raw != nil {
		c.ComputedKeys = c.raw.UnknownKeys()
		c.Raw = c.raw.Raw
		c.Config = c.raw.Config()
	}

	return nil
}
<commit_msg>terraform: better comments<commit_after>package terraform

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/config"
)

// ResourceProvisionerConfig is used to pair a provisioner
// with its provided configuration. This allows us to use singleton
// instances of each ResourceProvisioner and to keep the relevant
// configuration instead of instantiating a new Provisioner for each
// resource.
type ResourceProvisionerConfig struct {
	Type        string
	Provisioner ResourceProvisioner
	Config      *ResourceConfig
	RawConfig   *config.RawConfig
	ConnInfo    *config.RawConfig
}

// Resource encapsulates a resource, its configuration, its provider,
// its current state, and potentially a desired diff from the state it
// wants to reach.
type Resource struct {
	Id           string
	Config       *ResourceConfig
	Diff         *ResourceDiff
	Provider     ResourceProvider
	State        *ResourceState
	Provisioners []*ResourceProvisionerConfig
	Tainted      bool
}

// Vars returns the mapping of variables that should be replaced in
// configuration based on the attributes of this resource.
func (r *Resource) Vars() map[string]string {
	if r.State == nil {
		return nil
	}

	vars := make(map[string]string)
	for ak, av := range r.State.Attributes {
		vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
	}

	return vars
}

// ResourceConfig holds the configuration given for a resource. This is
// done instead of a raw `map[string]interface{}` type so that rich
// methods can be added to it to make dealing with it easier.
type ResourceConfig struct {
	ComputedKeys []string
	Raw          map[string]interface{}
	Config       map[string]interface{}

	raw *config.RawConfig
}

// NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
	result := &ResourceConfig{raw: c}
	result.interpolate(nil)
	return result
}

// CheckSet checks that the given list of configuration keys is
// properly set. If not, errors are returned for each unset key.
//
// This is useful to be called in the Validate method of a ResourceProvider.
func (c *ResourceConfig) CheckSet(keys []string) []error {
	var errs []error

	for _, k := range keys {
		if !c.IsSet(k) {
			errs = append(errs, fmt.Errorf("%s must be set", k))
		}
	}

	return errs
}

// Get looks up a configuration value by key and returns the value.
//
// The second return value is true if the get was successful. Get will
// not succeed if the value is being computed.
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
	// First try to get it from c.Config since that has interpolated values
	result, ok := c.get(k, c.Config)
	if ok {
		return result, ok
	}

	// Otherwise, just get it from the raw config
	return c.get(k, c.Raw)
}

// IsSet checks if the key in the configuration is set. A key is set if
// it has a value or the value is being computed (is unknown currently).
//
// This function should be used rather than checking the keys of the
// raw configuration itself, since a key may be omitted from the raw
// configuration if it is being computed.
func (c *ResourceConfig) IsSet(k string) bool {
	if c == nil {
		return false
	}

	for _, ck := range c.ComputedKeys {
		if ck == k {
			return true
		}
	}

	if _, ok := c.Get(k); ok {
		return true
	}

	return false
}

func (c *ResourceConfig) get(
	k string, raw map[string]interface{}) (interface{}, bool) {
	parts := strings.Split(k, ".")

	var current interface{} = raw
	for _, part := range parts {
		if current == nil {
			return nil, false
		}

		cv := reflect.ValueOf(current)
		switch cv.Kind() {
		case reflect.Map:
			v := cv.MapIndex(reflect.ValueOf(part))
			if !v.IsValid() {
				return nil, false
			}
			current = v.Interface()
		case reflect.Slice:
			if part == "#" {
				current = cv.Len()
			} else {
				i, err := strconv.ParseInt(part, 0, 0)
				if err != nil {
					return nil, false
				}
				current = cv.Index(int(i)).Interface()
			}
		default:
			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
		}
	}

	return current, true
}

func (c *ResourceConfig) interpolate(ctx *Context) error {
	if c == nil {
		return nil
	}

	if ctx != nil {
		if err := ctx.computeVars(c.raw); err != nil {
			return err
		}
	}

	if c.raw != nil {
		c.ComputedKeys = c.raw.UnknownKeys()
		c.Raw = c.raw.Raw
		c.Config = c.raw.Config()
	}

	return nil
}
<|endoftext|>


<commit_before>package hge

/*
#cgo pkg-config: hge-unix-c
#include "hge_c.h"
#include "callback.h"
#include <stdio.h>
void goHGE_System_Log(HGE_t *h, const char *str) {
	HGE_System_Log(h, str);
}
*/
import "C"

import (
	"fmt"
	"math"
	"runtime"
	"unsafe"
)

const (
	VERSION = C.HGE_VERSION
)

type Dword uint32

// Common math constants
const (
	Pi     = math.Pi
	Pi_2   = math.Pi / 2
	Pi_4   = math.Pi / 4
	One_Pi = 1 / math.Pi
	Two_Pi = 2 / math.Pi
)

// HGE System state constants
const (
	WINDOWED      BoolState = C.HGE_C_WINDOWED      // bool run in window? (default: false)
	ZBUFFER       BoolState = C.HGE_C_ZBUFFER       // bool use z-buffer? (default: false)
	TEXTUREFILTER BoolState = C.HGE_C_TEXTUREFILTER // bool texture filtering? (default: true)

	USESOUND BoolState = C.HGE_C_USESOUND // bool use sound? (default: true)

	DONTSUSPEND BoolState = C.HGE_C_DONTSUSPEND // bool focus lost:suspend? (default: false)
	HIDEMOUSE   BoolState = C.HGE_C_HIDEMOUSE   // bool hide system cursor? (default: true)

	SHOWSPLASH BoolState = C.HGE_C_SHOWSPLASH // bool show splash? (default: true)

	BOOLSTATE_FORCE_DWORD BoolState = C.HGE_C_BOOLSTATE_FORCE_DWORD
)

const (
	FRAMEFUNC      FuncState = C.HGE_C_FRAMEFUNC      // func() bool frame function (default: nil) (you MUST set this)
	RENDERFUNC     FuncState = C.HGE_C_RENDERFUNC     // func() bool render function (default: nil)
	FOCUSLOSTFUNC  FuncState = C.HGE_C_FOCUSLOSTFUNC  // func() bool focus lost function (default: nil)
	FOCUSGAINFUNC  FuncState = C.HGE_C_FOCUSGAINFUNC  // func() bool focus gain function (default: nil)
	GFXRESTOREFUNC FuncState = C.HGE_C_GFXRESTOREFUNC // func() bool gfx restore function (default: nil)
	EXITFUNC       FuncState = C.HGE_C_EXITFUNC       // func() bool exit function (default: nil)

	FUNCSTATE_FORCE_DWORD FuncState = C.HGE_C_FUNCSTATE_FORCE_DWORD
)

const (
	HWND       HwndState = C.HGE_C_HWND       // int		window handle: read only
	HWNDPARENT HwndState = C.HGE_C_HWNDPARENT // int		parent win handle	(default: 0)

	HWNDSTATE_FORCE_DWORD HwndState = C.HGE_C_HWNDSTATE_FORCE_DWORD
)

type Hwnd struct {
	hwnd C.HWND
}

const (
	SCREENWIDTH  IntState = C.HGE_C_SCREENWIDTH  // int screen width (default: 800)
	SCREENHEIGHT IntState = C.HGE_C_SCREENHEIGHT // int screen height (default: 600)
	SCREENBPP    IntState = C.HGE_C_SCREENBPP    // int screen bitdepth (default: 32) (desktop bpp in windowed mode)

	SAMPLERATE   IntState = C.HGE_C_SAMPLERATE   // int sample rate (default: 44100)
	FXVOLUME     IntState = C.HGE_C_FXVOLUME     // int global fx volume (default: 100)
	MUSVOLUME    IntState = C.HGE_C_MUSVOLUME    // int global music volume (default: 100)
	STREAMVOLUME IntState = C.HGE_C_STREAMVOLUME // int stream music volume (default: 100)

	FPS IntState = C.HGE_C_FPS // int fixed fps (default: hge.FPS_UNLIMITED)

	POWERSTATUS IntState = C.HGE_C_POWERSTATUS // int battery life percent + status

	ORIGSCREENWIDTH  IntState = C.HGE_C_ORIGSCREENWIDTH  // int original screen width (default: 800 ... not valid until hge.System_Initiate()!)
	ORIGSCREENHEIGHT IntState = C.HGE_C_ORIGSCREENHEIGHT // int original screen height (default: 600 ... not valid until hge.System_Initiate()!))

	INTSTATE_FORCE_DWORD IntState = C.HGE_C_INTSTATE_FORCE_DWORD
)

const (
	ICON  StringState = C.HGE_C_ICON  // string icon resource (default: nil)
	TITLE StringState = C.HGE_C_TITLE // string window title (default: "HGE")

	INIFILE StringState = C.HGE_C_INIFILE // string ini file (default: nil) (meaning no file)
	LOGFILE StringState = C.HGE_C_LOGFILE // string log file (default: nil) (meaning no file)

	STRINGSTATE_FORCE_DWORD StringState = C.HGE_C_STRINGSTATE_FORCE_DWORD
)

type (
	BoolState   int
	FuncState   int
	HwndState   int
	IntState    int
	StringState int
)

// HGE_POWERSTATUS system state special constants
const (
	PWR_AC          = C.HGE_PWR_AC
	PWR_UNSUPPORTED = C.HGE_PWR_UNSUPPORTED
)

func BoolToCInt(b bool) C.BOOL {
	if b {
		return 1
	}

	return 0
}

// HGE struct from C
type HGE struct {
	HGE *C.HGE_t
}

type Error struct {
	*HGE
}

func (e *Error) Error() string {
	return e.HGE.GetErrorMessage()
}

// Creates a new instance of an hge structure
func New(a ...interface{}) *HGE {
	ver := VERSION

	if len(a) == 1 {
		if v, ok := a[0].(int); ok {
			ver = v
		}
	}

	h := new(HGE)
	h.HGE = C.HGE_Create(C.int(ver))
	fmt.Println("Created HGE", *h)
	runtime.SetFinalizer(h, func(hge *HGE) {
		hge.Free()
	})

	return h
}

// Releases the memory the C++ library allocated for the HGE struct
func (h *HGE) Free() {
	fmt.Println("Freeing HGE", *h)
	C.HGE_Release(h.HGE)
}

// Initializes hardware and software needed to run engine.
func (h *HGE) Initiate() error {
	if C.HGE_System_Initiate(h.HGE) == 0 {
		return &Error{h}
	}

	return nil
}

//  Restores video mode and frees allocated resources.
func (h *HGE) Shutdown() {
	C.HGE_System_Shutdown(h.HGE)
}

// Starts running user defined frame func (h *HGE)tion.
func (h *HGE) Start() error {
	if C.HGE_System_Start(h.HGE) == 0 {
		return &Error{h}
	}

	return nil
}

//  Returns last occured HGE error description.
func (h *HGE) GetErrorMessage() string {
	return C.GoString(C.HGE_System_GetErrorMessage(h.HGE))
}

// Writes a formatted message to the log file.
func (h *HGE) Log(format string, v ...interface{}) {
	var str string

	if v != nil {
		str = fmt.Sprintf(format, v...)
	} else {
		str = format
	}

	fstr := C.CString(str)
	defer C.free(unsafe.Pointer(fstr))

	C.goHGE_System_Log(h.HGE, fstr)
}

// Launches an URL or external executable/data file.
func (h *HGE) Launch(url string) bool {
	urlstr := C.CString(url)
	defer C.free(unsafe.Pointer(urlstr))

	return C.HGE_System_Launch(h.HGE, urlstr) == 1
}

//  Saves current screen snapshot into a file.
func (h *HGE) Snapshot(a ...interface{}) {
	if len(a) == 1 {
		if filename, ok := a[0].(string); ok {
			fname := C.CString(filename)
			defer C.free(unsafe.Pointer(fname))

			C.HGE_System_Snapshot(h.HGE, fname)
			return
		}
	}

	C.HGE_System_Snapshot(h.HGE, nil)
}

// Sets internal system states.
// First param should be one of: BoolState, IntState, StringState, FuncState, HwndState
// Second parameter must be of the matching type, bool, int, string, StateFunc/func (h *HGE)() int, *Hwnd
func (h *HGE) SetState(a ...interface{}) {
	if len(a) == 2 {
		switch a[0].(type) {
		case BoolState:
			if bs, ok := a[1].(bool); ok {
				h.setStateBool(a[0].(BoolState), bs)
				return
			}

		case IntState:
			if is, ok := a[1].(int); ok {
				h.setStateInt(a[0].(IntState), is)
				return
			}

		case StringState:
			if ss, ok := a[1].(string); ok {
				h.setStateString(a[0].(StringState), ss)
				return
			}

		case FuncState:
			switch a[1].(type) {
			case StateFunc:
				h.setStateFunc(a[0].(FuncState), a[1].(StateFunc))
				return
			case func() int:
				h.setStateFunc(a[0].(FuncState), a[1].(func() int))
				return
			}

		case HwndState:
			if hs, ok := a[1].(*Hwnd); ok {
				h.setStateHwnd(a[0].(HwndState), hs)
				return
			}
		}
	}
}

func (h *HGE) setStateBool(state BoolState, value bool) {
	C.HGE_System_SetStateBool(h.HGE, C.HGE_BoolState_t(state), BoolToCInt(value))
}

func (h *HGE) setStateFunc(state FuncState, value StateFunc) {
	funcCBs[state] = value
	switch state {
	case FRAMEFUNC:
		C.setFrameFunc(h.HGE, C.HGE_FuncState_t(state))
	case RENDERFUNC:
		C.setRenderFunc(h.HGE, C.HGE_FuncState_t(state))
	case FOCUSLOSTFUNC:
		C.setFocusLostFunc(h.HGE, C.HGE_FuncState_t(state))
	case FOCUSGAINFUNC:
		C.setFocusGainFunc(h.HGE, C.HGE_FuncState_t(state))
	case GFXRESTOREFUNC:
		C.setGfxRestoreFunc(h.HGE, C.HGE_FuncState_t(state))
	case EXITFUNC:
		C.setExitFunc(h.HGE, C.HGE_FuncState_t(state))
	}
}

func (h *HGE) setStateHwnd(state HwndState, value *Hwnd) {
	C.HGE_System_SetStateHwnd(h.HGE, C.HGE_HwndState_t(state), value.hwnd)
}

func (h *HGE) setStateInt(state IntState, value int) {
	C.HGE_System_SetStateInt(h.HGE, C.HGE_IntState_t(state), C.int(value))
}

func (h *HGE) setStateString(state StringState, value string) {
	val := C.CString(value)
	defer C.free(unsafe.Pointer(val))

	C.HGE_System_SetStateString(h.HGE, C.HGE_StringState_t(state), val)
}

// Returns internal system state values.
func (h *HGE) GetState(a ...interface{}) interface{} {
	if len(a) == 1 {
		switch a[0].(type) {
		case BoolState:
			return h.getStateBool(a[0].(BoolState))

		case IntState:
			return h.getStateInt(a[0].(IntState))

		case StringState:
			return h.getStateString(a[0].(StringState))

		case FuncState:
			return h.getStateFunc(a[0].(FuncState))

		case HwndState:
			return h.getStateHwnd(a[0].(HwndState))
		}
	}

	return nil
}

func (h *HGE) getStateBool(state BoolState) bool {
	return C.HGE_System_GetStateBool(h.HGE, C.HGE_BoolState_t(state)) == 1
}

func (h *HGE) getStateFunc(state FuncState) StateFunc {
	// I don't know how to convert the HGE_Callback C func (h *HGE)tion type to a Go
	// func (h *HGE)tion, so we just pass back the Go func (h *HGE)tion
	return funcCBs[state]
}

func (h *HGE) getStateHwnd(state HwndState) Hwnd {
	var hwnd Hwnd
	hwnd.hwnd = C.HGE_System_GetStateHwnd(h.HGE, C.HGE_HwndState_t(state))
	return hwnd
}

func (h *HGE) getStateInt(state IntState) int {
	return int(C.HGE_System_GetStateInt(h.HGE, C.HGE_IntState_t(state)))
}

func (h *HGE) getStateString(state StringState) string {
	return C.GoString(C.HGE_System_GetStateString(h.HGE, C.HGE_StringState_t(state)))
}
<commit_msg>Binding: Allow func() bool as a FuncState<commit_after>package hge

/*
#cgo pkg-config: hge-unix-c
#include "hge_c.h"
#include "callback.h"
#include <stdio.h>
void goHGE_System_Log(HGE_t *h, const char *str) {
	HGE_System_Log(h, str);
}
*/
import "C"

import (
	"fmt"
	"math"
	"runtime"
	"unsafe"
)

const (
	VERSION = C.HGE_VERSION
)

type Dword uint32

// Common math constants
const (
	Pi     = math.Pi
	Pi_2   = math.Pi / 2
	Pi_4   = math.Pi / 4
	One_Pi = 1 / math.Pi
	Two_Pi = 2 / math.Pi
)

// HGE System state constants
const (
	WINDOWED      BoolState = C.HGE_C_WINDOWED      // bool run in window? (default: false)
	ZBUFFER       BoolState = C.HGE_C_ZBUFFER       // bool use z-buffer? (default: false)
	TEXTUREFILTER BoolState = C.HGE_C_TEXTUREFILTER // bool texture filtering? (default: true)

	USESOUND BoolState = C.HGE_C_USESOUND // bool use sound? (default: true)

	DONTSUSPEND BoolState = C.HGE_C_DONTSUSPEND // bool focus lost:suspend? (default: false)
	HIDEMOUSE   BoolState = C.HGE_C_HIDEMOUSE   // bool hide system cursor? (default: true)

	SHOWSPLASH BoolState = C.HGE_C_SHOWSPLASH // bool show splash? (default: true)

	BOOLSTATE_FORCE_DWORD BoolState = C.HGE_C_BOOLSTATE_FORCE_DWORD
)

const (
	FRAMEFUNC      FuncState = C.HGE_C_FRAMEFUNC      // func() bool frame function (default: nil) (you MUST set this)
	RENDERFUNC     FuncState = C.HGE_C_RENDERFUNC     // func() bool render function (default: nil)
	FOCUSLOSTFUNC  FuncState = C.HGE_C_FOCUSLOSTFUNC  // func() bool focus lost function (default: nil)
	FOCUSGAINFUNC  FuncState = C.HGE_C_FOCUSGAINFUNC  // func() bool focus gain function (default: nil)
	GFXRESTOREFUNC FuncState = C.HGE_C_GFXRESTOREFUNC // func() bool gfx restore function (default: nil)
	EXITFUNC       FuncState = C.HGE_C_EXITFUNC       // func() bool exit function (default: nil)

	FUNCSTATE_FORCE_DWORD FuncState = C.HGE_C_FUNCSTATE_FORCE_DWORD
)

const (
	HWND       HwndState = C.HGE_C_HWND       // int		window handle: read only
	HWNDPARENT HwndState = C.HGE_C_HWNDPARENT // int		parent win handle	(default: 0)

	HWNDSTATE_FORCE_DWORD HwndState = C.HGE_C_HWNDSTATE_FORCE_DWORD
)

type Hwnd struct {
	hwnd C.HWND
}

const (
	SCREENWIDTH  IntState = C.HGE_C_SCREENWIDTH  // int screen width (default: 800)
	SCREENHEIGHT IntState = C.HGE_C_SCREENHEIGHT // int screen height (default: 600)
	SCREENBPP    IntState = C.HGE_C_SCREENBPP    // int screen bitdepth (default: 32) (desktop bpp in windowed mode)

	SAMPLERATE   IntState = C.HGE_C_SAMPLERATE   // int sample rate (default: 44100)
	FXVOLUME     IntState = C.HGE_C_FXVOLUME     // int global fx volume (default: 100)
	MUSVOLUME    IntState = C.HGE_C_MUSVOLUME    // int global music volume (default: 100)
	STREAMVOLUME IntState = C.HGE_C_STREAMVOLUME // int stream music volume (default: 100)

	FPS IntState = C.HGE_C_FPS // int fixed fps (default: hge.FPS_UNLIMITED)

	POWERSTATUS IntState = C.HGE_C_POWERSTATUS // int battery life percent + status

	ORIGSCREENWIDTH  IntState = C.HGE_C_ORIGSCREENWIDTH  // int original screen width (default: 800 ... not valid until hge.System_Initiate()!)
	ORIGSCREENHEIGHT IntState = C.HGE_C_ORIGSCREENHEIGHT // int original screen height (default: 600 ... not valid until hge.System_Initiate()!))

	INTSTATE_FORCE_DWORD IntState = C.HGE_C_INTSTATE_FORCE_DWORD
)

const (
	ICON  StringState = C.HGE_C_ICON  // string icon resource (default: nil)
	TITLE StringState = C.HGE_C_TITLE // string window title (default: "HGE")

	INIFILE StringState = C.HGE_C_INIFILE // string ini file (default: nil) (meaning no file)
	LOGFILE StringState = C.HGE_C_LOGFILE // string log file (default: nil) (meaning no file)

	STRINGSTATE_FORCE_DWORD StringState = C.HGE_C_STRINGSTATE_FORCE_DWORD
)

type (
	BoolState   int
	FuncState   int
	HwndState   int
	IntState    int
	StringState int
)

// HGE_POWERSTATUS system state special constants
const (
	PWR_AC          = C.HGE_PWR_AC
	PWR_UNSUPPORTED = C.HGE_PWR_UNSUPPORTED
)

func BoolToCInt(b bool) C.BOOL {
	if b {
		return 1
	}

	return 0
}

// HGE struct from C
type HGE struct {
	HGE *C.HGE_t
}

type Error struct {
	*HGE
}

func (e *Error) Error() string {
	return e.HGE.GetErrorMessage()
}

// Creates a new instance of an hge structure
func New(a ...interface{}) *HGE {
	ver := VERSION

	if len(a) == 1 {
		if v, ok := a[0].(int); ok {
			ver = v
		}
	}

	h := new(HGE)
	h.HGE = C.HGE_Create(C.int(ver))
	fmt.Println("Created HGE", *h)
	runtime.SetFinalizer(h, func(hge *HGE) {
		hge.Free()
	})

	return h
}

// Releases the memory the C++ library allocated for the HGE struct
func (h *HGE) Free() {
	fmt.Println("Freeing HGE", *h)
	C.HGE_Release(h.HGE)
}

// Initializes hardware and software needed to run engine.
func (h *HGE) Initiate() error {
	if C.HGE_System_Initiate(h.HGE) == 0 {
		return &Error{h}
	}

	return nil
}

//  Restores video mode and frees allocated resources.
func (h *HGE) Shutdown() {
	C.HGE_System_Shutdown(h.HGE)
}

// Starts running user defined frame func (h *HGE)tion.
func (h *HGE) Start() error {
	if C.HGE_System_Start(h.HGE) == 0 {
		return &Error{h}
	}

	return nil
}

//  Returns last occured HGE error description.
func (h *HGE) GetErrorMessage() string {
	return C.GoString(C.HGE_System_GetErrorMessage(h.HGE))
}

// Writes a formatted message to the log file.
func (h *HGE) Log(format string, v ...interface{}) {
	var str string

	if v != nil {
		str = fmt.Sprintf(format, v...)
	} else {
		str = format
	}

	fstr := C.CString(str)
	defer C.free(unsafe.Pointer(fstr))

	C.goHGE_System_Log(h.HGE, fstr)
}

// Launches an URL or external executable/data file.
func (h *HGE) Launch(url string) bool {
	urlstr := C.CString(url)
	defer C.free(unsafe.Pointer(urlstr))

	return C.HGE_System_Launch(h.HGE, urlstr) == 1
}

//  Saves current screen snapshot into a file.
func (h *HGE) Snapshot(a ...interface{}) {
	if len(a) == 1 {
		if filename, ok := a[0].(string); ok {
			fname := C.CString(filename)
			defer C.free(unsafe.Pointer(fname))

			C.HGE_System_Snapshot(h.HGE, fname)
			return
		}
	}

	C.HGE_System_Snapshot(h.HGE, nil)
}

// Sets internal system states.
// First param should be one of: BoolState, IntState, StringState, FuncState, HwndState
// Second parameter must be of the matching type, bool, int, string, StateFunc/func (h *HGE)() int, *Hwnd
func (h *HGE) SetState(a ...interface{}) {
	if len(a) == 2 {
		switch a[0].(type) {
		case BoolState:
			if bs, ok := a[1].(bool); ok {
				h.setStateBool(a[0].(BoolState), bs)
				return
			}

		case IntState:
			if is, ok := a[1].(int); ok {
				h.setStateInt(a[0].(IntState), is)
				return
			}

		case StringState:
			if ss, ok := a[1].(string); ok {
				h.setStateString(a[0].(StringState), ss)
				return
			}

		case FuncState:
			switch a[1].(type) {
			case StateFunc:
				h.setStateFunc(a[0].(FuncState), a[1].(StateFunc))
				return
			case func() int:
				h.setStateFunc(a[0].(FuncState), a[1].(func() int))
				return
			}
		case func() bool:
			h.setStateFunc(a[0].(FuncState), func() int {
				if a[1].(func() bool)() {
					return 1
				}

				return 0
			})
			return
		default:
			h.setStateFunc(a[0].(FuncState), nil)
			return

		case HwndState:
			if hs, ok := a[1].(*Hwnd); ok {
				h.setStateHwnd(a[0].(HwndState), hs)
				return
			}
		}
	}
}

func (h *HGE) setStateBool(state BoolState, value bool) {
	C.HGE_System_SetStateBool(h.HGE, C.HGE_BoolState_t(state), BoolToCInt(value))
}

func (h *HGE) setStateFunc(state FuncState, value StateFunc) {
	funcCBs[state] = value
	switch state {
	case FRAMEFUNC:
		C.setFrameFunc(h.HGE, C.HGE_FuncState_t(state))
	case RENDERFUNC:
		C.setRenderFunc(h.HGE, C.HGE_FuncState_t(state))
	case FOCUSLOSTFUNC:
		C.setFocusLostFunc(h.HGE, C.HGE_FuncState_t(state))
	case FOCUSGAINFUNC:
		C.setFocusGainFunc(h.HGE, C.HGE_FuncState_t(state))
	case GFXRESTOREFUNC:
		C.setGfxRestoreFunc(h.HGE, C.HGE_FuncState_t(state))
	case EXITFUNC:
		C.setExitFunc(h.HGE, C.HGE_FuncState_t(state))
	}
}

func (h *HGE) setStateHwnd(state HwndState, value *Hwnd) {
	C.HGE_System_SetStateHwnd(h.HGE, C.HGE_HwndState_t(state), value.hwnd)
}

func (h *HGE) setStateInt(state IntState, value int) {
	C.HGE_System_SetStateInt(h.HGE, C.HGE_IntState_t(state), C.int(value))
}

func (h *HGE) setStateString(state StringState, value string) {
	val := C.CString(value)
	defer C.free(unsafe.Pointer(val))

	C.HGE_System_SetStateString(h.HGE, C.HGE_StringState_t(state), val)
}

// Returns internal system state values.
func (h *HGE) GetState(a ...interface{}) interface{} {
	if len(a) == 1 {
		switch a[0].(type) {
		case BoolState:
			return h.getStateBool(a[0].(BoolState))

		case IntState:
			return h.getStateInt(a[0].(IntState))

		case StringState:
			return h.getStateString(a[0].(StringState))

		case FuncState:
			return h.getStateFunc(a[0].(FuncState))

		case HwndState:
			return h.getStateHwnd(a[0].(HwndState))
		}
	}

	return nil
}

func (h *HGE) getStateBool(state BoolState) bool {
	return C.HGE_System_GetStateBool(h.HGE, C.HGE_BoolState_t(state)) == 1
}

func (h *HGE) getStateFunc(state FuncState) StateFunc {
	// I don't know how to convert the HGE_Callback C func (h *HGE)tion type to a Go
	// func (h *HGE)tion, so we just pass back the Go func (h *HGE)tion
	return funcCBs[state]
}

func (h *HGE) getStateHwnd(state HwndState) Hwnd {
	var hwnd Hwnd
	hwnd.hwnd = C.HGE_System_GetStateHwnd(h.HGE, C.HGE_HwndState_t(state))
	return hwnd
}

func (h *HGE) getStateInt(state IntState) int {
	return int(C.HGE_System_GetStateInt(h.HGE, C.HGE_IntState_t(state)))
}

func (h *HGE) getStateString(state StringState) string {
	return C.GoString(C.HGE_System_GetStateString(h.HGE, C.HGE_StringState_t(state)))
}
<|endoftext|>


<commit_before>// Package goublu History provides command line history in the goublu Ublu input area.
package goublu

// History keeps history lines from the goublu input line and output from the console out.
type History struct {
	commandLines   []string
	commandPointer int
	AllOut         string
}

// NewHistory is Ctor/0.
func NewHistory() *History {
	h := &History{}
	h.commandLines = make([]string, 0, 0)
	h.commandPointer = -1
	return h
}

// Append postpends a history line to the collection.
func (h *History) Append(l string) {
	h.commandLines = append(h.commandLines, l)
	h.commandPointer = len(h.commandLines) - 1
}

// Back returns the next backwards history line, decrementing the pointer for
// next history line request. The pointer is floored and does not wrap.
func (h *History) Back() string {
	var result string
	if h.commandPointer > -1 {
		result = h.commandLines[h.commandPointer]
		h.commandPointer = Max(h.commandPointer-1, -1)
	}
	return result
}

// BackWrap returns the next backwards history line, decrementing the pointer for
// next history line request. The pointer wraps.
func (h *History) BackWrap() string {
	var result string
	if len(h.commandLines) > 0 {
		if h.commandPointer > -1 {
			result = h.commandLines[h.commandPointer]
			h.commandPointer = (h.commandPointer - 1) % len(h.commandLines)
		} else {
			h.commandPointer = len(h.commandLines) - 1 // so we can't over-decrement
			if h.commandPointer > 0 {
				result = h.commandLines[h.commandPointer]
			}
			h.commandPointer = (h.commandPointer - 1) % len(h.commandLines)
		}
	}
	return result
}

// Forward returns the next forwards history line, incrementing the pointer for
// next history line request. The pointer is roofed and does not wrap.
func (h *History) Forward() string {
	var result string
	h.commandPointer = Max(-1, Min(h.commandPointer+1, len(h.commandLines)-1))
	if h.commandPointer > -1 {
		result = h.commandLines[h.commandPointer]
	}
	return result
}

// First returns the first history line if any, resetting the pointer if any.
func (h *History) First() string {
	var result string
	if len(h.commandLines) > 0 {
		h.commandPointer = 0
		result = h.commandLines[h.commandPointer]
	}
	return result
}

// Last returns the last history line if any, resetting the pointer if any.
func (h *History) Last() string {
	var result string
	if len(h.commandLines) > 0 {
		h.commandPointer = len(h.commandLines) - 1
		result = h.commandLines[h.commandPointer]
	}
	return result
}

// AppendAllOut adds more output text to the complete console out text.
func (h *History) AppendAllOut(text string) {
	h.AllOut = h.AllOut + text
}

// Max returns the max of two ints.
func Max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

// Min returns the min of two ints.
func Min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
<commit_msg>better command line recall<commit_after>// Package goublu History provides command line history in the goublu Ublu input area.
package goublu

// History keeps history lines from the goublu input line and output from the console out.
type History struct {
	commandLines   []string
	commandPointer int
	AllOut         string
}

// NewHistory is Ctor/0.
func NewHistory() *History {
	h := &History{}
	h.commandLines = make([]string, 0, 0)
	h.commandPointer = -1
	return h
}

// Append postpends a history line to the collection.
func (h *History) Append(l string) {
	h.commandLines = append(h.commandLines, l)
	h.commandPointer = len(h.commandLines) - 1
}

// Back returns the next backwards history line, decrementing the pointer for
// next history line request. The pointer is floored and does not wrap.
func (h *History) Back() string {
	var result string
	h.commandPointer = Min(len(h.commandLines)-1, Max(-1, h.commandPointer))
	if h.commandPointer >= 0 {
		result = h.commandLines[h.commandPointer]
		h.commandPointer = h.commandPointer - 1
	}
	return result
}

// BackWrap returns the next backwards history line, decrementing the pointer for
// next history line request. The pointer wraps.
func (h *History) BackWrap() string {
	var result string
	if len(h.commandLines) > 0 {
		if h.commandPointer > -1 {
			result = h.commandLines[h.commandPointer]
			h.commandPointer = (h.commandPointer - 1) % len(h.commandLines)
		} else {
			h.commandPointer = len(h.commandLines) - 1 // so we can't over-decrement
			if h.commandPointer > 0 {
				result = h.commandLines[h.commandPointer]
			}
			h.commandPointer = (h.commandPointer - 1) % len(h.commandLines)
		}
	}
	return result
}

// Forward returns the next forwards history line, incrementing the pointer for
// next history line request. The pointer is roofed and does not wrap.
func (h *History) Forward() string {
	var result string
	h.commandPointer = h.commandPointer + 1
	if h.commandPointer >= len(h.commandLines) {
		h.commandPointer = len(h.commandLines) - 1
	} else if h.commandPointer > -1 {
		result = h.commandLines[h.commandPointer]
	} else {
		h.commandPointer = -1
	}
	return result
}

// First returns the first history line if any, resetting the pointer if any.
func (h *History) First() string {
	var result string
	if len(h.commandLines) > 0 {
		h.commandPointer = 0
		result = h.commandLines[h.commandPointer]
	}
	return result
}

// Last returns the last history line if any, resetting the pointer if any.
func (h *History) Last() string {
	var result string
	if len(h.commandLines) > 0 {
		h.commandPointer = len(h.commandLines) - 1
		result = h.commandLines[h.commandPointer]
	}
	return result
}

// AppendAllOut adds more output text to the complete console out text.
func (h *History) AppendAllOut(text string) {
	h.AllOut = h.AllOut + text
}

// Max returns the max of two ints.
func Max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

// Min returns the min of two ints.
func Min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
<|endoftext|>


<commit_before>package hostess

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"sort"
	"strings"
)

const default_osx = `
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost
`

const default_linux = `
127.0.0.1   localhost
127.0.1.1   HOSTNAME

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
`

func TrimWS(s string) string {
	return strings.Trim(s, " \n\t")
}

func parseLine(line string) []Hostname {
	hostnames := make([]Hostname, 0)

	// Parse leading comment for disabled lines
	enabled := true
	if line[0:1] == "#" {
		enabled = false
		line = line[1:]
	}

	// Parse other comment for actual comments
	line = strings.Split(line, "#")[0]

	// Replace tabs and multispaces with single spaces throughout
	line = strings.Replace(line, "\t", " ", -1)
	line = strings.Replace(line, "  ", " ", -1)

	// Break line into words
	words := strings.Split(line, " ")

	// Separate the first bit (the ip) from the other bits (the domains)
	ip := words[0]
	domains := words[1:]

	if LooksLikeIpv4(ip) || LooksLikeIpv6(ip) {
		for _, v := range domains {
			hostnames = append(hostnames, Hostname{v, ip, enabled})
		}
	}

	// 1. Split on # to discard comments.
	//    - What if it's a legit line but commented out?
	//    - Disabled lines should have # at the beginning of the line
	// 2. Split on first space to find the IP
	//    - Pass to LooksLikeIpv4 or LooksLikeIpv6 to check
	//    - If it doesn't look like either of these then it's probably a random
	//      comment.
	// 3. Split remainder of line on whitespace to find
	//    domain names
	//    - Watch out for comments.

	return hostnames
}

var ipv4_pattern = regexp.MustCompile(`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`)

func LooksLikeIpv4(ip string) bool {
	return false
}

var ipv6_pattern = regexp.MustCompile(``)

func LooksLikeIpv6(ip string) bool {
	return false
}

func ContainsHostname(hostnames []Hostname, hostname Hostname) bool {
	for _, v := range hostnames {
		if v.Ip == hostname.Ip && v.Domain == hostname.Domain {
			return true
		}
	}
	return false
}

func ContainsDomain(hostnames []Hostname, domain string) bool {
	for _, v := range hostnames {
		if v.Domain == domain {
			return true
		}
	}
	return false
}

func ContainsIp(hostnames []Hostname, ip string) bool {
	for _, v := range hostnames {
		if v.Ip == ip {
			return true
		}
	}
	return false
}

type Hostname struct {
	Domain  string
	Ip      string
	Enabled bool
	// Ipv6    bool
}

func (h *Hostname) Format() string {
	r := fmt.Sprintf("%s %s", h.Ip, h.Domain)
	if !h.Enabled {
		r = "# " + r
	}
	return r
}

// Hostfile represents /etc/hosts (or a similar file, depending on OS), and
// includes a list of Hostnames. Hostfile includes
type Hostfile struct {
	Path  string
	Hosts map[string]*Hostname
	data  string
}

// NewHostFile creates a new Hostfile object from the specified file.
func NewHostfile(path string) *Hostfile {
	return &Hostfile{path, make(map[string]*Hostname), ""}
}

func (h *Hostfile) Load() string {
	data, err := ioutil.ReadFile(h.Path)
	if err != nil {
		fmt.Println("Can't read ", h.Path)
		os.Exit(1)
	}
	h.data = string(data)
	return h.data
}

func (h *Hostfile) Parse() {
	for _, v := range strings.Split(h.data, "\n") {
		fmt.Println(v)
	}
}

func getSortedMapKeys(m map[string][]string) []string {
	keys := make([]string, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i += 1
	}
	sort.Strings(keys)
	return keys
}

// MoveToFront looks for string in a slice of strings and if it finds it, moves
// it to the front of the slice.
// Note: this could probably be made faster using pointers to switch the values
// instead of copying a bunch of crap, but it works and speed is not a problem.
func MoveToFront(list []string, search string) []string {
	for k, v := range list {
		if v == search {
			list = append(list[:k], list[k+1:]...)
		}
	}
	return append([]string{search}, list...)
}

// ListDomainsByIp will look through Hostfile to find domains that match the
// specified Ip and return them in a sorted slice.
func (h *Hostfile) ListDomainsByIp(ip string) []string {
	names := make([]string, 0)
	for _, v := range h.Hosts {
		if v.Ip == ip {
			names = append(names, v.Domain)
		}
	}
	sort.Strings(names)

	// Magic for localhost only, to make sure it's the first domain on its line
	if ip == "127.0.0.1" {
		names = MoveToFront(names, "localhost")
	}

	return names
}

// Format takes the current list of Hostnames in this Hostfile and turns it
// into a string suitable for use as an /etc/hosts file.
// Sorting uses the following logic:
// 1. List is sorted by IP address
// 2. Commented items are left in place
// 3. 127.* appears at the top of the list (so boot resolvers don't break)
// 4. When present, localhost will always appear first in the domain list
func (h *Hostfile) Format() string {
	// localhost := "127.0.0.1 localhost"

	localhosts := make(map[string][]string)
	ips := make(map[string][]string)

	// Map domains and IPs into slices of domains keyd by IP
	// 127.0.0.1 = [localhost, blah, blah2]
	// 2.2.2.3 = [domain1, domain2]
	for _, hostname := range h.Hosts {
		if hostname.Ip[0:4] == "127." {
			localhosts[hostname.Ip] = append(localhosts[hostname.Ip], hostname.Domain)
		} else {
			ips[hostname.Ip] = append(ips[hostname.Ip], hostname.Domain)
		}
	}

	localhosts_keys := getSortedMapKeys(localhosts)
	ips_keys := getSortedMapKeys(ips)
	out := make([]string, 0)

	for _, ip := range localhosts_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, domain := range h.ListDomainsByIp(ip) {
			hostname := *h.Hosts[domain]
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	for _, ip := range ips_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, domain := range h.ListDomainsByIp(ip) {
			hostname := *h.Hosts[domain]
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	return strings.Join(out, "\n")
}

func (h *Hostfile) Save() error {
	// h.Format(h.Path)
	return nil
}

func (h *Hostfile) Add(host Hostname) error {
	host_f, found := h.Hosts[host.Domain]
	if found {
		if host_f.Ip == host.Ip {
			return errors.New(fmt.Sprintf("Duplicate hostname entry for %s -> %s",
				host.Domain, host.Ip))
		} else {
			return errors.New(fmt.Sprintf("Conflicting hostname entries for %s -> %s and -> %s",
				host.Domain, host.Ip, host_f.Ip))
		}
	} else {
		h.Hosts[host.Domain] = &host
	}
	return nil
}

func (h *Hostfile) Delete(domain string) {
	delete(h.Hosts, domain)
}

func (h *Hostfile) Enable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = true
	}
}

func (h *Hostfile) Disable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = false
	}
}

func GetHostsPath() string {
	path := os.Getenv("HOSTESS_FILE")
	if path == "" {
		path = "/etc/hosts"
	}
	return path
}
<commit_msg>Moved parseLine after its dependent functions<commit_after>package hostess

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"sort"
	"strings"
)

const default_osx = `
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost
`

const default_linux = `
127.0.0.1   localhost
127.0.1.1   HOSTNAME

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
`

func TrimWS(s string) string {
	return strings.Trim(s, " \n\t")
}

var ipv4_pattern = regexp.MustCompile(`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`)

func LooksLikeIpv4(ip string) bool {
	return false
}

var ipv6_pattern = regexp.MustCompile(``)

func LooksLikeIpv6(ip string) bool {
	return false
}

func parseLine(line string) []Hostname {
	hostnames := make([]Hostname, 0)

	// Parse leading comment for disabled lines
	enabled := true
	if line[0:1] == "#" {
		enabled = false
		line = line[1:]
	}

	// Parse other comment for actual comments
	line = strings.Split(line, "#")[0]

	// Replace tabs and multispaces with single spaces throughout
	line = strings.Replace(line, "\t", " ", -1)
	line = strings.Replace(line, "  ", " ", -1)

	// Break line into words
	words := strings.Split(line, " ")

	// Separate the first bit (the ip) from the other bits (the domains)
	ip := words[0]
	domains := words[1:]

	if LooksLikeIpv4(ip) || LooksLikeIpv6(ip) {
		for _, v := range domains {
			hostnames = append(hostnames, Hostname{v, ip, enabled})
		}
	}

	// 1. Split on # to discard comments.
	//    - What if it's a legit line but commented out?
	//    - Disabled lines should have # at the beginning of the line
	// 2. Split on first space to find the IP
	//    - Pass to LooksLikeIpv4 or LooksLikeIpv6 to check
	//    - If it doesn't look like either of these then it's probably a random
	//      comment.
	// 3. Split remainder of line on whitespace to find
	//    domain names
	//    - Watch out for comments.

	return hostnames
}

func ContainsHostname(hostnames []Hostname, hostname Hostname) bool {
	for _, v := range hostnames {
		if v.Ip == hostname.Ip && v.Domain == hostname.Domain {
			return true
		}
	}
	return false
}

func ContainsDomain(hostnames []Hostname, domain string) bool {
	for _, v := range hostnames {
		if v.Domain == domain {
			return true
		}
	}
	return false
}

func ContainsIp(hostnames []Hostname, ip string) bool {
	for _, v := range hostnames {
		if v.Ip == ip {
			return true
		}
	}
	return false
}

type Hostname struct {
	Domain  string
	Ip      string
	Enabled bool
	// Ipv6    bool
}

func (h *Hostname) Format() string {
	r := fmt.Sprintf("%s %s", h.Ip, h.Domain)
	if !h.Enabled {
		r = "# " + r
	}
	return r
}

// Hostfile represents /etc/hosts (or a similar file, depending on OS), and
// includes a list of Hostnames. Hostfile includes
type Hostfile struct {
	Path  string
	Hosts map[string]*Hostname
	data  string
}

// NewHostFile creates a new Hostfile object from the specified file.
func NewHostfile(path string) *Hostfile {
	return &Hostfile{path, make(map[string]*Hostname), ""}
}

func (h *Hostfile) Load() string {
	data, err := ioutil.ReadFile(h.Path)
	if err != nil {
		fmt.Println("Can't read ", h.Path)
		os.Exit(1)
	}
	h.data = string(data)
	return h.data
}

func (h *Hostfile) Parse() {
	for _, v := range strings.Split(h.data, "\n") {
		fmt.Println(v)
	}
}

func getSortedMapKeys(m map[string][]string) []string {
	keys := make([]string, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i += 1
	}
	sort.Strings(keys)
	return keys
}

// MoveToFront looks for string in a slice of strings and if it finds it, moves
// it to the front of the slice.
// Note: this could probably be made faster using pointers to switch the values
// instead of copying a bunch of crap, but it works and speed is not a problem.
func MoveToFront(list []string, search string) []string {
	for k, v := range list {
		if v == search {
			list = append(list[:k], list[k+1:]...)
		}
	}
	return append([]string{search}, list...)
}

// ListDomainsByIp will look through Hostfile to find domains that match the
// specified Ip and return them in a sorted slice.
func (h *Hostfile) ListDomainsByIp(ip string) []string {
	names := make([]string, 0)
	for _, v := range h.Hosts {
		if v.Ip == ip {
			names = append(names, v.Domain)
		}
	}
	sort.Strings(names)

	// Magic for localhost only, to make sure it's the first domain on its line
	if ip == "127.0.0.1" {
		names = MoveToFront(names, "localhost")
	}

	return names
}

// Format takes the current list of Hostnames in this Hostfile and turns it
// into a string suitable for use as an /etc/hosts file.
// Sorting uses the following logic:
// 1. List is sorted by IP address
// 2. Commented items are left in place
// 3. 127.* appears at the top of the list (so boot resolvers don't break)
// 4. When present, localhost will always appear first in the domain list
func (h *Hostfile) Format() string {
	// localhost := "127.0.0.1 localhost"

	localhosts := make(map[string][]string)
	ips := make(map[string][]string)

	// Map domains and IPs into slices of domains keyd by IP
	// 127.0.0.1 = [localhost, blah, blah2]
	// 2.2.2.3 = [domain1, domain2]
	for _, hostname := range h.Hosts {
		if hostname.Ip[0:4] == "127." {
			localhosts[hostname.Ip] = append(localhosts[hostname.Ip], hostname.Domain)
		} else {
			ips[hostname.Ip] = append(ips[hostname.Ip], hostname.Domain)
		}
	}

	localhosts_keys := getSortedMapKeys(localhosts)
	ips_keys := getSortedMapKeys(ips)
	out := make([]string, 0)

	for _, ip := range localhosts_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, domain := range h.ListDomainsByIp(ip) {
			hostname := *h.Hosts[domain]
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	for _, ip := range ips_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, domain := range h.ListDomainsByIp(ip) {
			hostname := *h.Hosts[domain]
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	return strings.Join(out, "\n")
}

func (h *Hostfile) Save() error {
	// h.Format(h.Path)
	return nil
}

func (h *Hostfile) Add(host Hostname) error {
	host_f, found := h.Hosts[host.Domain]
	if found {
		if host_f.Ip == host.Ip {
			return errors.New(fmt.Sprintf("Duplicate hostname entry for %s -> %s",
				host.Domain, host.Ip))
		} else {
			return errors.New(fmt.Sprintf("Conflicting hostname entries for %s -> %s and -> %s",
				host.Domain, host.Ip, host_f.Ip))
		}
	} else {
		h.Hosts[host.Domain] = &host
	}
	return nil
}

func (h *Hostfile) Delete(domain string) {
	delete(h.Hosts, domain)
}

func (h *Hostfile) Enable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = true
	}
}

func (h *Hostfile) Disable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = false
	}
}

func GetHostsPath() string {
	path := os.Getenv("HOSTESS_FILE")
	if path == "" {
		path = "/etc/hosts"
	}
	return path
}
<|endoftext|>


<commit_before>package telegram

import (
	"fmt"
	"time"

	"github.com/beevee/konfurbot"
	"github.com/looplab/fsm"
	"github.com/tucnak/telebot"
)

const (
	welcomeCommand       = "greet"
	returnToStartCommand = "return"
	unknownCommand       = "unknown"

	foodCommand = "🌶 Еда"

	talkCommand       = "🔥 Доклады / МК"
	talkNowCommand    = "Которые идут сейчас"
	talkNextCommand   = "Которые начнутся скоро"
	talkAllCommand    = "Все"
	talkLongCommand   = "С тизерами (простыня!)"
	talkShortCommand  = "Без тизеров (ура! краткость!)"
	talkTalkCommand   = "Доклады"
	talkMasterCommand = "Мастер-классы"

	funCommand = "🍾 Развлечения"

	transferCommand = "🚜 Трансфер"

	welcomeState  = "welcome"
	startState    = "start"
	talkState     = "talk"
	talkNowState  = "talknow"
	talkNextState = "talknext"
	talkAllState  = "talkall"
)

var stateMessageOptions = map[string]*telebot.SendOptions{
	startState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{foodCommand, talkCommand},
				[]string{funCommand, transferCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkNowCommand, talkNextCommand, talkAllCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkNowState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkLongCommand, talkShortCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkNextState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkLongCommand, talkShortCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkAllState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkTalkCommand, talkMasterCommand},
			},
			ResizeKeyboard: true,
		},
	},
}

func initStateMachine() *fsm.FSM {
	return fsm.NewFSM(
		welcomeState,

		fsm.Events{
			{Name: welcomeCommand, Src: []string{welcomeState}, Dst: startState},
			{Name: foodCommand, Src: []string{startState}, Dst: startState},
			{Name: talkCommand, Src: []string{startState}, Dst: talkState},
			{Name: talkNowCommand, Src: []string{talkState}, Dst: talkNowState},
			{Name: talkNextCommand, Src: []string{talkState}, Dst: talkNextState},
			{Name: talkLongCommand, Src: []string{talkNowState, talkNextState}, Dst: startState},
			{Name: talkShortCommand, Src: []string{talkNowState, talkNextState}, Dst: startState},
			{Name: talkAllCommand, Src: []string{talkState}, Dst: talkAllState},
			{Name: talkTalkCommand, Src: []string{talkAllState}, Dst: startState},
			{Name: talkMasterCommand, Src: []string{talkAllState}, Dst: startState},
			{Name: returnToStartCommand, Src: []string{startState}, Dst: startState},
			{
				Name: unknownCommand,
				Src:  []string{welcomeState, startState, talkState, talkNowState, talkNextState, talkAllState},
				Dst:  startState,
			},
		},

		fsm.Callbacks{
			welcomeCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Добро пожаловать на КонфУР!", stateMessageOptions[e.Dst])
			}),

			foodCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				events := bot.ScheduleStorage.GetEventsByType("food")
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Окей, какие доклады и мастер-классы?", stateMessageOptions[e.Dst])
			}),

			talkNowCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Их может оказаться довольно много. Тизеры надо?", stateMessageOptions[e.Dst])
			}),

			talkNextCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Их может оказаться довольно много. Тизеры надо?", stateMessageOptions[e.Dst])
			}),

			talkLongCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				var events []konfurbot.Event
				switch e.Src {
				case talkNowState:
					events = bot.ScheduleStorage.GetCurrentEventsByType("talk", time.Now().In(bot.Timezone))
				case talkNextState:
					events = bot.ScheduleStorage.GetNextEventsByType("talk", time.Now().In(bot.Timezone), time.Hour)
				}
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, true), stateMessageOptions[e.Dst])
			}),

			talkShortCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				var events []konfurbot.Event
				switch e.Src {
				case talkNowState:
					events = bot.ScheduleStorage.GetCurrentEventsByType("talk", time.Now().In(bot.Timezone))
				case talkNextState:
					events = bot.ScheduleStorage.GetNextEventsByType("talk", time.Now().In(bot.Timezone), time.Hour)
				}
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkTalkCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				events := bot.ScheduleStorage.GetEventsByTypeAndSubtype("talk", "talk")
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkMasterCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				events := bot.ScheduleStorage.GetEventsByTypeAndSubtype("talk", "master")
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkAllCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Полное расписание довольно длинное. Давай посмотрим отдельно, доклады или мастер-классы? С тизерами вообще не буду предлагать :)", stateMessageOptions[e.Dst])
			}),

			unknownCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Я не понимаю эту команду. Давай попробуем еще раз с начала.", stateMessageOptions[e.Dst])
			}),
		},
	)
}

func wrapCallback(f func(*fsm.Event, telebot.Chat, *Bot) error) func(*fsm.Event) {
	return func(e *fsm.Event) {
		if len(e.Args) < 2 {
			return
		}
		chat := e.Args[0].(telebot.Chat)
		bot := e.Args[1].(*Bot)
		if err := f(e, chat, bot); err != nil {
			bot.Logger.Log("msg", "error sending message", "chatid", chat.ID, "error", err)
		}
	}
}

func makeResponseFromEvents(events []konfurbot.Event, long bool) string {
	var response string
	for _, event := range events {
		response += fmt.Sprintf("%s — %s", event.Start.Format("15:04"), event.Finish.Format("15:04"))
		if event.Venue != "" {
			response += fmt.Sprintf(" [%s]", event.Venue)
		}
		response += fmt.Sprintf(": %s", event.Short)
		if event.Speaker != "" {
			response += fmt.Sprintf(" (%s)", event.Speaker)
		}
		response += "\n"
		if long {
			response += fmt.Sprintf("%s\n\n", event.Long)
		}
	}
	if response == "" {
		response = "Ничего нет :("
	}
	return response
}
<commit_msg>Make buttons wider to fit on mobile devices<commit_after>package telegram

import (
	"fmt"
	"time"

	"github.com/beevee/konfurbot"
	"github.com/looplab/fsm"
	"github.com/tucnak/telebot"
)

const (
	welcomeCommand       = "greet"
	returnToStartCommand = "return"
	unknownCommand       = "unknown"

	foodCommand = "🌶 Еда"

	talkCommand       = "🔥 Доклады / МК"
	talkNowCommand    = "Которые идут сейчас"
	talkNextCommand   = "Которые начнутся скоро"
	talkAllCommand    = "Все"
	talkLongCommand   = "С тизерами (простыня!)"
	talkShortCommand  = "Без тизеров (ура! краткость!)"
	talkTalkCommand   = "Доклады"
	talkMasterCommand = "Мастер-классы"

	funCommand = "🍾 Развлечения"

	transferCommand = "🚜 Трансфер"

	welcomeState  = "welcome"
	startState    = "start"
	talkState     = "talk"
	talkNowState  = "talknow"
	talkNextState = "talknext"
	talkAllState  = "talkall"
)

var stateMessageOptions = map[string]*telebot.SendOptions{
	startState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{foodCommand, talkCommand},
				[]string{funCommand, transferCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkNowCommand},
				[]string{talkNextCommand},
				[]string{talkAllCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkNowState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkLongCommand},
				[]string{talkShortCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkNextState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkLongCommand},
				[]string{talkShortCommand},
			},
			ResizeKeyboard: true,
		},
	},

	talkAllState: &telebot.SendOptions{
		ReplyMarkup: telebot.ReplyMarkup{
			CustomKeyboard: [][]string{
				[]string{talkTalkCommand},
				[]string{talkMasterCommand},
			},
			ResizeKeyboard: true,
		},
	},
}

func initStateMachine() *fsm.FSM {
	return fsm.NewFSM(
		welcomeState,

		fsm.Events{
			{Name: welcomeCommand, Src: []string{welcomeState}, Dst: startState},
			{Name: foodCommand, Src: []string{startState}, Dst: startState},
			{Name: talkCommand, Src: []string{startState}, Dst: talkState},
			{Name: talkNowCommand, Src: []string{talkState}, Dst: talkNowState},
			{Name: talkNextCommand, Src: []string{talkState}, Dst: talkNextState},
			{Name: talkLongCommand, Src: []string{talkNowState, talkNextState}, Dst: startState},
			{Name: talkShortCommand, Src: []string{talkNowState, talkNextState}, Dst: startState},
			{Name: talkAllCommand, Src: []string{talkState}, Dst: talkAllState},
			{Name: talkTalkCommand, Src: []string{talkAllState}, Dst: startState},
			{Name: talkMasterCommand, Src: []string{talkAllState}, Dst: startState},
			{Name: returnToStartCommand, Src: []string{startState}, Dst: startState},
			{
				Name: unknownCommand,
				Src:  []string{welcomeState, startState, talkState, talkNowState, talkNextState, talkAllState},
				Dst:  startState,
			},
		},

		fsm.Callbacks{
			welcomeCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Добро пожаловать на КонфУР!", stateMessageOptions[e.Dst])
			}),

			foodCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				events := bot.ScheduleStorage.GetEventsByType("food")
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Окей, какие доклады и мастер-классы?", stateMessageOptions[e.Dst])
			}),

			talkNowCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Их может оказаться довольно много. Тизеры надо?", stateMessageOptions[e.Dst])
			}),

			talkNextCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Их может оказаться довольно много. Тизеры надо?", stateMessageOptions[e.Dst])
			}),

			talkLongCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				var events []konfurbot.Event
				switch e.Src {
				case talkNowState:
					events = bot.ScheduleStorage.GetCurrentEventsByType("talk", time.Now().In(bot.Timezone))
				case talkNextState:
					events = bot.ScheduleStorage.GetNextEventsByType("talk", time.Now().In(bot.Timezone), time.Hour)
				}
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, true), stateMessageOptions[e.Dst])
			}),

			talkShortCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				var events []konfurbot.Event
				switch e.Src {
				case talkNowState:
					events = bot.ScheduleStorage.GetCurrentEventsByType("talk", time.Now().In(bot.Timezone))
				case talkNextState:
					events = bot.ScheduleStorage.GetNextEventsByType("talk", time.Now().In(bot.Timezone), time.Hour)
				}
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkTalkCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				events := bot.ScheduleStorage.GetEventsByTypeAndSubtype("talk", "talk")
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkMasterCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				events := bot.ScheduleStorage.GetEventsByTypeAndSubtype("talk", "master")
				return bot.telebot.SendMessage(chat, makeResponseFromEvents(events, false), stateMessageOptions[e.Dst])
			}),

			talkAllCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Полное расписание довольно длинное. Давай посмотрим отдельно, доклады или мастер-классы? С тизерами вообще не буду предлагать :)", stateMessageOptions[e.Dst])
			}),

			unknownCommand: wrapCallback(func(e *fsm.Event, chat telebot.Chat, bot *Bot) error {
				return bot.telebot.SendMessage(chat, "Я не понимаю эту команду. Давай попробуем еще раз с начала.", stateMessageOptions[e.Dst])
			}),
		},
	)
}

func wrapCallback(f func(*fsm.Event, telebot.Chat, *Bot) error) func(*fsm.Event) {
	return func(e *fsm.Event) {
		if len(e.Args) < 2 {
			return
		}
		chat := e.Args[0].(telebot.Chat)
		bot := e.Args[1].(*Bot)
		if err := f(e, chat, bot); err != nil {
			bot.Logger.Log("msg", "error sending message", "chatid", chat.ID, "error", err)
		}
	}
}

func makeResponseFromEvents(events []konfurbot.Event, long bool) string {
	var response string
	for _, event := range events {
		response += fmt.Sprintf("%s — %s", event.Start.Format("15:04"), event.Finish.Format("15:04"))
		if event.Venue != "" {
			response += fmt.Sprintf(" [%s]", event.Venue)
		}
		response += fmt.Sprintf(": %s", event.Short)
		if event.Speaker != "" {
			response += fmt.Sprintf(" (%s)", event.Speaker)
		}
		response += "\n"
		if long {
			response += fmt.Sprintf("%s\n\n", event.Long)
		}
	}
	if response == "" {
		response = "Ничего нет :("
	}
	return response
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util/wait"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func extinguish(f *Framework, totalNS int, maxAllowedAfterDel int, maxSeconds int) {
	var err error

	By("Creating testing namespaces")
	wg := &sync.WaitGroup{}
	wg.Add(totalNS)
	for n := 0; n < totalNS; n += 1 {
		go func(n int) {
			defer wg.Done()
			defer GinkgoRecover()
			_, err = f.CreateNamespace(fmt.Sprintf("nslifetest-%v", n), nil)
			Expect(err).NotTo(HaveOccurred())
		}(n)
	}
	wg.Wait()

	//Wait 10 seconds, then SEND delete requests for all the namespaces.
	By("Waiting 10 seconds")
	time.Sleep(time.Duration(10 * time.Second))
	deleted, err := deleteNamespaces(f.Client, []string{"nslifetest"}, nil /* skipFilter */)
	Expect(err).NotTo(HaveOccurred())
	Expect(len(deleted)).To(Equal(totalNS))

	By("Waiting for namespaces to vanish")
	//Now POLL until all namespaces have been eradicated.
	expectNoError(wait.Poll(2*time.Second, time.Duration(maxSeconds)*time.Second,
		func() (bool, error) {
			var cnt = 0
			nsList, err := f.Client.Namespaces().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			for _, item := range nsList.Items {
				if strings.Contains(item.Name, "nslifetest") {
					cnt++
				}
			}
			if cnt > maxAllowedAfterDel {
				Logf("Remaining namespaces : %v", cnt)
				return false, nil
			}
			return true, nil
		}))
}

// This test must run [Serial] due to the impact of running other parallel
// tests can have on its performance.  Each test that follows the common
// test framework follows this pattern:
//   1. Create a Namespace
//   2. Do work that generates content in that namespace
//   3. Delete a Namespace
// Creation of a Namespace is non-trivial since it requires waiting for a
// ServiceAccount to be generated.
// Deletion of a Namespace is non-trivial and performance intensive since
// its an orchestrated process.  The controller that handles deletion must
// query the namespace for all existing content, and then delete each piece
// of content in turn.  As the API surface grows to add more KIND objects
// that could exist in a Namespace, the number of calls that the namespace
// controller must orchestrate grows since it must LIST, DELETE (1x1) each
// KIND.
// There is work underway to improve this, but it's
// most likely not going to get significantly better until etcd v3.
// Going back to this test, this test generates 100 Namespace objects, and then
// rapidly deletes all of them.  This causes the NamespaceController to observe
// and attempt to process a large number of deletes concurrently.  In effect,
// it's like running 100 traditional e2e tests in parallel.  If the namespace
// controller orchestrating deletes is slowed down deleting another test's
// content then this test may fail.  Since the goal of this test is to soak
// Namespace creation, and soak Namespace deletion, its not appropriate to
// further soak the cluster with other parallel Namespace deletion activities
// that each have a variable amount of content in the associated Namespace.
// When run in [Serial] this test appears to delete Namespace objects at a
// rate of approximately 1 per second.
var _ = Describe("Namespaces [Serial]", func() {

	f := NewDefaultFramework("namespaces")

	It("should delete fast enough (90 percent of 100 namespaces in 150 seconds)",
		func() { extinguish(f, 100, 10, 150) })

	// On hold until etcd3; see #7372
	It("should always delete fast (ALL of 100 namespaces in 150 seconds) [Feature:ComprehensiveNamespaceDraining]",
		func() { extinguish(f, 100, 0, 150) })
})
<commit_msg>Add e2e tests to verify more resources are deleted<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func extinguish(f *Framework, totalNS int, maxAllowedAfterDel int, maxSeconds int) {
	var err error

	By("Creating testing namespaces")
	wg := &sync.WaitGroup{}
	wg.Add(totalNS)
	for n := 0; n < totalNS; n += 1 {
		go func(n int) {
			defer wg.Done()
			defer GinkgoRecover()
			_, err = f.CreateNamespace(fmt.Sprintf("nslifetest-%v", n), nil)
			Expect(err).NotTo(HaveOccurred())
		}(n)
	}
	wg.Wait()

	//Wait 10 seconds, then SEND delete requests for all the namespaces.
	By("Waiting 10 seconds")
	time.Sleep(time.Duration(10 * time.Second))
	deleted, err := deleteNamespaces(f.Client, []string{"nslifetest"}, nil /* skipFilter */)
	Expect(err).NotTo(HaveOccurred())
	Expect(len(deleted)).To(Equal(totalNS))

	By("Waiting for namespaces to vanish")
	//Now POLL until all namespaces have been eradicated.
	expectNoError(wait.Poll(2*time.Second, time.Duration(maxSeconds)*time.Second,
		func() (bool, error) {
			var cnt = 0
			nsList, err := f.Client.Namespaces().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			for _, item := range nsList.Items {
				if strings.Contains(item.Name, "nslifetest") {
					cnt++
				}
			}
			if cnt > maxAllowedAfterDel {
				Logf("Remaining namespaces : %v", cnt)
				return false, nil
			}
			return true, nil
		}))
}

func ensurePodsAreRemovedWhenNamespaceIsDeleted(f *Framework) {
	var err error

	By("Creating a test namespace")
	namespace, err := f.CreateNamespace("nsdeletetest", nil)
	Expect(err).NotTo(HaveOccurred())

	By("Waiting for a default service account to be provisioned in namespace")
	err = waitForDefaultServiceAccountInNamespace(f.Client, namespace.Name)
	Expect(err).NotTo(HaveOccurred())

	By("Creating a pod in the namespace")
	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name: "test-pod",
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:  "nginx",
					Image: "gcr.io/google_containers/pause:2.0",
				},
			},
		},
	}
	pod, err = f.Client.Pods(namespace.Name).Create(pod)
	Expect(err).NotTo(HaveOccurred())

	By("Waiting for the pod to have running status")
	expectNoError(waitForPodRunningInNamespace(f.Client, pod.Name, pod.Namespace))

	By("Deleting the namespace")
	err = f.Client.Namespaces().Delete(namespace.Name)
	Expect(err).NotTo(HaveOccurred())

	By("Waiting for the namespace to be removed.")
	maxWaitSeconds := int64(60) + *pod.Spec.TerminationGracePeriodSeconds
	expectNoError(wait.Poll(1*time.Second, time.Duration(maxWaitSeconds)*time.Second,
		func() (bool, error) {
			_, err = f.Client.Namespaces().Get(namespace.Name)
			if err != nil && errors.IsNotFound(err) {
				return true, nil
			}
			return false, nil
		}))

	By("Verifying there is no pod in the namespace")
	_, err = f.Client.Pods(namespace.Name).Get(pod.Name)
	Expect(err).To(HaveOccurred())
}

func ensureServicesAreRemovedWhenNamespaceIsDeleted(f *Framework) {
	var err error

	By("Creating a test namespace")
	namespace, err := f.CreateNamespace("nsdeletetest", nil)
	Expect(err).NotTo(HaveOccurred())

	By("Waiting for a default service account to be provisioned in namespace")
	err = waitForDefaultServiceAccountInNamespace(f.Client, namespace.Name)
	Expect(err).NotTo(HaveOccurred())

	By("Creating a service in the namespace")
	serviceName := "test-service"
	labels := map[string]string{
		"foo": "bar",
		"baz": "blah",
	}
	service := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: serviceName,
		},
		Spec: api.ServiceSpec{
			Selector: labels,
			Ports: []api.ServicePort{{
				Port:       80,
				TargetPort: intstr.FromInt(80),
			}},
		},
	}
	service, err = f.Client.Services(namespace.Name).Create(service)
	Expect(err).NotTo(HaveOccurred())

	By("Deleting the namespace")
	err = f.Client.Namespaces().Delete(namespace.Name)
	Expect(err).NotTo(HaveOccurred())

	By("Waiting for the namespace to be removed.")
	maxWaitSeconds := int64(60)
	expectNoError(wait.Poll(1*time.Second, time.Duration(maxWaitSeconds)*time.Second,
		func() (bool, error) {
			_, err = f.Client.Namespaces().Get(namespace.Name)
			if err != nil && errors.IsNotFound(err) {
				return true, nil
			}
			return false, nil
		}))

	By("Verifying there is no service in the namespace")
	_, err = f.Client.Services(namespace.Name).Get(service.Name)
	Expect(err).To(HaveOccurred())
}

// This test must run [Serial] due to the impact of running other parallel
// tests can have on its performance.  Each test that follows the common
// test framework follows this pattern:
//   1. Create a Namespace
//   2. Do work that generates content in that namespace
//   3. Delete a Namespace
// Creation of a Namespace is non-trivial since it requires waiting for a
// ServiceAccount to be generated.
// Deletion of a Namespace is non-trivial and performance intensive since
// its an orchestrated process.  The controller that handles deletion must
// query the namespace for all existing content, and then delete each piece
// of content in turn.  As the API surface grows to add more KIND objects
// that could exist in a Namespace, the number of calls that the namespace
// controller must orchestrate grows since it must LIST, DELETE (1x1) each
// KIND.
// There is work underway to improve this, but it's
// most likely not going to get significantly better until etcd v3.
// Going back to this test, this test generates 100 Namespace objects, and then
// rapidly deletes all of them.  This causes the NamespaceController to observe
// and attempt to process a large number of deletes concurrently.  In effect,
// it's like running 100 traditional e2e tests in parallel.  If the namespace
// controller orchestrating deletes is slowed down deleting another test's
// content then this test may fail.  Since the goal of this test is to soak
// Namespace creation, and soak Namespace deletion, its not appropriate to
// further soak the cluster with other parallel Namespace deletion activities
// that each have a variable amount of content in the associated Namespace.
// When run in [Serial] this test appears to delete Namespace objects at a
// rate of approximately 1 per second.
var _ = Describe("Namespaces [Serial]", func() {

	f := NewDefaultFramework("namespaces")

	It("should ensure that all pods are removed when a namespace is deleted.",
		func() { ensurePodsAreRemovedWhenNamespaceIsDeleted(f) })

	It("should ensure that all services are removed when a namespace is deleted.",
		func() { ensureServicesAreRemovedWhenNamespaceIsDeleted(f) })

	It("should delete fast enough (90 percent of 100 namespaces in 150 seconds)",
		func() { extinguish(f, 100, 10, 150) })

	// On hold until etcd3; see #7372
	It("should always delete fast (ALL of 100 namespaces in 150 seconds) [Feature:ComprehensiveNamespaceDraining]",
		func() { extinguish(f, 100, 0, 150) })

})
<|endoftext|>


<commit_before>package test

/*
 * ZLint Copyright 2020 Regents of the University of Michigan
 *
 * 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.
 */

// Contains resources necessary to the Unit Test Cases

import (
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/zmap/zcrypto/x509"
	"github.com/zmap/zlint/lint"
)

// TestLint executes the given lintName against a certificate read from
// a testcert data file with the given filename. Filenames should be relative to
// `testdata/` and not absolute file paths.
//
// Important: TestLint is only appropriate for unit tests. It will panic if the
// lintName is not known or if the testCertFilename can not be loaded.
func TestLint(lintName string, testCertFilename string) *lint.LintResult {
	return TestLintCert(lintName, ReadTestCert(testCertFilename))
}

func TestLintCert(lintName string, cert *x509.Certificate) *lint.LintResult {
	// NOTE(@cpu): Once the `lint.Lints` is not exported this will have to be
	// changed, likely to use a function like `lint.LintByName`. For now use the
	// exported map directly to consolidate access to this one function instead of
	// many individual lint unit tests.
	l := lint.Lints[lintName]
	if l == nil {
		panic(fmt.Sprintf(
			"Lint name %q does not exist in lint.Lints. "+
				"Did you forget to RegisterLint?\n",
			lintName))
	}

	res := l.Execute(cert)
	// We never expect a lint to return a nil LintResult
	if res == nil {
		panic(fmt.Sprintf(
			"Running lint %q on test certificate generated a nil LintResult.\n",
			lintName))
	}
	return res
}

// ReadTestCert loads a x509.Certificate from the given inPath which is assumed
// to be relative to `testdata/`.
//
// Important: ReadTestCert is only appropriate for unit tests. It will panic if
// the inPath file can not be loaded.
func ReadTestCert(inPath string) *x509.Certificate {
	fullPath := fmt.Sprintf("../../testdata/%s", inPath)

	data, err := ioutil.ReadFile(fullPath)
	if err != nil {
		panic(fmt.Sprintf(
			"Unable to read test certificate from %q - %q "+
				"Does a unit test have an incorrect test file name?\n",
			fullPath, err))
	}

	if strings.Contains(string(data), "-BEGIN CERTIFICATE-") {
		block, _ := pem.Decode(data)
		if block == nil {
			panic(fmt.Sprintf(
				"Failed to PEM decode test certificate from %q - "+
					"Does a unit test have a buggy test cert file?\n",
				fullPath))
		}
		data = block.Bytes
	}

	theCert, err := x509.ParseCertificate(data)
	if err != nil {
		panic(fmt.Sprintf(
			"Failed to parse x509 test certificate from %q - "+
				"Does a unit test have a buggy test cert file?\n",
			fullPath))
	}

	return theCert
}
<commit_msg>test: more comments in helpers.go (#366)<commit_after>package test

/*
 * ZLint Copyright 2020 Regents of the University of Michigan
 *
 * 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.
 */

// Contains resources necessary to the Unit Test Cases

import (
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/zmap/zcrypto/x509"
	"github.com/zmap/zlint/lint"
)

// TestLint executes the given lintName against a certificate read from
// a testcert data file with the given filename. Filenames should be relative to
// `testdata/` and not absolute file paths.
//
// Important: TestLint is only appropriate for unit tests. It will panic if the
// lintName is not known or if the testCertFilename can not be loaded, or if the
// lint result is nil.
func TestLint(lintName string, testCertFilename string) *lint.LintResult {
	return TestLintCert(lintName, ReadTestCert(testCertFilename))
}

// TestLintCert executes a lint with the given name against an already parsed
// certificate. This is useful when a unit test reads a certificate from disk
// and then mutates it in some way before trying to lint it.
//
// Important: TestLintCert is only appropriate for unit tests. It will panic if
// the lintName is not known or if the lint result is nil.
func TestLintCert(lintName string, cert *x509.Certificate) *lint.LintResult {
	// NOTE(@cpu): Once `lint.Lints` is not exported this will have to be
	// changed, likely to use a function like `lint.LintByName`. For now use the
	// exported map directly to consolidate access to this one function instead of
	// many individual lint unit tests.
	l := lint.Lints[lintName]
	if l == nil {
		panic(fmt.Sprintf(
			"Lint name %q does not exist in lint.Lints. "+
				"Did you forget to RegisterLint?\n",
			lintName))
	}

	res := l.Execute(cert)
	// We never expect a lint to return a nil LintResult
	if res == nil {
		panic(fmt.Sprintf(
			"Running lint %q on test certificate generated a nil LintResult.\n",
			lintName))
	}
	return res
}

// ReadTestCert loads a x509.Certificate from the given inPath which is assumed
// to be relative to `testdata/`.
//
// Important: ReadTestCert is only appropriate for unit tests. It will panic if
// the inPath file can not be loaded.
func ReadTestCert(inPath string) *x509.Certificate {
	fullPath := fmt.Sprintf("../../testdata/%s", inPath)

	data, err := ioutil.ReadFile(fullPath)
	if err != nil {
		panic(fmt.Sprintf(
			"Unable to read test certificate from %q - %q "+
				"Does a unit test have an incorrect test file name?\n",
			fullPath, err))
	}

	if strings.Contains(string(data), "-BEGIN CERTIFICATE-") {
		block, _ := pem.Decode(data)
		if block == nil {
			panic(fmt.Sprintf(
				"Failed to PEM decode test certificate from %q - "+
					"Does a unit test have a buggy test cert file?\n",
				fullPath))
		}
		data = block.Bytes
	}

	theCert, err := x509.ParseCertificate(data)
	if err != nil {
		panic(fmt.Sprintf(
			"Failed to parse x509 test certificate from %q - "+
				"Does a unit test have a buggy test cert file?\n",
			fullPath))
	}

	return theCert
}
<|endoftext|>