<commit_before>// Go implementation of http://www.hashids.org under MIT license
// Generates hashes from an array of integers, eg. for YouTube like hashes
// Setup: go get github.com/speps/go-hashids
// Original implementations by Ivan Akimov at https://github.com/ivanakimov
// Thanks to Rémy Oudompheng and Peter Hellberg for code review and fixes

package hashids

import (
	"fmt"
	"errors"
	"math"
)

const (
	// Version is the version number of the library
	Version string = "1.0.0"

	// DefaultAlphabet is the default alphabet used by go-hashids
	DefaultAlphabet string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"

	minAlphabetLength int     = 16
	sepDiv            float64 = 3.5
	guardDiv          float64 = 12.0
)

var sepsOriginal = []rune("cfhistuCFHISTU")

// HashID contains everything needed to encode/decode hashids
type HashID struct {
	alphabet  []rune
	minLength int
	salt      []rune
	seps      []rune
	guards    []rune
}

// HashIDData contains the information needed to generate hashids
type HashIDData struct {
	// Alphabet is the alphabet used to generate new ids
	Alphabet string

	// MinLength is the minimum length of a generated id
	MinLength int

	// Salt is the secret used to make the generated id harder to guess
	Salt string
}

// NewData creates a new HashIDData with the DefaultAlphabet already set.
func NewData() *HashIDData {
	return &HashIDData{Alphabet: DefaultAlphabet}
}

// New creates a new HashID
func New() *HashID {
	return NewWithData(NewData())
}

// NewWithData creates a new HashID with the provided HashIDData
func NewWithData(data *HashIDData) *HashID {
	if len(data.Alphabet) < minAlphabetLength {
		panic(fmt.Errorf("alphabet must contain at least %d characters", minAlphabetLength))
	}
	// Check if all characters are unique in Alphabet
	uniqueCheck := make(map[rune]bool, len(data.Alphabet))
	for _, a := range data.Alphabet {
		if _, found := uniqueCheck[a]; found {
			panic(errors.New("duplicate character in alphabet"))
		}
		uniqueCheck[a] = true
	}

	alphabet := []rune(data.Alphabet)
	salt := []rune(data.Salt)

	seps := make([]rune, len(sepsOriginal))
	copy(seps, sepsOriginal)

	// seps should contain only characters present in alphabet; alphabet should not contains seps
	for i := 0; i < len(seps); i++ {
		foundIndex := -1
		for j, a := range alphabet {
			if a == seps[i] {
				foundIndex = j
				break
			}
		}
		if foundIndex == -1 {
			seps = append(seps[:i], seps[i+1:]...)
			i--
		} else {
			alphabet = append(alphabet[:foundIndex], alphabet[foundIndex+1:]...)
		}
	}
	seps = consistentShuffle(seps, salt)

	if len(seps) == 0 || float64(len(alphabet))/float64(len(seps)) > sepDiv {
		sepsLength := int(math.Ceil(float64(len(alphabet)) / sepDiv))
		if sepsLength == 1 {
			sepsLength++
		}
		if sepsLength > len(seps) {
			diff := sepsLength - len(seps)
			seps = append(seps, alphabet[:diff]...)
			alphabet = alphabet[diff:]
		} else {
			seps = seps[:sepsLength]
		}
	}
	alphabet = consistentShuffle(alphabet, salt)

	guardCount := int(math.Ceil(float64(len(alphabet)) / guardDiv))
	var guards []rune
	if len(alphabet) < 3 {
		guards = seps[:guardCount]
		seps = seps[guardCount:]
	} else {
		guards = alphabet[:guardCount]
		alphabet = alphabet[guardCount:]
	}

	return &HashID{
		alphabet:  alphabet,
		minLength: data.MinLength,
		salt:      salt,
		seps:      seps,
		guards:    guards,
	}
}

// Encode hashes an array of int to a string containing at least MinLength characters taken from the Alphabet.
// Use Decode using the same Alphabet and Salt to get back the array of int.
func (h *HashID) Encode(numbers []int) (string, error) {
	numbers64 := make([]int64, 0, len(numbers))
	for _, id := range numbers {
		numbers64 = append(numbers64, int64(id))
	}
	return h.EncodeInt64(numbers64)
}

// EncodeInt64 hashes an array of int64 to a string containing at least MinLength characters taken from the Alphabet.
// Use DecodeInt64 using the same Alphabet and Salt to get back the array of int64.
func (h *HashID) EncodeInt64(numbers []int64) (string, error) {
	if len(numbers) == 0 {
		return "", errors.New("encoding empty array of numbers makes no sense")
	}
	for _, n := range numbers {
		if n < 0 {
			return "", errors.New("negative number not supported")
		}
	}

	alphabet := make([]rune, len(h.alphabet))
	copy(alphabet, h.alphabet)

	numbersHash := int64(0)
	for i, n := range numbers {
		numbersHash += (n % int64(i+100))
	}

	result := make([]rune, 0, h.minLength)
	lottery := alphabet[numbersHash%int64(len(alphabet))]
	result = append(result, lottery)

	for i, n := range numbers {
		buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
		alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
		hash := hash(n, alphabet)
		result = append(result, hash...)

		if i+1 < len(numbers) {
			n %= int64(hash[0]) + int64(i)
			result = append(result, h.seps[n%int64(len(h.seps))])
		}
	}

	if len(result) < h.minLength {
		guardIndex := (numbersHash + int64(result[0])) % int64(len(h.guards))
		result = append([]rune{h.guards[guardIndex]}, result...)

		if len(result) < h.minLength {
			guardIndex = (numbersHash + int64(result[2])) % int64(len(h.guards))
			result = append(result, h.guards[guardIndex])
		}
	}

	halfLength := len(alphabet) / 2
	for len(result) < h.minLength {
		alphabet = consistentShuffle(alphabet, alphabet)
		result = append(alphabet[halfLength:], append(result, alphabet[:halfLength]...)...)
		excess := len(result) - h.minLength
		if excess > 0 {
			result = result[excess/2 : excess/2+h.minLength]
		}
	}

	return string(result), nil
}

// DEPRECATED: Use DecryptWithError instead
// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) Decode(hash string) []int {
	result, err := h.DecodeWithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) DecodeWithError(hash string) ([]int, error) {
	result64, err := h.DecodeInt64WithError(hash)
	if err != nil {
		return nil, err
	}
	result := make([]int, 0, len(result64))
	for _, id := range result64 {
		result = append(result, int(id))
	}
	return result, nil
}

// DEPRECATED: Use DecryptInt64WithError instead
// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64(hash string) []int64 {
	result, err := h.DecodeInt64WithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64WithError(hash string) ([]int64, error) {
	hashes := splitRunes([]rune(hash), h.guards)
	hashIndex := 0
	if len(hashes) == 2 || len(hashes) == 3 {
		hashIndex = 1
	}

	result := make([]int64, 0)

	hashBreakdown := hashes[hashIndex]
	if len(hashBreakdown) > 0 {
		lottery := hashBreakdown[0]
		hashBreakdown = hashBreakdown[1:]
		hashes = splitRunes(hashBreakdown, h.seps)
		alphabet := []rune(h.alphabet)
		for _, subHash := range hashes {
			buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
			alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
			number, err := unhash(subHash, alphabet)
			if err != nil {
				return nil, err
			}
			result = append(result, number)
		}
	}

	return result, nil
}

func splitRunes(input, seps []rune) [][]rune {
	splitIndices := make([]int, 0)
	for i, inputRune := range input {
		for _, sepsRune := range seps {
			if inputRune == sepsRune {
				splitIndices = append(splitIndices, i)
			}
		}
	}

	result := make([][]rune, 0, len(splitIndices)+1)
	inputLeft := input[:]
	for _, splitIndex := range splitIndices {
		splitIndex -= len(input) - len(inputLeft)
		subInput := make([]rune, splitIndex)
		copy(subInput, inputLeft[:splitIndex])
		result = append(result, subInput)
		inputLeft = inputLeft[splitIndex+1:]
	}
	result = append(result, inputLeft)

	return result
}

func hash(input int64, alphabet []rune) []rune {
	result := make([]rune, 0)
	for {
		r := alphabet[input%int64(len(alphabet))]
		result = append(result, r)
		input /= int64(len(alphabet))
		if input == 0 {
			break
		}
	}
	reversed := make([]rune, len(result))
	for i, r := range result {
		reversed[len(result)-i-1] = r
	}
	return reversed
}

func unhash(input, alphabet []rune) (int64, error) {
	result := int64(0)
	for i, inputRune := range input {
		alphabetPos := -1
		for pos, alphabetRune := range alphabet {
			if inputRune == alphabetRune {
				alphabetPos = pos
				break
			}
		}
		if alphabetPos == -1 {
			return 0, errors.New("alphabet used for hash was different")
		}

		result += int64(alphabetPos) * int64(math.Pow(float64(len(alphabet)), float64(len(input)-i-1)))
	}
	return result, nil
}

func consistentShuffle(alphabet, salt []rune) []rune {
	if len(salt) == 0 {
		return alphabet
	}

	result := make([]rune, len(alphabet))
	copy(result, alphabet)
	for i, v, p := len(result)-1, 0, 0; i > 0; i-- {
		p += int(salt[v])
		j := (int(salt[v]) + v + p) % i
		result[i], result[j] = result[j], result[i]
		v = (v + 1) % len(salt)
	}

	return result
}
<commit_msg>Update comments to conform with the changes of functions' name<commit_after>// Go implementation of http://www.hashids.org under MIT license
// Generates hashes from an array of integers, eg. for YouTube like hashes
// Setup: go get github.com/speps/go-hashids
// Original implementations by Ivan Akimov at https://github.com/ivanakimov
// Thanks to Rémy Oudompheng and Peter Hellberg for code review and fixes

package hashids

import (
	"errors"
	"fmt"
	"math"
)

const (
	// Version is the version number of the library
	Version string = "1.0.0"

	// DefaultAlphabet is the default alphabet used by go-hashids
	DefaultAlphabet string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"

	minAlphabetLength int     = 16
	sepDiv            float64 = 3.5
	guardDiv          float64 = 12.0
)

var sepsOriginal = []rune("cfhistuCFHISTU")

// HashID contains everything needed to encode/decode hashids
type HashID struct {
	alphabet  []rune
	minLength int
	salt      []rune
	seps      []rune
	guards    []rune
}

// HashIDData contains the information needed to generate hashids
type HashIDData struct {
	// Alphabet is the alphabet used to generate new ids
	Alphabet string

	// MinLength is the minimum length of a generated id
	MinLength int

	// Salt is the secret used to make the generated id harder to guess
	Salt string
}

// NewData creates a new HashIDData with the DefaultAlphabet already set.
func NewData() *HashIDData {
	return &HashIDData{Alphabet: DefaultAlphabet}
}

// New creates a new HashID
func New() *HashID {
	return NewWithData(NewData())
}

// NewWithData creates a new HashID with the provided HashIDData
func NewWithData(data *HashIDData) *HashID {
	if len(data.Alphabet) < minAlphabetLength {
		panic(fmt.Errorf("alphabet must contain at least %d characters", minAlphabetLength))
	}
	// Check if all characters are unique in Alphabet
	uniqueCheck := make(map[rune]bool, len(data.Alphabet))
	for _, a := range data.Alphabet {
		if _, found := uniqueCheck[a]; found {
			panic(errors.New("duplicate character in alphabet"))
		}
		uniqueCheck[a] = true
	}

	alphabet := []rune(data.Alphabet)
	salt := []rune(data.Salt)

	seps := make([]rune, len(sepsOriginal))
	copy(seps, sepsOriginal)

	// seps should contain only characters present in alphabet; alphabet should not contains seps
	for i := 0; i < len(seps); i++ {
		foundIndex := -1
		for j, a := range alphabet {
			if a == seps[i] {
				foundIndex = j
				break
			}
		}
		if foundIndex == -1 {
			seps = append(seps[:i], seps[i+1:]...)
			i--
		} else {
			alphabet = append(alphabet[:foundIndex], alphabet[foundIndex+1:]...)
		}
	}
	seps = consistentShuffle(seps, salt)

	if len(seps) == 0 || float64(len(alphabet))/float64(len(seps)) > sepDiv {
		sepsLength := int(math.Ceil(float64(len(alphabet)) / sepDiv))
		if sepsLength == 1 {
			sepsLength++
		}
		if sepsLength > len(seps) {
			diff := sepsLength - len(seps)
			seps = append(seps, alphabet[:diff]...)
			alphabet = alphabet[diff:]
		} else {
			seps = seps[:sepsLength]
		}
	}
	alphabet = consistentShuffle(alphabet, salt)

	guardCount := int(math.Ceil(float64(len(alphabet)) / guardDiv))
	var guards []rune
	if len(alphabet) < 3 {
		guards = seps[:guardCount]
		seps = seps[guardCount:]
	} else {
		guards = alphabet[:guardCount]
		alphabet = alphabet[guardCount:]
	}

	return &HashID{
		alphabet:  alphabet,
		minLength: data.MinLength,
		salt:      salt,
		seps:      seps,
		guards:    guards,
	}
}

// Encode hashes an array of int to a string containing at least MinLength characters taken from the Alphabet.
// Use Decode using the same Alphabet and Salt to get back the array of int.
func (h *HashID) Encode(numbers []int) (string, error) {
	numbers64 := make([]int64, 0, len(numbers))
	for _, id := range numbers {
		numbers64 = append(numbers64, int64(id))
	}
	return h.EncodeInt64(numbers64)
}

// EncodeInt64 hashes an array of int64 to a string containing at least MinLength characters taken from the Alphabet.
// Use DecodeInt64 using the same Alphabet and Salt to get back the array of int64.
func (h *HashID) EncodeInt64(numbers []int64) (string, error) {
	if len(numbers) == 0 {
		return "", errors.New("encoding empty array of numbers makes no sense")
	}
	for _, n := range numbers {
		if n < 0 {
			return "", errors.New("negative number not supported")
		}
	}

	alphabet := make([]rune, len(h.alphabet))
	copy(alphabet, h.alphabet)

	numbersHash := int64(0)
	for i, n := range numbers {
		numbersHash += (n % int64(i+100))
	}

	result := make([]rune, 0, h.minLength)
	lottery := alphabet[numbersHash%int64(len(alphabet))]
	result = append(result, lottery)

	for i, n := range numbers {
		buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
		alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
		hash := hash(n, alphabet)
		result = append(result, hash...)

		if i+1 < len(numbers) {
			n %= int64(hash[0]) + int64(i)
			result = append(result, h.seps[n%int64(len(h.seps))])
		}
	}

	if len(result) < h.minLength {
		guardIndex := (numbersHash + int64(result[0])) % int64(len(h.guards))
		result = append([]rune{h.guards[guardIndex]}, result...)

		if len(result) < h.minLength {
			guardIndex = (numbersHash + int64(result[2])) % int64(len(h.guards))
			result = append(result, h.guards[guardIndex])
		}
	}

	halfLength := len(alphabet) / 2
	for len(result) < h.minLength {
		alphabet = consistentShuffle(alphabet, alphabet)
		result = append(alphabet[halfLength:], append(result, alphabet[:halfLength]...)...)
		excess := len(result) - h.minLength
		if excess > 0 {
			result = result[excess/2 : excess/2+h.minLength]
		}
	}

	return string(result), nil
}

// DEPRECATED: Use DecodeWithError instead
// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) Decode(hash string) []int {
	result, err := h.DecodeWithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// Decode unhashes the string passed to an array of int.
// It is symmetric with Encode if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on Decode.
func (h *HashID) DecodeWithError(hash string) ([]int, error) {
	result64, err := h.DecodeInt64WithError(hash)
	if err != nil {
		return nil, err
	}
	result := make([]int, 0, len(result64))
	for _, id := range result64 {
		result = append(result, int(id))
	}
	return result, nil
}

// DEPRECATED: Use DecodeInt64WithError instead
// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64(hash string) []int64 {
	result, err := h.DecodeInt64WithError(hash)
	if err != nil {
		panic(err)
	}
	return result
}

// DecodeInt64 unhashes the string passed to an array of int64.
// It is symmetric with EncodeInt64 if the Alphabet and Salt are the same ones which were used to hash.
// MinLength has no effect on DecodeInt64.
func (h *HashID) DecodeInt64WithError(hash string) ([]int64, error) {
	hashes := splitRunes([]rune(hash), h.guards)
	hashIndex := 0
	if len(hashes) == 2 || len(hashes) == 3 {
		hashIndex = 1
	}

	result := make([]int64, 0)

	hashBreakdown := hashes[hashIndex]
	if len(hashBreakdown) > 0 {
		lottery := hashBreakdown[0]
		hashBreakdown = hashBreakdown[1:]
		hashes = splitRunes(hashBreakdown, h.seps)
		alphabet := []rune(h.alphabet)
		for _, subHash := range hashes {
			buffer := append([]rune{lottery}, append(h.salt, alphabet...)...)
			alphabet = consistentShuffle(alphabet, buffer[:len(alphabet)])
			number, err := unhash(subHash, alphabet)
			if err != nil {
				return nil, err
			}
			result = append(result, number)
		}
	}

	return result, nil
}

func splitRunes(input, seps []rune) [][]rune {
	splitIndices := make([]int, 0)
	for i, inputRune := range input {
		for _, sepsRune := range seps {
			if inputRune == sepsRune {
				splitIndices = append(splitIndices, i)
			}
		}
	}

	result := make([][]rune, 0, len(splitIndices)+1)
	inputLeft := input[:]
	for _, splitIndex := range splitIndices {
		splitIndex -= len(input) - len(inputLeft)
		subInput := make([]rune, splitIndex)
		copy(subInput, inputLeft[:splitIndex])
		result = append(result, subInput)
		inputLeft = inputLeft[splitIndex+1:]
	}
	result = append(result, inputLeft)

	return result
}

func hash(input int64, alphabet []rune) []rune {
	result := make([]rune, 0)
	for {
		r := alphabet[input%int64(len(alphabet))]
		result = append(result, r)
		input /= int64(len(alphabet))
		if input == 0 {
			break
		}
	}
	reversed := make([]rune, len(result))
	for i, r := range result {
		reversed[len(result)-i-1] = r
	}
	return reversed
}

func unhash(input, alphabet []rune) (int64, error) {
	result := int64(0)
	for i, inputRune := range input {
		alphabetPos := -1
		for pos, alphabetRune := range alphabet {
			if inputRune == alphabetRune {
				alphabetPos = pos
				break
			}
		}
		if alphabetPos == -1 {
			return 0, errors.New("alphabet used for hash was different")
		}

		result += int64(alphabetPos) * int64(math.Pow(float64(len(alphabet)), float64(len(input)-i-1)))
	}
	return result, nil
}

func consistentShuffle(alphabet, salt []rune) []rune {
	if len(salt) == 0 {
		return alphabet
	}

	result := make([]rune, len(alphabet))
	copy(result, alphabet)
	for i, v, p := len(result)-1, 0, 0; i > 0; i-- {
		p += int(salt[v])
		j := (int(salt[v]) + v + p) % i
		result[i], result[j] = result[j], result[i]
		v = (v + 1) % len(salt)
	}

	return result
}
<|endoftext|>


<commit_before>// Package hashmap implements a simple non-blocking (lock-free) hash map.
//
// The semantics of this hashmap are quite different from the go built-in.
// Aside from thread-safety, any values (including structs and slices) are
// allowed as keys, and a value of nil is indestinguishable from an element not
// being in the map.
package hashmap

import (
	a "sync/atomic"
	r "reflect"
)

var (
	defCap = uint32(32)
)

// A hashmap entry.
type entry struct {
	k interface{}
	v interface{}
}

type Hashmap struct {
	data []entry
	size uint32
	capc uint32
}

// Creates a hashmap with the default initial capacity (32). The hashmap will
// be grown to twice its size when the number of entries reaches 50% of the
// capacity.
func New() *Hashmap {
	return NewCap(defCap)
}

// Creates a hashmap with the specified initial capacity.
func NewCap(c uint32) *Hashmap {
	return &Hashmap{
		data: make([]entry, c),
		size: 0,
		capc: uint32(c),
	}
}

// Returns the capacity of this hashmap.
func (hm *Hashmap) Capacity() uint32 {
	return a.LoadUint32(&hm.capc)
}

// Returns the size of this hashmap. This is /not/ the number of elements
// stored - it is the number of keys allocated.
func (hm *Hashmap) Size() uint32 {
	return a.LoadUint32(&hm.size)
}

func (hm *Hashmap) Get(k interface{}) interface{} {
	// If the map changes capacity during the operation, we will detect
	// that and retry. Note that the only legal transformation of this
	// value is for it to increase (double), so we can easily check to see
	// if it has changed.
	c := hm.Capacity()

	// Guaranteed to be correct when c is correct.
	h := hash(k, c)

	// Since keys cannot be removed, we know that our target entry lies
	// somewhere 'after' the entry indexed by h, and that no nil keys lie
	// in between.
	for i := uint32(0); i < h+c; i++ {
		// Atomically fetch the data indexed by (i%c).
		e := hm.data[i%c] // TODO atomic

		// Note that at this point, the data in e is incorrect if and
		// only if the capacity has changed (and the map has therefore
		// been expanded). We now check to see if that has happened,
		// and if so, abort.
		if c != hm.Capacity() {
			// This is the part that breaks our per-goroutine
			// progress guarantee.
			return hm.Get(k)
		}

		// The data in e may now be considered actionable. Since e is
		// stored in local memory, we don't have to worry about making
		// further operations atomic.

		// If e.k is nil, it's ok to claim that there's no such
		// available element.
		if e.k == nil { return nil }
		if r.DeepEqual(e.k, k) {
			return e.v
		}
	}

	// Really, this should never happen; it just means we've cycled through
	// the whole map, and none of the keys were nil
	return nil
}

func (hm *Hashmap) Set(k interface{}, v interface{}) {
	c := hm.Capacity()
	h := hash(k, c)
	for i := uint32(0); i < h+c; i++ {
		e := &hm.data[i%c]
		if e.k == nil {
			e.v = v
			e.k = k
			hm.size++
			// TODO Grow if necessary
			return
		}
		if r.DeepEqual(e.k, k) {
			e.v = v
			return
		}
	}
	panic("Hashmap full")
}

// Deletes the given key from the map, and returns true if it existed. Although
// this method is semantically equivalent to m.Set(k, nil), this method will
// not allocate another key if the key is not already in use, and should
// therefore be used instead.
func (hm *Hashmap) Del(k interface{}) bool {
	c := hm.Capacity()
	h := hash(k, c)
	for i := uint32(0); i < h+c; i++ {
		e := &hm.data[i%c]
		if r.DeepEqual(e.k, k) {
			//e.k = nil
			e.v = nil
			hm.size--
			return true
		}
	}
	return false
}

func (hm *Hashmap) Grow(c uint32) {
	// TODO
}

func hash(key interface{}, c uint32) uint32 {
	switch k := key.(type) {
	case int:
		return uint32(k) % c
	}
	return uint32(0)
}
<commit_msg>Almost completely finished Get<commit_after>// Package hashmap implements a simple non-blocking (lock-free) hash map.
//
// The semantics of this hashmap are quite different from the go built-in.
// Aside from thread-safety, any values (including structs and slices) are
// allowed as keys, and a value of nil is indestinguishable from an element not
// being in the map.
package hashmap

import (
	a "sync/atomic"
	r "reflect"
)

var (
	defCap = uint32(32)
)

// A hashmap entry.
type entry struct {
	k interface{}
	v interface{}
}

type Hashmap struct {
	data []entry
	size uint32
	capc uint32
}

// Creates a hashmap with the default initial capacity (32). The hashmap will
// be grown to twice its size when the number of entries reaches 50% of the
// capacity.
func New() *Hashmap {
	return NewCap(defCap)
}

// Creates a hashmap with the specified initial capacity.
func NewCap(c uint32) *Hashmap {
	return &Hashmap{
		data: make([]entry, c),
		size: 0,
		capc: uint32(c),
	}
}

// Returns the capacity of this hashmap.
func (hm *Hashmap) Capacity() uint32 {
	return a.LoadUint32(&hm.capc)
}

// Returns the size of this hashmap. This is /not/ the number of elements
// stored - it is the number of keys allocated.
func (hm *Hashmap) Size() uint32 {
	return a.LoadUint32(&hm.size)
}

func (hm *Hashmap) Get(k interface{}) interface{} {
	// If the map changes capacity during the operation, we will detect
	// that and retry. Note that the only legal transformation of this
	// value is for it to increase (double), so we can easily check to see
	// if it has changed.
	c := hm.Capacity()

	// Guaranteed to be correct when c is correct.
	h := hash(k, c)

	// Since keys cannot be removed, we know that our target entry lies
	// somewhere 'after' the entry indexed by h, and that no nil keys lie
	// in between.
	for i := uint32(0); i < h+c; i++ {
		// Atomically fetch the data indexed by (i%c). Since there are
		// 16 bytes to fetch, this can't be done in a single atomic
		// operation; however, since a set key can never change, we
		// don't need perfect atomicity except when fetching the 64
		// bits of the value.
		/*e := entry{
			k: nil,
			v: nil,
		}*/
		e := hm.data[i%c] // TODO atomic

		// Note that at this point, the data in e is incorrect if and
		// only if the capacity has changed (and the map has therefore
		// been expanded). We now check to see if that has happened,
		// and if so, abort.
		if c != hm.Capacity() {
			// This is the part that breaks our per-goroutine
			// progress guarantee.
			return hm.Get(k)
		}

		// The data in e may now be considered actionable. Since e is
		// stored in local memory, we don't have to worry about making
		// further operations atomic.

		// If e.k is nil, it's ok to claim that there's no such
		// available element.
		if e.k == nil { return nil }

		// Again, since we know that e.v matched e.k at some point,
		// it's ok to do this with no further checks.
		if r.DeepEqual(e.k, k) {
			return e.v
		}
	}

	// Really, this should never happen; it just means we've cycled through
	// the whole map, and none of the keys were nil
	return nil
}

func (hm *Hashmap) Set(k interface{}, v interface{}) {
	c := hm.Capacity()
	h := hash(k, c)
	for i := uint32(0); i < h+c; i++ {
		e := &hm.data[i%c]
		if e.k == nil {
			e.v = v
			e.k = k
			hm.size++
			// TODO Grow if necessary
			return
		}
		if r.DeepEqual(e.k, k) {
			e.v = v
			return
		}
	}
	panic("Hashmap full")
}

// Deletes the given key from the map, and returns true if it existed. Although
// this method is semantically equivalent to m.Set(k, nil), this method will
// not allocate another key if the key is not already in use, and should
// therefore be used instead.
func (hm *Hashmap) Del(k interface{}) bool {
	c := hm.Capacity()
	h := hash(k, c)
	for i := uint32(0); i < h+c; i++ {
		e := &hm.data[i%c]
		if r.DeepEqual(e.k, k) {
			//e.k = nil
			e.v = nil
			hm.size--
			return true
		}
	}
	return false
}

func (hm *Hashmap) Grow(c uint32) {
	// TODO
}

func hash(key interface{}, c uint32) uint32 {
	switch k := key.(type) {
	case int:
		return uint32(k) % c
	}
	return uint32(0)
}
<|endoftext|>


<commit_before>package mithril

import "testing"

func TestCreateElement(t *testing.T) {
	element := M("div#id-value.class-name[data1='value1'][data2=\"value2\"]")
	if element == nil {
		t.Fatalf("Expected element to be a VirtualElement object but it was nil")
	}
}

func TestElementTag(t *testing.T) {
	if el := M("li"); el.Tag != "li" {
		t.Fatalf("Expected element tag to be \"li\" but it was %s", el.Tag)
	}
}

func TestElementID(t *testing.T) {
	if el := M("div#id-value"); el.Attrs.ID != "id-value" {
		t.Fatalf("Expected element ID to be id-value but it was %s", el.Attrs.ID)
	}
}

func TestCreateElementChildren(t *testing.T) {
	element := M(
		"div#obj1",
		M("div#obj2"),
	)
	if _, ok := element.Children.(*VirtualElement); !ok {
		t.Fatalf("Expected element.Children to be a VirtualElement")
	}
}
<commit_msg>update mithril_test<commit_after>package mithril

import "testing"

func TestCreateElement(t *testing.T) {
	element := M("div#id-value.class-name[data1='value1'][data2=\"value2\"][data3]")
	if element == nil {
		t.Fatalf("Expected element to be a VirtualElement object but it was nil")
	}
}

func TestElementTag(t *testing.T) {
	if el := M("li"); el.Tag != "li" {
		t.Fatalf("Expected element tag to be \"li\" but it was %s", el.Tag)
	}
}

func TestElementID(t *testing.T) {
	if el := M("div#id-value"); el.Attrs.ID != "id-value" {
		t.Fatalf("Expected element ID to be id-value but it was %s", el.Attrs.ID)
	}
}

func TestCreateElementChildren(t *testing.T) {
	element := M(
		"div#obj1",
		M("div#obj2"),
	)
	if _, ok := element.Children.(*VirtualElement); !ok {
		t.Fatalf("Expected element.Children to be a VirtualElement")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
)

type Header struct {
	License     [100]byte
	Name        [12]byte
	Version     [8]byte
	Timestamp   [42]byte
	FileSize    uint32
	FormatCount uint16
	FileCount   uint16
	Val1       [2]byte
	Val2       [2]byte
	Val3       [2]byte
	Val4       [2]byte
	Val5       [2]byte
}

type FormatInfo struct {
	Name  [4]byte
	Count uint16
	Pos   uint16
}

type FileInfo struct {
	Name [12]byte
	_    uint16
	Size uint32
	Addr uint32
	Val1 uint32
}

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func readNextBytes(file *os.File, number int) []byte {
	bytes := make([]byte, number)

	num, err := file.Read(bytes)
	if num != number {
		fmt.Printf("Ran out of bytes! (wanted: %d, got: %d)\n", number, num)
	}
	check(err)
	return bytes
}

func getBuffer(f *os.File, n int) *bytes.Buffer {
	data := readNextBytes(f, n)
	buffer := bytes.NewBuffer(data)
	return buffer
}

func unpackHeader(f *os.File, hdrSize int) *Header {
	hdr := Header{}
	err := binary.Read(getBuffer(f, hdrSize), binary.LittleEndian, &hdr)
	check(err)
	return &hdr
}

func unpackFormatList(f *os.File, hdr *Header) []*FormatInfo {
	formatList := make([]*FormatInfo, hdr.FormatCount)
	for i := 0; i < int(hdr.FormatCount); i++ {
		aFormat := FormatInfo{}
		err := binary.Read(getBuffer(f, 8), binary.LittleEndian, &aFormat)
		check(err)
		formatList[i] = &aFormat
	}
	return formatList
}

func unpackFileList(f *os.File, hdr *Header) []*FileInfo {
	fileList := make([]*FileInfo, hdr.FileCount)
	for i := 0; i < int(hdr.FileCount); i++ {
		aFile := FileInfo{}
		err := binary.Read(getBuffer(f, 26), binary.LittleEndian, &aFile)
		check(err)
		fileList[i] = &aFile
	}
	return fileList
}

func unpackFiles(f *os.File, hdr *Header, formats []*FormatInfo, files []*FileInfo)  {
	for i := 0; i < len(formats); i++ {
		workDir := "./" +  string(bytes.Trim(hdr.Name[:12], "x\000")) + "/" +
			string(formats[i].Name[:3]) + "/"
		fmt.Printf("Extracting to %s\n", workDir)
		os.MkdirAll(workDir, os.ModePerm)
		fmt.Printf("File count: %d\n", formats[i].Count)
		for ii := formats[i].Pos; ii < formats[i].Count+formats[i].Pos; ii++ {
			s := workDir + string(bytes.Trim(files[ii].Name[:12], "x\000"))
			//fmt.Printf("Writing file: %s\n", s)
			w, werr := os.Create(s)
			check(werr)
			addr := int64(files[ii].Addr)
			fsize := int(files[ii].Size)
			f.Seek(addr, 0)
			wdata := (readNextBytes(f, fsize))
			_, werr = w.Write(wdata)
			check(werr)
			w.Close()
		}
	}
}

func unpack(f *os.File, hdr *Header) {
	formatList := unpackFormatList(f, hdr)
	fileList := unpackFileList(f, hdr)
	unpackFiles(f, hdr, formatList, fileList)
}

func main() {
	var hdrSize int

	path := os.Args[1]

	f, err := os.Open(path)
	check(err)
	defer f.Close()

	fmt.Printf("%s opened\n", path)

	formatCheck := readNextBytes(f, 1)

	if formatCheck[0] == byte(0x41) {
		fmt.Printf("Valid RSF format found\n")
		hdrSize = 0xb4
	} else if formatCheck[0] == byte(0x6c) {
		log.Fatal("Cannot handle old-style RSF format\n")
	} else {
		log.Fatal("Unknown file format\n")
	}

	f.Seek(0, 0)
	header := unpackHeader(f, hdrSize)

	fmt.Printf("%s\n%s\n%s\n%s\nFilesize: %d\nFormats: %d Files: %d\n", header.License, header.Name,
		header.Version, header.Timestamp, header.FileSize, header.FormatCount, header.FileCount)

	unpack(f, header)
}
<commit_msg>Added check for no arguments<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"os"
)

type Header struct {
	License     [100]byte
	Name        [12]byte
	Version     [8]byte
	Timestamp   [42]byte
	FileSize    uint32
	FormatCount uint16
	FileCount   uint16
	Val1       [2]byte
	Val2       [2]byte
	Val3       [2]byte
	Val4       [2]byte
	Val5       [2]byte
}

type FormatInfo struct {
	Name  [4]byte
	Count uint16
	Pos   uint16
}

type FileInfo struct {
	Name [12]byte
	_    uint16
	Size uint32
	Addr uint32
	Val1 uint32
}

func check(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func readNextBytes(file *os.File, number int) []byte {
	bytes := make([]byte, number)

	num, err := file.Read(bytes)
	if num != number {
		fmt.Printf("Ran out of bytes! (wanted: %d, got: %d)\n", number, num)
	}
	check(err)
	return bytes
}

func getBuffer(f *os.File, n int) *bytes.Buffer {
	data := readNextBytes(f, n)
	buffer := bytes.NewBuffer(data)
	return buffer
}

func unpackHeader(f *os.File, hdrSize int) *Header {
	hdr := Header{}
	err := binary.Read(getBuffer(f, hdrSize), binary.LittleEndian, &hdr)
	check(err)
	return &hdr
}

func unpackFormatList(f *os.File, hdr *Header) []*FormatInfo {
	formatList := make([]*FormatInfo, hdr.FormatCount)
	for i := 0; i < int(hdr.FormatCount); i++ {
		aFormat := FormatInfo{}
		err := binary.Read(getBuffer(f, 8), binary.LittleEndian, &aFormat)
		check(err)
		formatList[i] = &aFormat
	}
	return formatList
}

func unpackFileList(f *os.File, hdr *Header) []*FileInfo {
	fileList := make([]*FileInfo, hdr.FileCount)
	for i := 0; i < int(hdr.FileCount); i++ {
		aFile := FileInfo{}
		err := binary.Read(getBuffer(f, 26), binary.LittleEndian, &aFile)
		check(err)
		fileList[i] = &aFile
	}
	return fileList
}

func unpackFiles(f *os.File, hdr *Header, formats []*FormatInfo, files []*FileInfo)  {
	for i := 0; i < len(formats); i++ {
		workDir := "./" +  string(bytes.Trim(hdr.Name[:12], "x\000")) + "/" +
			string(formats[i].Name[:3]) + "/"
		fmt.Printf("Extracting to %s\n", workDir)
		os.MkdirAll(workDir, os.ModePerm)
		fmt.Printf("File count: %d\n", formats[i].Count)
		for ii := formats[i].Pos; ii < formats[i].Count+formats[i].Pos; ii++ {
			s := workDir + string(bytes.Trim(files[ii].Name[:12], "x\000"))
			//fmt.Printf("Writing file: %s\n", s)
			w, werr := os.Create(s)
			check(werr)
			addr := int64(files[ii].Addr)
			fsize := int(files[ii].Size)
			f.Seek(addr, 0)
			wdata := (readNextBytes(f, fsize))
			_, werr = w.Write(wdata)
			check(werr)
			w.Close()
		}
	}
}

func unpack(f *os.File, hdr *Header) {
	formatList := unpackFormatList(f, hdr)
	fileList := unpackFileList(f, hdr)
	unpackFiles(f, hdr, formatList, fileList)
}

func main() {
	var hdrSize int

	if len(os.Args) == 1 {
		fmt.Printf("Usage: rsfunpack FILE\n")
		return
	}
	path := os.Args[1]

	f, err := os.Open(path)
	check(err)
	defer f.Close()

	fmt.Printf("%s opened\n", path)

	formatCheck := readNextBytes(f, 1)

	if formatCheck[0] == byte(0x41) {
		fmt.Printf("Valid RSF format found\n")
		hdrSize = 0xb4
	} else if formatCheck[0] == byte(0x6c) {
		log.Fatal("Cannot handle old-style RSF format\n")
	} else {
		log.Fatal("Unknown file format\n")
	}

	f.Seek(0, 0)
	header := unpackHeader(f, hdrSize)

	fmt.Printf("%s\n%s\n%s\n%s\nFilesize: %d\nFormats: %d Files: %d\n", header.License, header.Name,
		header.Version, header.Timestamp, header.FileSize, header.FormatCount, header.FileCount)

	unpack(f, header)
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2019 Red Hat, Inc.
 *
 */

package tests_test

import (
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/api/policy/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

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

	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/tests"
)

const (
	WaitSecondsBeforeDeploymentCheck     = 2 * time.Second
	DefaultStabilizationTimeoutInSeconds = 300
	DefaultPollIntervalInSeconds         = 3
	labelKey                             = "control-plane-test"
	labelValue                           = "selected"
)

var _ = Describe("KubeVirt control plane resilience", func() {

	var nodeNames []string
	var selectedNodeName string

	controlPlaneDeploymentNames := []string{"virt-api", "virt-controller"}

	tests.FlagParse()

	getRunningReadyPods := func(podList *v1.PodList, podNames []string, nodeNames ...string) (pods []*v1.Pod) {
		pods = make([]*v1.Pod, 0)
		for _, pod := range podList.Items {
			if pod.Status.Phase != v1.PodRunning {
				continue
			}
			podReady := tests.PodReady(&pod)
			if podReady != v1.ConditionTrue {
				continue
			}
			for _, podName := range podNames {
				if strings.HasPrefix(pod.Name, podName) {
					if len(nodeNames) > 0 {
						for _, nodeName := range nodeNames {
							if pod.Spec.NodeName == nodeName {
								deepCopy := pod.DeepCopy()
								pods = append(pods, deepCopy)
							}
						}
					} else {
						deepCopy := pod.DeepCopy()
						pods = append(pods, deepCopy)
					}
				}
			}
		}
		return
	}

	getPodList := func() (podList *v1.PodList, err error) {
		virtCli, err := kubecli.GetKubevirtClient()
		if err != nil {
			return
		}
		podList, err = virtCli.CoreV1().Pods(tests.KubeVirtInstallNamespace).List(metav1.ListOptions{})
		return
	}

	getSelectedNode := func() (selectedNode *v1.Node) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		selectedNode, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Get(selectedNodeName, metav1.GetOptions{})
		tests.PanicOnError(err)
		return
	}

	waitForDeploymentsToStabilize := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		deploymentsClient := kubecli.KubevirtClient.AppsV1(virtCli).Deployments(tests.KubeVirtInstallNamespace)
		for _, deploymentName := range controlPlaneDeploymentNames {
			deployment, err := deploymentsClient.Get(deploymentName, metav1.GetOptions{})
			if err != nil {
				return false, err
			}

			if !(deployment.Status.UpdatedReplicas == *(deployment.Spec.Replicas) &&
				deployment.Status.Replicas == *(deployment.Spec.Replicas) &&
				deployment.Status.AvailableReplicas == *(deployment.Spec.Replicas)) {
				return false, err
			}
		}
		return true, nil
	}

	addLabelToSelectedNode := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		selectedNode, err := kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Get(selectedNodeName, metav1.GetOptions{})
		tests.PanicOnError(err)
		if selectedNode.Labels == nil {
			selectedNode.Labels = make(map[string]string)
		}
		selectedNode.Labels[labelKey] = labelValue
		_, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Update(selectedNode)
		if err != nil {
			return false, fmt.Errorf("failed to update node: %v", err)
		}
		return true, nil
	}

	// Add nodeSelector to deployments so that they get scheduled to selectedNode
	addNodeSelectorToDeployments := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		deploymentsClient := kubecli.KubevirtClient.AppsV1(virtCli).Deployments(tests.KubeVirtInstallNamespace)
		tests.PanicOnError(err)
		for _, deploymentName := range controlPlaneDeploymentNames {
			deployment, err := deploymentsClient.Get(deploymentName, metav1.GetOptions{})
			tests.PanicOnError(err)

			labelMap := make(map[string]string)
			labelMap[labelKey] = labelValue
			if deployment.Spec.Template.Spec.NodeSelector == nil {
				deployment.Spec.Template.Spec.NodeSelector = make(map[string]string)
			}
			deployment.Spec.Template.Spec.NodeSelector[labelKey] = labelValue
			_, err = deploymentsClient.Update(deployment)
			if err != nil {
				return false, err
			}
		}
		return true, nil
	}

	checkControlPlanePodsHaveNodeSelector := func() (bool, error) {
		podList, err := getPodList()
		tests.PanicOnError(err)
		runningControlPlanePods := getRunningReadyPods(podList, controlPlaneDeploymentNames)
		for _, pod := range runningControlPlanePods {
			if actualLabelValue, ok := pod.Spec.NodeSelector[labelKey]; ok {
				if actualLabelValue != labelValue {
					return false, fmt.Errorf("pod %s has node selector %s with value %s, expected was %s", pod.Name, labelKey, actualLabelValue, labelValue)
				}
			} else {
				return false, fmt.Errorf("pod %s has no node selector %s", pod.Name, labelKey)
			}
		}
		return true, nil
	}

	BeforeEach(func() {
		tests.SkipIfNoCmd("kubectl")
		tests.BeforeTestCleanup()

		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)

		nodes := tests.GetAllSchedulableNodes(virtCli).Items
		nodeNames = make([]string, len(nodes))
		for index, node := range nodes {
			nodeNames[index] = node.Name
		}

		// select one node from result for test, first node will do
		selectedNodeName = nodes[0].Name

		Eventually(addLabelToSelectedNode,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(addNodeSelectorToDeployments,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		time.Sleep(WaitSecondsBeforeDeploymentCheck)

		Eventually(checkControlPlanePodsHaveNodeSelector,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(waitForDeploymentsToStabilize,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())
	})

	removeNodeSelectorFromDeployments := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		deploymentsClient := kubecli.KubevirtClient.AppsV1(virtCli).Deployments(tests.KubeVirtInstallNamespace)
		for _, deploymentName := range controlPlaneDeploymentNames {
			deployment, err := deploymentsClient.Get(deploymentName, metav1.GetOptions{})
			if err != nil {
				return false, err
			}
			delete(deployment.Spec.Template.Spec.NodeSelector, labelKey)
			_, err = deploymentsClient.Update(deployment)
			if err != nil {
				return false, err
			}
		}
		return true, nil
	}

	// Clean up selectedNode: Remove label and make schedulable again
	cleanUpSelectedNode := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		selectedNode := getSelectedNode()
		selectedNode.Spec.Unschedulable = false
		delete(selectedNode.Labels, labelKey)
		_, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Update(selectedNode)
		if err != nil {
			return false, err
		}
		return true, nil
	}

	AfterEach(func() {
		Eventually(removeNodeSelectorFromDeployments,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(cleanUpSelectedNode,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		time.Sleep(WaitSecondsBeforeDeploymentCheck)

		Eventually(waitForDeploymentsToStabilize,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())
	})

	When("evicting pods of control plane, last eviction should fail", func() {

		test := func(podName string) {
			virtCli, err := kubecli.GetKubevirtClient()
			tests.PanicOnError(err)

			By(fmt.Sprintf("set node %s unschedulable\n", selectedNodeName))
			selectedNode := getSelectedNode()
			selectedNode.Spec.Unschedulable = true
			_, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Update(selectedNode)

			By(fmt.Sprintf("Try to evict all pods %s from node %s\n", podName, selectedNodeName))
			podList, err := getPodList()
			Expect(err).ToNot(HaveOccurred())
			runningPods := getRunningReadyPods(podList, []string{podName})
			for index, pod := range runningPods {
				err = virtCli.CoreV1().Pods(tests.KubeVirtInstallNamespace).Evict(&v1beta1.Eviction{ObjectMeta: metav1.ObjectMeta{Name: pod.Name}})
				if index < len(runningPods)-1 {
					Expect(err).ToNot(HaveOccurred())
				}
			}
			Expect(err).To(HaveOccurred(), "no error occurred on evict of last pod")
		}

		It("for virt-controller pods", func() { test("virt-controller") })
		It("for virt-api pods", func() { test("virt-api") })

	})

})
<commit_msg>Add check for removal of nodeSelectors on pods<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2019 Red Hat, Inc.
 *
 */

package tests_test

import (
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/api/policy/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

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

	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/tests"
)

const (
	WaitSecondsBeforeDeploymentCheck     = 2 * time.Second
	DefaultStabilizationTimeoutInSeconds = 300
	DefaultPollIntervalInSeconds         = 3
	labelKey                             = "control-plane-test"
	labelValue                           = "selected"
)

var _ = Describe("KubeVirt control plane resilience", func() {

	var nodeNames []string
	var selectedNodeName string

	controlPlaneDeploymentNames := []string{"virt-api", "virt-controller"}

	tests.FlagParse()

	getRunningReadyPods := func(podList *v1.PodList, podNames []string, nodeNames ...string) (pods []*v1.Pod) {
		pods = make([]*v1.Pod, 0)
		for _, pod := range podList.Items {
			if pod.Status.Phase != v1.PodRunning {
				continue
			}
			podReady := tests.PodReady(&pod)
			if podReady != v1.ConditionTrue {
				continue
			}
			for _, podName := range podNames {
				if strings.HasPrefix(pod.Name, podName) {
					if len(nodeNames) > 0 {
						for _, nodeName := range nodeNames {
							if pod.Spec.NodeName == nodeName {
								deepCopy := pod.DeepCopy()
								pods = append(pods, deepCopy)
							}
						}
					} else {
						deepCopy := pod.DeepCopy()
						pods = append(pods, deepCopy)
					}
				}
			}
		}
		return
	}

	getPodList := func() (podList *v1.PodList, err error) {
		virtCli, err := kubecli.GetKubevirtClient()
		if err != nil {
			return
		}
		podList, err = virtCli.CoreV1().Pods(tests.KubeVirtInstallNamespace).List(metav1.ListOptions{})
		return
	}

	getSelectedNode := func() (selectedNode *v1.Node) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		selectedNode, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Get(selectedNodeName, metav1.GetOptions{})
		tests.PanicOnError(err)
		return
	}

	waitForDeploymentsToStabilize := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		deploymentsClient := kubecli.KubevirtClient.AppsV1(virtCli).Deployments(tests.KubeVirtInstallNamespace)
		for _, deploymentName := range controlPlaneDeploymentNames {
			deployment, err := deploymentsClient.Get(deploymentName, metav1.GetOptions{})
			if err != nil {
				return false, err
			}

			if !(deployment.Status.UpdatedReplicas == *(deployment.Spec.Replicas) &&
				deployment.Status.Replicas == *(deployment.Spec.Replicas) &&
				deployment.Status.AvailableReplicas == *(deployment.Spec.Replicas)) {
				return false, err
			}
		}
		return true, nil
	}

	addLabelToSelectedNode := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		selectedNode, err := kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Get(selectedNodeName, metav1.GetOptions{})
		tests.PanicOnError(err)
		if selectedNode.Labels == nil {
			selectedNode.Labels = make(map[string]string)
		}
		selectedNode.Labels[labelKey] = labelValue
		_, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Update(selectedNode)
		if err != nil {
			return false, fmt.Errorf("failed to update node: %v", err)
		}
		return true, nil
	}

	// Add nodeSelector to deployments so that they get scheduled to selectedNode
	addNodeSelectorToDeployments := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		deploymentsClient := kubecli.KubevirtClient.AppsV1(virtCli).Deployments(tests.KubeVirtInstallNamespace)
		tests.PanicOnError(err)
		for _, deploymentName := range controlPlaneDeploymentNames {
			deployment, err := deploymentsClient.Get(deploymentName, metav1.GetOptions{})
			tests.PanicOnError(err)

			labelMap := make(map[string]string)
			labelMap[labelKey] = labelValue
			if deployment.Spec.Template.Spec.NodeSelector == nil {
				deployment.Spec.Template.Spec.NodeSelector = make(map[string]string)
			}
			deployment.Spec.Template.Spec.NodeSelector[labelKey] = labelValue
			_, err = deploymentsClient.Update(deployment)
			if err != nil {
				return false, err
			}
		}
		return true, nil
	}

	checkControlPlanePodsHaveNodeSelector := func() (bool, error) {
		podList, err := getPodList()
		tests.PanicOnError(err)
		runningControlPlanePods := getRunningReadyPods(podList, controlPlaneDeploymentNames)
		for _, pod := range runningControlPlanePods {
			if actualLabelValue, ok := pod.Spec.NodeSelector[labelKey]; ok {
				if actualLabelValue != labelValue {
					return false, fmt.Errorf("pod %s has node selector %s with value %s, expected was %s", pod.Name, labelKey, actualLabelValue, labelValue)
				}
			} else {
				return false, fmt.Errorf("pod %s has no node selector %s", pod.Name, labelKey)
			}
		}
		return true, nil
	}

	BeforeEach(func() {
		tests.SkipIfNoCmd("kubectl")
		tests.BeforeTestCleanup()

		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)

		nodes := tests.GetAllSchedulableNodes(virtCli).Items
		nodeNames = make([]string, len(nodes))
		for index, node := range nodes {
			nodeNames[index] = node.Name
		}

		// select one node from result for test, first node will do
		selectedNodeName = nodes[0].Name

		Eventually(addLabelToSelectedNode,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(addNodeSelectorToDeployments,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		time.Sleep(WaitSecondsBeforeDeploymentCheck)

		Eventually(checkControlPlanePodsHaveNodeSelector,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(waitForDeploymentsToStabilize,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())
	})

	removeNodeSelectorFromDeployments := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		deploymentsClient := kubecli.KubevirtClient.AppsV1(virtCli).Deployments(tests.KubeVirtInstallNamespace)
		for _, deploymentName := range controlPlaneDeploymentNames {
			deployment, err := deploymentsClient.Get(deploymentName, metav1.GetOptions{})
			if err != nil {
				return false, err
			}
			delete(deployment.Spec.Template.Spec.NodeSelector, labelKey)
			_, err = deploymentsClient.Update(deployment)
			if err != nil {
				return false, err
			}
		}
		return true, nil
	}

	// Clean up selectedNode: Remove label and make schedulable again
	cleanUpSelectedNode := func() (bool, error) {
		virtCli, err := kubecli.GetKubevirtClient()
		tests.PanicOnError(err)
		selectedNode := getSelectedNode()
		selectedNode.Spec.Unschedulable = false
		delete(selectedNode.Labels, labelKey)
		_, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Update(selectedNode)
		if err != nil {
			return false, err
		}
		return true, nil
	}

	checkControlPlanePodsDontHaveNodeSelector := func() (bool, error) {
		podList, err := getPodList()
		tests.PanicOnError(err)
		runningControlPlanePods := getRunningReadyPods(podList, controlPlaneDeploymentNames)
		for _, pod := range runningControlPlanePods {
			if _, ok := pod.Spec.NodeSelector[labelKey]; ok {
				return false, fmt.Errorf("pod %s has still node selector %s", pod.Name, labelKey)
			}
		}
		return true, nil
	}

	AfterEach(func() {
		Eventually(removeNodeSelectorFromDeployments,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(cleanUpSelectedNode,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		time.Sleep(WaitSecondsBeforeDeploymentCheck)

		Eventually(checkControlPlanePodsDontHaveNodeSelector,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())

		Eventually(waitForDeploymentsToStabilize,
			DefaultStabilizationTimeoutInSeconds, DefaultPollIntervalInSeconds,
		).Should(BeTrue())
	})

	When("evicting pods of control plane, last eviction should fail", func() {

		test := func(podName string) {
			virtCli, err := kubecli.GetKubevirtClient()
			tests.PanicOnError(err)

			By(fmt.Sprintf("set node %s unschedulable\n", selectedNodeName))
			selectedNode := getSelectedNode()
			selectedNode.Spec.Unschedulable = true
			_, err = kubecli.KubevirtClient.CoreV1(virtCli).Nodes().Update(selectedNode)

			By(fmt.Sprintf("Try to evict all pods %s from node %s\n", podName, selectedNodeName))
			podList, err := getPodList()
			Expect(err).ToNot(HaveOccurred())
			runningPods := getRunningReadyPods(podList, []string{podName})
			for index, pod := range runningPods {
				err = virtCli.CoreV1().Pods(tests.KubeVirtInstallNamespace).Evict(&v1beta1.Eviction{ObjectMeta: metav1.ObjectMeta{Name: pod.Name}})
				if index < len(runningPods)-1 {
					Expect(err).ToNot(HaveOccurred())
				}
			}
			Expect(err).To(HaveOccurred(), "no error occurred on evict of last pod")
		}

		It("for virt-controller pods", func() { test("virt-controller") })
		It("for virt-api pods", func() { test("virt-api") })

	})

})
<|endoftext|>


<commit_before>package gorma

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

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

func TitleCase(s string) string {
	return strings.Title(s)
}

// 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 {
			pieces := strings.Split(child, ":")
			associations = associations + "List" + pieces[0] + "(context.Context, int) []" + pieces[1] + "\n"
			associations = associations + "Add" + pieces[1] + "(context.Context, int, int) (error)\n"
			associations = associations + "Delete" + pieces[1] + "(context.Context, int, int) error \n"
		}
	}
	return associations
}
func IncludeForeignKey(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#belongsto"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + child + "ID int\n"

		}
	}
	return associations
}

func PKTag(res *design.UserTypeDefinition) string {
	var tag string
	if metatag, ok := res.Metadata["github.com/bketelsen/gorma#gormpktag"]; ok {
		tag = fmt.Sprintf(" `gorm:\"%s\"`", metatag)

	} else {
		tag = "`gorm:\"primary_key\"`"
	}
	return tag
}
func Plural(s string) string {
	return inflection.Plural(s)
}
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
	Password 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)
}

// GoTypeDef returns the Go code that defines a Go type which matches the data structure
// definition (the part that comes after `type foo`).
// tabs indicates the number of tab character(s) used to tabulate the definition however the first
// line is never indented.
// jsonTags controls whether to produce json tags.
// inner indicates whether to prefix the struct of an attribute of type object with *.
func GoTypeDef(ds design.DataStructure, tabs int, jsonTags, inner bool) string {
	return godef(ds, tabs, jsonTags, inner, false)
}

// godef is the common implementation for both GoTypeDef and GoResDef.
// The only difference between the two is how the type names for fields that refer to a media type
// is generated: GoTypeDef uses the type name but GoResDef uses the underlying resource name if the
// type is a media type that corresponds to the canonical representation of a resource.
func godef(ds design.DataStructure, tabs int, jsonTags, inner, res bool) string {
	var buffer bytes.Buffer
	def := ds.Definition()
	t := def.Type
	switch actual := t.(type) {
	case design.Primitive:
		return codegen.GoTypeName(t, tabs)
	case *design.Array:
		return "[]" + godef(actual.ElemType, tabs, jsonTags, true, res)
	case *design.Hash:
		keyDef := godef(actual.KeyType, tabs, jsonTags, true, res)
		elemDef := godef(actual.ElemType, tabs, jsonTags, true, res)
		return fmt.Sprintf("map[%s]%s", keyDef, elemDef)
	case design.Object:
		if inner {
			buffer.WriteByte('*')
		}
		buffer.WriteString("struct {\n")
		keys := make([]string, len(actual))
		i := 0
		for n := range actual {
			keys[i] = n
			i++
		}
		sort.Strings(keys)
		for _, name := range keys {
			codegen.WriteTabs(&buffer, tabs+1)
			typedef := godef(actual[name], tabs+1, jsonTags, true, res)
			fname := codegen.Goify(name, true)
			var tags string
			if jsonTags {
				var omit string
				var gorm string
				if !def.IsRequired(name) {
					omit = ",omitempty"
				}
				if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#gormtag"]; ok {
					gorm = fmt.Sprintf(" gorm:\"%s\"", val)
				}
				tags = fmt.Sprintf(" `json:\"%s%s\" %s`", name, omit, gorm)
			}
			desc := actual[name].Description
			if desc != "" {
				desc = fmt.Sprintf("// %s\n", desc)
			}
			buffer.WriteString(fmt.Sprintf("%s%s %s%s\n", desc, fname, typedef, tags))
		}
		codegen.WriteTabs(&buffer, tabs)
		buffer.WriteString("}")
		return buffer.String()
	case *design.UserTypeDefinition:
		name := codegen.GoTypeName(actual, tabs)
		if actual.Type.IsObject() {
			return "*" + name
		}
		return name
	case *design.MediaTypeDefinition:
		if res && actual.Resource != nil {
			return "*" + codegen.Goify(actual.Resource.Name, true)
		}
		name := codegen.GoTypeName(actual, tabs)
		if actual.Type.IsObject() {
			return "*" + name
		}
		return name
	default:
		panic("goa bug: unknown data structure type")
	}
}

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

	start := s[0:strings.Index(s, "{")+1] + "\n  	ID        int " + PKTag(res) + "\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 didEmail, isId, 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
		}
		if isAuthboss {
			if !didEmail && !isId {
				newstrings = append(newstrings, chunk)
				didEmail = true
			}
		} 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>support gorma#sqltag and insert value into sql: in struct tags<commit_after>package gorma

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

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

func TitleCase(s string) string {
	return strings.Title(s)
}

// 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 {
			pieces := strings.Split(child, ":")
			associations = associations + "List" + pieces[0] + "(context.Context, int) []" + pieces[1] + "\n"
			associations = associations + "Add" + pieces[1] + "(context.Context, int, int) (error)\n"
			associations = associations + "Delete" + pieces[1] + "(context.Context, int, int) error \n"
		}
	}
	return associations
}
func IncludeForeignKey(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#belongsto"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + child + "ID int\n"

		}
	}
	return associations
}

func PKTag(res *design.UserTypeDefinition) string {
	var tag string
	if metatag, ok := res.Metadata["github.com/bketelsen/gorma#gormpktag"]; ok {
		tag = fmt.Sprintf(" `gorm:\"%s\"`", metatag)

	} else {
		tag = "`gorm:\"primary_key\"`"
	}
	return tag
}
func Plural(s string) string {
	return inflection.Plural(s)
}
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
	Password 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)
}

// GoTypeDef returns the Go code that defines a Go type which matches the data structure
// definition (the part that comes after `type foo`).
// tabs indicates the number of tab character(s) used to tabulate the definition however the first
// line is never indented.
// jsonTags controls whether to produce json tags.
// inner indicates whether to prefix the struct of an attribute of type object with *.
func GoTypeDef(ds design.DataStructure, tabs int, jsonTags, inner bool) string {
	return godef(ds, tabs, jsonTags, inner, false)
}

// godef is the common implementation for both GoTypeDef and GoResDef.
// The only difference between the two is how the type names for fields that refer to a media type
// is generated: GoTypeDef uses the type name but GoResDef uses the underlying resource name if the
// type is a media type that corresponds to the canonical representation of a resource.
func godef(ds design.DataStructure, tabs int, jsonTags, inner, res bool) string {
	var buffer bytes.Buffer
	def := ds.Definition()
	t := def.Type
	switch actual := t.(type) {
	case design.Primitive:
		return codegen.GoTypeName(t, tabs)
	case *design.Array:
		return "[]" + godef(actual.ElemType, tabs, jsonTags, true, res)
	case *design.Hash:
		keyDef := godef(actual.KeyType, tabs, jsonTags, true, res)
		elemDef := godef(actual.ElemType, tabs, jsonTags, true, res)
		return fmt.Sprintf("map[%s]%s", keyDef, elemDef)
	case design.Object:
		if inner {
			buffer.WriteByte('*')
		}
		buffer.WriteString("struct {\n")
		keys := make([]string, len(actual))
		i := 0
		for n := range actual {
			keys[i] = n
			i++
		}
		sort.Strings(keys)
		for _, name := range keys {
			codegen.WriteTabs(&buffer, tabs+1)
			typedef := godef(actual[name], tabs+1, jsonTags, true, res)
			fname := codegen.Goify(name, true)
			var tags string
			if jsonTags {
				var omit string
				var gorm, sql string
				if !def.IsRequired(name) {
					omit = ",omitempty"
				}
				if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#gormtag"]; ok {
					gorm = fmt.Sprintf(" gorm:\"%s\"", val)
				}
				if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#sqltag"]; ok {
					sql = fmt.Sprintf(" sql:\"%s\"", val)
				}
				tags = fmt.Sprintf(" `json:\"%s%s\"%s%s`", name, omit, gorm, sql)
			}
			desc := actual[name].Description
			if desc != "" {
				desc = fmt.Sprintf("// %s\n", desc)
			}
			buffer.WriteString(fmt.Sprintf("%s%s %s%s\n", desc, fname, typedef, tags))
		}
		codegen.WriteTabs(&buffer, tabs)
		buffer.WriteString("}")
		return buffer.String()
	case *design.UserTypeDefinition:
		name := codegen.GoTypeName(actual, tabs)
		if actual.Type.IsObject() {
			return "*" + name
		}
		return name
	case *design.MediaTypeDefinition:
		if res && actual.Resource != nil {
			return "*" + codegen.Goify(actual.Resource.Name, true)
		}
		name := codegen.GoTypeName(actual, tabs)
		if actual.Type.IsObject() {
			return "*" + name
		}
		return name
	default:
		panic("goa bug: unknown data structure type")
	}
}

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

	start := s[0:strings.Index(s, "{")+1] + "\n  	ID        int " + PKTag(res) + "\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 didEmail, isId, 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
		}
		if isAuthboss {
			if !didEmail && !isId {
				newstrings = append(newstrings, chunk)
				didEmail = true
			}
		} 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>/*
 * Datadog API for Go
 *
 * Please see the included LICENSE file for licensing information.
 *
 * Copyright 2017 by authors and contributors.
 */

package datadog

import "encoding/json"

// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool { return &v }

// GetBool is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetBool(v *bool) (b bool, ok bool) {
	if v != nil {
		return *v, true
	}

	return b, ok
}

// Int is a helper routine that allocates a new int value
// to store v and returns a pointer to it.
func Int(v int) *int { return &v }

// GetInt is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetInt(v *int) (i int, ok bool) {
	if v != nil {
		return *v, true
	}

	return i, ok
}

// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string { return &v }

// GetString is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetString(v *string) (str string, ok bool) {
	if v != nil {
		return *v, true
	}

	return str, ok
}

// JsonNumber is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func JsonNumber(v json.Number) *json.Number { return &v }

// GetJsonNumber is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetJsonNumber(v *json.Number) (num json.Number, ok bool) {
	if v != nil {
		return *v, true
	}

	return num, ok
}
<commit_msg>Be explicit in what is returned instead of using the default value.<commit_after>/*
 * Datadog API for Go
 *
 * Please see the included LICENSE file for licensing information.
 *
 * Copyright 2017 by authors and contributors.
 */

package datadog

import "encoding/json"

// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool { return &v }

// GetBool is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetBool(v *bool) (bool, bool) {
	if v != nil {
		return *v, true
	}

	return false, false
}

// Int is a helper routine that allocates a new int value
// to store v and returns a pointer to it.
func Int(v int) *int { return &v }

// GetInt is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetInt(v *int) (int, bool) {
	if v != nil {
		return *v, true
	}

	return 0, false
}

// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string { return &v }

// GetString is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetString(v *string) (string, bool) {
	if v != nil {
		return *v, true
	}

	return "", false
}

// JsonNumber is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func JsonNumber(v json.Number) *json.Number { return &v }

// GetJsonNumber is a helper routine that returns a boolean representing
// if a value was set, and if so, dereferences the pointer to it.
func GetJsonNumber(v *json.Number) (json.Number, bool) {
	if v != nil {
		return *v, true
	}

	return "", false
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"os"
	"os/exec"
	"runtime"
)

type RepoInf struct {
	RepositoryName string
	Description    string
	RepoUrl        string
	Lang           string
	Star           string
	Fork           string
}

const (
	TREND_MAX_NUM     = 25
	REPO_NAME_MAX_LEN = 35
	LANG_LEN_MAX      = 12
)

var repoInf []RepoInf
var baseUrl string = "https://github.com/trending"
var repoNameMaxLen int

var (
	lang    = flag.String("l", "all", "Select language")
	desc    = flag.Bool("d", false, "Show description")
	num     = flag.Int("n", 10, "Limit numbers")
	brows   = flag.Int("b", 0, "Show repository on browser")
	help    = flag.Bool("h", false, "Show help message")
	version = flag.Bool("v", false, "Show version")
)

func main() {
	flag.Usage = func() {
		fmt.Fprint(os.Stderr, `
usage: ghtrend <command> [options] <args>

optional arguments:
  -l    Select language.
  -d    Show description.
  -n    Limit numbers.
  -b    Show repository on browser.
  -h    Show help message.
  -v    Show version.
`)
	}
	flag.Parse()

	if *version {
		showVersion()
		os.Exit(0)
	}
	if *help {
		flag.Usage()
		os.Exit(0)
	}

	n := getNum(*num)
	url := getGithubUrl(*lang)

	repoInf = getMemory(n)

	getPage(url, n)

	if *brows > 0 && *brows <= 25 {
		browsUrl := getBrowsUrl(*brows)
		openBrowser(browsUrl)
		os.Exit(0)
	}
	showResult()
}

func getGithubUrl(lang string) string {
	if lang == "" {
		return baseUrl
	} else {
		return baseUrl + "?l=" + lang
	}
}

func getNum(num int) int {
	if num > TREND_MAX_NUM {
		num = TREND_MAX_NUM
	}
	return num
}

func getMemory(num int) []RepoInf {
	return make([]RepoInf, num)
}

func getPage(url string, num int) {
	repoNameMaxLen = REPO_NAME_MAX_LEN
	var repolen int
	doc, _ := goquery.NewDocument(url)
	doc.Find(".leaderboard-list-content").Each(func(i int, s *goquery.Selection) {
		if i < num {
			repoInf[i].RepositoryName = s.Find("a[class='repository-name']").Text()
			repoInf[i].Description = s.Find("p[class='repo-leaderboard-description']").Text()
			repoInf[i].RepoUrl = s.Find("a[class='repository-name']").Text()
			repoInf[i].Lang = s.Find("span[class='title-meta']").Text()
			repoInf[i].Star = s.Find(".repo-leaderboard-meta .repo-leaderboard-meta-item .octicon-star").Parent().Text()
			repoInf[i].Fork = s.Find(".repo-leaderboard-meta .repo-leaderboard-meta-item .octicon-git-branch").Parent().Text()

			repolen = len(repoInf[i].RepositoryName)
			if repolen > repoNameMaxLen {
				repoNameMaxLen = repolen
			}
		}

	})
}

func showResult() {
	fmt.Println("Trending " + *lang + " repositories on GitHub today")

	spaces := " "
	line := ""
	for i := 0; i < repoNameMaxLen-4; i++ {
		spaces += " "
		line += "-"
	}

	title := ""
	lines := ""
	title_starfork := "Star Fork"
	line_starfork := " ---- ----"

	if *lang == "all" {
		title = "No. Name " + spaces + "Lang         " + title_starfork
		lines = "--- -----" + line + " ------------" + line_starfork
	} else {
		title = "No. Name " + spaces + title_starfork
		lines = "--- -----" + line + line_starfork
	}

	fmt.Println(title)
	fmt.Println(lines)

	for i, rp := range repoInf {
		spaces = "  "
		for j := 0; j < repoNameMaxLen-len(rp.RepositoryName); j++ {
			spaces += " "
		}
		spaces2 := " "

		if *lang == "all" {
			for k := 0; k < LANG_LEN_MAX-len(rp.Lang); k++ {
				spaces2 += " "
			}
			fmt.Println(fmt.Sprintf("%3d", i+1) + " " + rp.RepositoryName + spaces + rp.Lang + spaces2 + fmt.Sprintf("%4s", rp.Star) + " " + fmt.Sprintf("%4s", rp.Fork))
		} else {
			fmt.Println(fmt.Sprintf("%3d", i+1) + " " + rp.RepositoryName + spaces + fmt.Sprintf("%4s", rp.Star) + " " + fmt.Sprintf("%4s", rp.Fork))
		}

		if *desc {
			fmt.Println("    " + rp.Description)
		}
	}
}

func getBrowsUrl(idx int) string {
	return "https://github.com/" + repoInf[idx-1].RepoUrl
}

func openBrowser(url string) {
	os := runtime.GOOS
	switch {
	case os == "windows":
		exec.Command("cmd", "/c", "start", url).Run()
	case os == "darwin":
		exec.Command("open", url).Run()
	case os == "linux":
		exec.Command("xdg-open", url).Run()
	}
}
<commit_msg>Change open browse to use library<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/skratchdot/open-golang/open"
	"os"
)

type RepoInf struct {
	RepositoryName string
	Description    string
	RepoUrl        string
	Lang           string
	Star           string
	Fork           string
}

const (
	TREND_MAX_NUM     = 25
	REPO_NAME_MAX_LEN = 35
	LANG_LEN_MAX      = 12
)

var repoInf []RepoInf
var baseUrl string = "https://github.com/trending"
var repoNameMaxLen int

var (
	lang    = flag.String("l", "all", "Select language")
	desc    = flag.Bool("d", false, "Show description")
	num     = flag.Int("n", 10, "Limit numbers")
	brows   = flag.Int("b", 0, "Show repository on browser")
	help    = flag.Bool("h", false, "Show help message")
	version = flag.Bool("v", false, "Show version")
)

func main() {
	flag.Usage = func() {
		fmt.Fprint(os.Stderr, `
usage: ghtrend <command> [options] <args>

optional arguments:
  -l    Select language.
  -d    Show description.
  -n    Limit numbers.
  -b    Show repository on browser.
  -h    Show help message.
  -v    Show version.
`)
	}
	flag.Parse()

	if *version {
		showVersion()
		os.Exit(0)
	}
	if *help {
		flag.Usage()
		os.Exit(0)
	}

	n := getNum(*num)
	url := getGithubUrl(*lang)

	repoInf = getMemory(n)

	getPage(url, n)

	if *brows > 0 && *brows <= 25 {
		browsUrl := getBrowsUrl(*brows)
		openBrowser(browsUrl)
		os.Exit(0)
	}
	showResult()
}

func getGithubUrl(lang string) string {
	if lang == "" {
		return baseUrl
	} else {
		return baseUrl + "?l=" + lang
	}
}

func getNum(num int) int {
	if num > TREND_MAX_NUM {
		num = TREND_MAX_NUM
	}
	return num
}

func getMemory(num int) []RepoInf {
	return make([]RepoInf, num)
}

func getPage(url string, num int) {
	repoNameMaxLen = REPO_NAME_MAX_LEN
	var repolen int
	doc, _ := goquery.NewDocument(url)
	doc.Find(".leaderboard-list-content").Each(func(i int, s *goquery.Selection) {
		if i < num {
			repoInf[i].RepositoryName = s.Find("a[class='repository-name']").Text()
			repoInf[i].Description = s.Find("p[class='repo-leaderboard-description']").Text()
			repoInf[i].RepoUrl = s.Find("a[class='repository-name']").Text()
			repoInf[i].Lang = s.Find("span[class='title-meta']").Text()
			repoInf[i].Star = s.Find(".repo-leaderboard-meta .repo-leaderboard-meta-item .octicon-star").Parent().Text()
			repoInf[i].Fork = s.Find(".repo-leaderboard-meta .repo-leaderboard-meta-item .octicon-git-branch").Parent().Text()

			repolen = len(repoInf[i].RepositoryName)
			if repolen > repoNameMaxLen {
				repoNameMaxLen = repolen
			}
		}

	})
}

func showResult() {
	fmt.Println("Trending " + *lang + " repositories on GitHub today")

	spaces := " "
	line := ""
	for i := 0; i < repoNameMaxLen-4; i++ {
		spaces += " "
		line += "-"
	}

	title := ""
	lines := ""
	title_starfork := "Star Fork"
	line_starfork := " ---- ----"

	if *lang == "all" {
		title = "No. Name " + spaces + "Lang         " + title_starfork
		lines = "--- -----" + line + " ------------" + line_starfork
	} else {
		title = "No. Name " + spaces + title_starfork
		lines = "--- -----" + line + line_starfork
	}

	fmt.Println(title)
	fmt.Println(lines)

	for i, rp := range repoInf {
		spaces = "  "
		for j := 0; j < repoNameMaxLen-len(rp.RepositoryName); j++ {
			spaces += " "
		}
		spaces2 := " "

		if *lang == "all" {
			for k := 0; k < LANG_LEN_MAX-len(rp.Lang); k++ {
				spaces2 += " "
			}
			fmt.Println(fmt.Sprintf("%3d", i+1) + " " + rp.RepositoryName + spaces + rp.Lang + spaces2 + fmt.Sprintf("%4s", rp.Star) + " " + fmt.Sprintf("%4s", rp.Fork))
		} else {
			fmt.Println(fmt.Sprintf("%3d", i+1) + " " + rp.RepositoryName + spaces + fmt.Sprintf("%4s", rp.Star) + " " + fmt.Sprintf("%4s", rp.Fork))
		}

		if *desc {
			fmt.Println("    " + rp.Description)
		}
	}
}

func getBrowsUrl(idx int) string {
	return "https://github.com/" + repoInf[idx-1].RepoUrl
}

func openBrowser(url string) {
	open.Run(url)
}
<|endoftext|>


<commit_before>package pivotaltracker

import (
	// Stdlib
	"fmt"
	"sort"
	"strings"

	// Internal
	"github.com/salsaflow/salsaflow/config/loader"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/prompt"

	// Vendor
	"github.com/bgentry/speakeasy"
	"gopkg.in/salsita/go-pivotaltracker.v1/v5/pivotal"
)

// Configuration ===============================================================

type moduleConfig struct {
	ProjectId        int
	ComponentLabel   string
	PointMeLabel     string
	ReviewedLabel    string
	SkipReviewLabel  string
	TestedLabel      string
	SkipTestingLabel string
	SkipCheckLabels  []string
	UserToken        string
}

func loadConfig() (*moduleConfig, error) {
	// Load the config.
	spec := newConfigSpec()
	if err := loader.LoadConfig(spec); err != nil {
		return nil, err
	}

	// Assemble the config object.
	var (
		local  = spec.local
		global = spec.global
	)
	return &moduleConfig{
		ProjectId:        local.ProjectId,
		ComponentLabel:   *local.ComponentLabel,
		PointMeLabel:     local.Labels.PointMeLabel,
		ReviewedLabel:    local.Labels.ReviewedLabel,
		SkipReviewLabel:  local.Labels.SkipReviewLabel,
		TestedLabel:      local.Labels.TestedLabel,
		SkipTestingLabel: local.Labels.SkipTestingLabel,
		SkipCheckLabels:  local.Labels.SkipCheckLabels,
		UserToken:        global.UserToken,
	}, nil
}

// Configuration spec ----------------------------------------------------------

type configSpec struct {
	global *GlobalConfig
	local  *LocalConfig
}

func newConfigSpec() *configSpec {
	return &configSpec{}
}

// ConfigKey is a part of loader.ConfigSpec
func (spec *configSpec) ConfigKey() string {
	return ModuleId
}

// ModuleKind is a part of loader.ModuleConfigSpec
func (spec *configSpec) ModuleKind() loader.ModuleKind {
	return loader.ModuleKindIssueTracking
}

// GlobalConfig is a part of loader.ConfigSpec
func (spec *configSpec) GlobalConfig() loader.ConfigContainer {
	spec.global = &GlobalConfig{}
	return spec.global
}

// LocalConfig is a part of loader.ConfigSpec
func (spec *configSpec) LocalConfig() loader.ConfigContainer {
	spec.local = &LocalConfig{spec: spec}
	return spec.local
}

// Global configuration --------------------------------------------------------

// GlobalConfig implements loader.ConfigContainer
type GlobalConfig struct {
	UserToken string `json:"token"`
}

// PromptUserForConfig is a part of loader.ConfigContainer
func (global *GlobalConfig) PromptUserForConfig() error {
	token, err := speakeasy.Ask("Insert your personal Pivotal Tracker token: ")
	if err != nil {
		return err
	}
	if token == "" {
		prompt.PanicCancel()
	}

	global.UserToken = token
	return nil
}

// Local configuration ---------------------------------------------------------

const (
	DefaultPointMeLabel     = "point me"
	DefaultReviewedLabel    = "reviewed"
	DefaultSkipReviewLabel  = "no review"
	DefaultTestedLabel      = "qa+"
	DefaultSkipTestingLabel = "no qa"
)

var DefaultSkipCheckLabels = []string{"dupe", "wontfix"}

// LocalConfig implements loader.ConfigContainer interface.
type LocalConfig struct {
	spec *configSpec

	ProjectId      int     `json:"project_id"`
	ComponentLabel *string `json:"component_label"`
	Labels         struct {
		PointMeLabel     string   `json:"point_me"`
		ReviewedLabel    string   `json:"reviewed"`
		SkipReviewLabel  string   `json:"skip_review"`
		TestedLabel      string   `json:"tested"`
		SkipTestingLabel string   `json:"skip_testing"`
		SkipCheckLabels  []string `json:"skip_release_check_labels"`
	} `json:"workflow_labels"`
}

// PromptUserForConfig is a part of loader.ConfigContainer interface.
func (local *LocalConfig) PromptUserForConfig() error {
	c := LocalConfig{spec: local.spec}

	// Prompt for the project ID.
	task := "Fetch available Pivotal Tracker projects"
	log.Run(task)

	client := pivotal.NewClient(local.spec.global.UserToken)

	projects, _, err := client.Projects.List()
	if err != nil {
		return errs.NewError(task, err)
	}
	sort.Sort(ptProjects(projects))

	fmt.Println()
	fmt.Println("Available Pivotal Tracker projects:")
	fmt.Println()
	for i, project := range projects {
		fmt.Printf("  [%v] %v\n", i+1, project.Name)
	}
	fmt.Println()
	fmt.Println("Choose the project to associate this repository with.")
	index, err := prompt.PromptIndex("Project number: ", 1, len(projects))
	if err != nil {
		if err == prompt.ErrCanceled {
			prompt.PanicCancel()
		}

		return err
	}
	fmt.Println()

	c.ProjectId = projects[index-1].Id

	// Prompt for the labels.
	promptForLabel := func(dst *string, labelName, defaultValue string) {
		if err != nil {
			return
		}
		question := fmt.Sprintf("%v label", labelName)
		var label string
		label, err = prompt.PromptDefault(question, defaultValue)
		if err == nil {
			*dst = label
		}
	}

	var componentLabel string
	promptForLabel(&componentLabel, "Component", "")
	c.ComponentLabel = &componentLabel

	promptForLabel(&c.Labels.PointMeLabel, "Point me", DefaultPointMeLabel)
	promptForLabel(&c.Labels.ReviewedLabel, "Reviewed", DefaultReviewedLabel)
	promptForLabel(&c.Labels.SkipReviewLabel, "Skip review", DefaultSkipReviewLabel)
	promptForLabel(&c.Labels.TestedLabel, "Testing passed", DefaultTestedLabel)
	promptForLabel(&c.Labels.SkipTestingLabel, "Skip testing", DefaultSkipTestingLabel)
	if err != nil {
		return err
	}

	// Prompt for the skip check labels.
	skipCheckLabelsString, err := prompt.Prompt(fmt.Sprintf(
		"Skip check labels (comma-separated) (%v added automatically): ",
		strings.Join(DefaultSkipCheckLabels, ", ")))
	if err != nil {
		if err != prompt.ErrCanceled {
			return err
		}
	}
	c.Labels.SkipCheckLabels = DefaultSkipCheckLabels
	skipCheckLabels := strings.SplitAfter(skipCheckLabelsString, ",")
	for _, label := range skipCheckLabels {
		if label != "" {
			c.Labels.SkipCheckLabels = append(c.Labels.SkipCheckLabels, label)
		}
	}

	*local = c
	return nil
}

// Implement sort.Interface to sort projects alphabetically.
type ptProjects []*pivotal.Project

func (ps ptProjects) Len() int {
	return len(ps)
}

func (ps ptProjects) Less(i, j int) bool {
	return ps[i].Name < ps[j].Name
}

func (ps ptProjects) Swap(i, j int) {
	ps[i], ps[j] = ps[j], ps[i]
}
<commit_msg>pivotaltracker: Use prompt.Dialog for GlobalConfig<commit_after>package pivotaltracker

import (
	// Stdlib
	"fmt"
	"sort"
	"strings"

	// Internal
	"github.com/salsaflow/salsaflow/config/loader"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/prompt"

	// Vendor
	"gopkg.in/salsita/go-pivotaltracker.v1/v5/pivotal"
)

// Configuration ===============================================================

type moduleConfig struct {
	ProjectId        int
	ComponentLabel   string
	PointMeLabel     string
	ReviewedLabel    string
	SkipReviewLabel  string
	TestedLabel      string
	SkipTestingLabel string
	SkipCheckLabels  []string
	UserToken        string
}

func loadConfig() (*moduleConfig, error) {
	// Load the config.
	spec := newConfigSpec()
	if err := loader.LoadConfig(spec); err != nil {
		return nil, err
	}

	// Assemble the config object.
	var (
		local  = spec.local
		global = spec.global
	)
	return &moduleConfig{
		ProjectId:        local.ProjectId,
		ComponentLabel:   *local.ComponentLabel,
		PointMeLabel:     local.Labels.PointMeLabel,
		ReviewedLabel:    local.Labels.ReviewedLabel,
		SkipReviewLabel:  local.Labels.SkipReviewLabel,
		TestedLabel:      local.Labels.TestedLabel,
		SkipTestingLabel: local.Labels.SkipTestingLabel,
		SkipCheckLabels:  local.Labels.SkipCheckLabels,
		UserToken:        global.UserToken,
	}, nil
}

// Configuration spec ----------------------------------------------------------

type configSpec struct {
	global *GlobalConfig
	local  *LocalConfig
}

func newConfigSpec() *configSpec {
	return &configSpec{}
}

// ConfigKey is a part of loader.ConfigSpec
func (spec *configSpec) ConfigKey() string {
	return ModuleId
}

// ModuleKind is a part of loader.ModuleConfigSpec
func (spec *configSpec) ModuleKind() loader.ModuleKind {
	return loader.ModuleKindIssueTracking
}

// GlobalConfig is a part of loader.ConfigSpec
func (spec *configSpec) GlobalConfig() loader.ConfigContainer {
	spec.global = &GlobalConfig{}
	return spec.global
}

// LocalConfig is a part of loader.ConfigSpec
func (spec *configSpec) LocalConfig() loader.ConfigContainer {
	spec.local = &LocalConfig{spec: spec}
	return spec.local
}

// Global configuration --------------------------------------------------------

// GlobalConfig implements loader.ConfigContainer
type GlobalConfig struct {
	UserToken string `prompt:"personal Pivotal Tracker token" secret:"true" json:"token"`
}

// PromptUserForConfig is a part of loader.ConfigContainer
func (global *GlobalConfig) PromptUserForConfig() error {
	var c GlobalConfig
	if err := prompt.Dialog(&c, "Insert your"); err != nil {
		return err
	}

	*global = c
	return nil
}

// Local configuration ---------------------------------------------------------

const (
	DefaultPointMeLabel     = "point me"
	DefaultReviewedLabel    = "reviewed"
	DefaultSkipReviewLabel  = "no review"
	DefaultTestedLabel      = "qa+"
	DefaultSkipTestingLabel = "no qa"
)

var DefaultSkipCheckLabels = []string{"dupe", "wontfix"}

// LocalConfig implements loader.ConfigContainer interface.
type LocalConfig struct {
	spec *configSpec

	ProjectId      int     `json:"project_id"`
	ComponentLabel *string `json:"component_label"`
	Labels         struct {
		PointMeLabel     string   `json:"point_me"`
		ReviewedLabel    string   `json:"reviewed"`
		SkipReviewLabel  string   `json:"skip_review"`
		TestedLabel      string   `json:"tested"`
		SkipTestingLabel string   `json:"skip_testing"`
		SkipCheckLabels  []string `json:"skip_release_check_labels"`
	} `json:"workflow_labels"`
}

// PromptUserForConfig is a part of loader.ConfigContainer interface.
func (local *LocalConfig) PromptUserForConfig() error {
	c := LocalConfig{spec: local.spec}

	// Prompt for the project ID.
	task := "Fetch available Pivotal Tracker projects"
	log.Run(task)

	client := pivotal.NewClient(local.spec.global.UserToken)

	projects, _, err := client.Projects.List()
	if err != nil {
		return errs.NewError(task, err)
	}
	sort.Sort(ptProjects(projects))

	fmt.Println()
	fmt.Println("Available Pivotal Tracker projects:")
	fmt.Println()
	for i, project := range projects {
		fmt.Printf("  [%v] %v\n", i+1, project.Name)
	}
	fmt.Println()
	fmt.Println("Choose the project to associate this repository with.")
	index, err := prompt.PromptIndex("Project number: ", 1, len(projects))
	if err != nil {
		if err == prompt.ErrCanceled {
			prompt.PanicCancel()
		}

		return err
	}
	fmt.Println()

	c.ProjectId = projects[index-1].Id

	// Prompt for the labels.
	promptForLabel := func(dst *string, labelName, defaultValue string) {
		if err != nil {
			return
		}
		question := fmt.Sprintf("%v label", labelName)
		var label string
		label, err = prompt.PromptDefault(question, defaultValue)
		if err == nil {
			*dst = label
		}
	}

	var componentLabel string
	promptForLabel(&componentLabel, "Component", "")
	c.ComponentLabel = &componentLabel

	promptForLabel(&c.Labels.PointMeLabel, "Point me", DefaultPointMeLabel)
	promptForLabel(&c.Labels.ReviewedLabel, "Reviewed", DefaultReviewedLabel)
	promptForLabel(&c.Labels.SkipReviewLabel, "Skip review", DefaultSkipReviewLabel)
	promptForLabel(&c.Labels.TestedLabel, "Testing passed", DefaultTestedLabel)
	promptForLabel(&c.Labels.SkipTestingLabel, "Skip testing", DefaultSkipTestingLabel)
	if err != nil {
		return err
	}

	// Prompt for the skip check labels.
	skipCheckLabelsString, err := prompt.Prompt(fmt.Sprintf(
		"Skip check labels (comma-separated) (%v added automatically): ",
		strings.Join(DefaultSkipCheckLabels, ", ")))
	if err != nil {
		if err != prompt.ErrCanceled {
			return err
		}
	}
	c.Labels.SkipCheckLabels = DefaultSkipCheckLabels
	skipCheckLabels := strings.SplitAfter(skipCheckLabelsString, ",")
	for _, label := range skipCheckLabels {
		if label != "" {
			c.Labels.SkipCheckLabels = append(c.Labels.SkipCheckLabels, label)
		}
	}

	*local = c
	return nil
}

// Implement sort.Interface to sort projects alphabetically.
type ptProjects []*pivotal.Project

func (ps ptProjects) Len() int {
	return len(ps)
}

func (ps ptProjects) Less(i, j int) bool {
	return ps[i].Name < ps[j].Name
}

func (ps ptProjects) Swap(i, j int) {
	ps[i], ps[j] = ps[j], ps[i]
}
<|endoftext|>