<commit_before>// This file is part of Ringot.
/*
Copyright 2016 pinkienort <cantabilehisa@gmail.com>

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

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

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

package main

import termbox "github.com/nsf/termbox-go"

type KeybindMode uint8

const (
	KEYBIND_MODE_COMMON = iota
	KEYBIND_MODE_CONVERSATION
	KEYBIND_MODE_HOME_TIMELINE
	KEYBIND_MODE_INPUT
	KEYBIND_MODE_CONFIRM
	KEYBIND_MODE_MENTION_VIEW
	KEYBIND_MODE_USER_TIMELINE
	KEYBIND_MODE_USER_FAVORITE
	KEYBIND_MODE_LIST_VIEW
)

type Action uint8
type keybind struct {
	Mod    termbox.Modifier
	Key    termbox.Key
	Ch     rune
	Action Action
}

const ( /* common event action list */
	ACTION_LIKE_TWEET = iota + 1
	ACTION_MENTION
	ACTION_RETWEET
	ACTION_OPEN_IMAGES
	ACTION_NEXT_TWEET
	ACTION_PREVIOUS_TWEET
	ACTION_PAGE_DOWN
	ACTION_PAGE_UP
	ACTION_MOVE_TO_TOP_TWEET
	ACTION_MOVE_TO_BOTTOM_TWEET
	ACTION_TURN_INPUT_MODE
	ACTION_TURN_COMMAND_MODE
	ACTION_TURN_HOME_TIMELINE_MODE
	ACTION_TURN_CONVERSATION_VIEW_MODE
	ACTION_TURN_MENTION_VIEW_MODE
	ACTION_TURN_USER_TIMELINE_MODE
	ACTION_QUIT
	ACTION_OPEN_URL
	ACTION_SHOW_HELP
)
const ( /* home timeline action list */
	ACTION_LOAD_PREVIOUSE_TWEETS = iota + 1
	ACTION_LOAD_NEW_TWEETS
)
const ( /* input mode action list */
	ACTION_MOVE_LEFT = iota + 1
	ACTION_MOVE_RIGHT
	ACTION_MOVE_UP
	ACTION_MOVE_DOWN
	ACTION_INSERT_SPACE
	ACTION_EXIT_INPUT_MODE
	ACTION_DELETE_RUNE
	ACTION_MOVE_LINE_TOP
	ACTION_MOVE_LINE_BOTTOM
	ACTION_TURN_CONFIRM_MODE
	ACTION_INSERT_NEW_LINE
	ACTION_TEXT_CUT
	ACTION_TEXT_PASTE
)
const ( /* confirm mode action list */
	ACTION_CANCEL_SUBMIT = iota + 1
	ACTION_SUBMIT_TWEET
)
const ( /* mention view mode action list */
	ACTION_LOAD_PREVIOUSE_MENTIONS = iota + 1
	ACTION_LOAD_NEW_MENTIONS
)
const ( /* conversation mode action list */
	ACTION_EXIT_CONVERSATION_MODE = iota + 1
)
const ( /* user timeline mode action list */
	ACTION_LOAD_PREVIOUSE_USER_TWEETS = iota + 1
	ACTION_LOAD_NEW_USER_TWEETS
)
const ( /* list mode actin list */
	ACTION_LOAD_PREVIOUSE_LIST = iota + 1
	ACTION_LOAD_NEW_LIST
)

const NO_MOD = 0
const NO_KEY = 0
const NO_CH = 0
const NO_ACTION = 0

/* keybind list */
var commonKeybindList = []keybind{
	{NO_MOD, termbox.KeyCtrlS, NO_CH, ACTION_TURN_INPUT_MODE},
	{NO_MOD, termbox.KeyCtrlW, NO_CH, ACTION_MENTION},
	{NO_MOD, termbox.KeyCtrlF, NO_CH, ACTION_LIKE_TWEET},
	{NO_MOD, termbox.KeyCtrlV, NO_CH, ACTION_RETWEET},
	{NO_MOD, termbox.KeyCtrlO, NO_CH, ACTION_OPEN_URL},
	{NO_MOD, termbox.KeyCtrlP, NO_CH, ACTION_OPEN_IMAGES},

	{NO_MOD, termbox.KeyCtrlZ, NO_CH, ACTION_TURN_HOME_TIMELINE_MODE},
	{NO_MOD, termbox.KeyCtrlX, NO_CH, ACTION_TURN_MENTION_VIEW_MODE},
	{NO_MOD, termbox.KeyCtrlD, NO_CH, ACTION_TURN_USER_TIMELINE_MODE},
	{NO_MOD, termbox.KeyArrowRight, NO_CH, ACTION_TURN_CONVERSATION_VIEW_MODE},
	{termbox.ModAlt, NO_KEY, 'x', ACTION_TURN_COMMAND_MODE}, /* TODO: need ModAlt field */
	{NO_MOD, termbox.KeyCtrlQ, NO_CH, ACTION_QUIT},

	{NO_MOD, termbox.KeyArrowUp, NO_CH, ACTION_NEXT_TWEET},
	{NO_MOD, termbox.KeyArrowDown, NO_CH, ACTION_PREVIOUS_TWEET},
	{NO_MOD, termbox.KeyHome, NO_CH, ACTION_MOVE_TO_TOP_TWEET},
	{NO_MOD, termbox.KeyPgup, NO_CH, ACTION_MOVE_TO_TOP_TWEET},
	{NO_MOD, termbox.KeyEnd, NO_CH, ACTION_MOVE_TO_BOTTOM_TWEET},
	{NO_MOD, termbox.KeyPgdn, NO_CH, ACTION_MOVE_TO_BOTTOM_TWEET},
}

var homeTimelineKeybindList = []keybind{
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_TWEETS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_TWEETS},
}

var inputModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyArrowLeft, NO_CH, ACTION_MOVE_LEFT},
	{NO_MOD, termbox.KeyArrowRight, NO_CH, ACTION_MOVE_RIGHT},
	{NO_MOD, termbox.KeyArrowUp, NO_CH, ACTION_MOVE_UP},
	{NO_MOD, termbox.KeyArrowDown, NO_CH, ACTION_MOVE_DOWN},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_INSERT_SPACE},
	{NO_MOD, termbox.KeyEsc, NO_CH, ACTION_EXIT_INPUT_MODE},
	{NO_MOD, termbox.KeyCtrlG, NO_CH, ACTION_EXIT_INPUT_MODE},
	{NO_MOD, termbox.KeyBackspace, NO_CH, ACTION_DELETE_RUNE},
	{NO_MOD, termbox.KeyBackspace2, NO_CH, ACTION_DELETE_RUNE},
	{NO_MOD, termbox.KeyCtrlA, NO_CH, ACTION_MOVE_LINE_TOP},
	{NO_MOD, termbox.KeyCtrlE, NO_CH, ACTION_MOVE_LINE_BOTTOM},
	{NO_MOD, termbox.KeyCtrlJ, NO_CH, ACTION_TURN_CONFIRM_MODE},
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_INSERT_NEW_LINE},
	{NO_MOD, termbox.KeyCtrlW, NO_CH, ACTION_TEXT_CUT},
	{NO_MOD, termbox.KeyCtrlY, NO_CH, ACTION_TEXT_PASTE},
}

var confirmModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEsc, NO_CH, ACTION_CANCEL_SUBMIT},
	{NO_MOD, termbox.KeyCtrlG, NO_CH, ACTION_CANCEL_SUBMIT},
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_SUBMIT_TWEET},
}

var mentionViewModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_MENTIONS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_MENTIONS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_MENTIONS},
}

var conversationModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyArrowLeft, NO_CH, ACTION_EXIT_CONVERSATION_MODE},
}

var userTimelineModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_USER_TWEETS},
}

var favoriteModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_USER_TWEETS},
}

var listModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_LIST},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_LIST},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_LIST},
}

func (view *view) handleAction(ev termbox.Event, mode KeybindMode) Action {
	var keybindList []keybind
	switch mode {
	case KEYBIND_MODE_COMMON:
		keybindList = commonKeybindList
	case KEYBIND_MODE_CONVERSATION:
		keybindList = conversationModeKeybindList
	case KEYBIND_MODE_HOME_TIMELINE:
		keybindList = homeTimelineKeybindList
	case KEYBIND_MODE_INPUT:
		keybindList = inputModeKeybindList
	case KEYBIND_MODE_CONFIRM:
		keybindList = confirmModeKeybindList
	case KEYBIND_MODE_MENTION_VIEW:
		keybindList = mentionViewModeKeybindList
	case KEYBIND_MODE_USER_TIMELINE:
		keybindList = userTimelineModeKeybindList
	case KEYBIND_MODE_USER_FAVORITE:
		keybindList = favoriteModeKeybindList
	case KEYBIND_MODE_LIST_VIEW:
		keybindList = listModeKeybindList
	}
	for i := 0; i < len(keybindList); i++ {
		if (ev.Mod == keybindList[i].Mod) &&
			(ev.Key == keybindList[i].Key) &&
			(ev.Ch == keybindList[i].Ch) {
			return keybindList[i].Action
		}
	}
	return NO_ACTION
}
<commit_msg>Fix that enter reload was missing on Timeline View<commit_after>// This file is part of Ringot.
/*
Copyright 2016 pinkienort <cantabilehisa@gmail.com>

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

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

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

package main

import termbox "github.com/nsf/termbox-go"

type KeybindMode uint8

const (
	KEYBIND_MODE_COMMON = iota
	KEYBIND_MODE_CONVERSATION
	KEYBIND_MODE_HOME_TIMELINE
	KEYBIND_MODE_INPUT
	KEYBIND_MODE_CONFIRM
	KEYBIND_MODE_MENTION_VIEW
	KEYBIND_MODE_USER_TIMELINE
	KEYBIND_MODE_USER_FAVORITE
	KEYBIND_MODE_LIST_VIEW
)

type Action uint8
type keybind struct {
	Mod    termbox.Modifier
	Key    termbox.Key
	Ch     rune
	Action Action
}

const ( /* common event action list */
	ACTION_LIKE_TWEET = iota + 1
	ACTION_MENTION
	ACTION_RETWEET
	ACTION_OPEN_IMAGES
	ACTION_NEXT_TWEET
	ACTION_PREVIOUS_TWEET
	ACTION_PAGE_DOWN
	ACTION_PAGE_UP
	ACTION_MOVE_TO_TOP_TWEET
	ACTION_MOVE_TO_BOTTOM_TWEET
	ACTION_TURN_INPUT_MODE
	ACTION_TURN_COMMAND_MODE
	ACTION_TURN_HOME_TIMELINE_MODE
	ACTION_TURN_CONVERSATION_VIEW_MODE
	ACTION_TURN_MENTION_VIEW_MODE
	ACTION_TURN_USER_TIMELINE_MODE
	ACTION_QUIT
	ACTION_OPEN_URL
	ACTION_SHOW_HELP
)
const ( /* home timeline action list */
	ACTION_LOAD_PREVIOUSE_TWEETS = iota + 1
	ACTION_LOAD_NEW_TWEETS
)
const ( /* input mode action list */
	ACTION_MOVE_LEFT = iota + 1
	ACTION_MOVE_RIGHT
	ACTION_MOVE_UP
	ACTION_MOVE_DOWN
	ACTION_INSERT_SPACE
	ACTION_EXIT_INPUT_MODE
	ACTION_DELETE_RUNE
	ACTION_MOVE_LINE_TOP
	ACTION_MOVE_LINE_BOTTOM
	ACTION_TURN_CONFIRM_MODE
	ACTION_INSERT_NEW_LINE
	ACTION_TEXT_CUT
	ACTION_TEXT_PASTE
)
const ( /* confirm mode action list */
	ACTION_CANCEL_SUBMIT = iota + 1
	ACTION_SUBMIT_TWEET
)
const ( /* mention view mode action list */
	ACTION_LOAD_PREVIOUSE_MENTIONS = iota + 1
	ACTION_LOAD_NEW_MENTIONS
)
const ( /* conversation mode action list */
	ACTION_EXIT_CONVERSATION_MODE = iota + 1
)
const ( /* user timeline mode action list */
	ACTION_LOAD_PREVIOUSE_USER_TWEETS = iota + 1
	ACTION_LOAD_NEW_USER_TWEETS
)
const ( /* list mode actin list */
	ACTION_LOAD_PREVIOUSE_LIST = iota + 1
	ACTION_LOAD_NEW_LIST
)

const NO_MOD = 0
const NO_KEY = 0
const NO_CH = 0
const NO_ACTION = 0

/* keybind list */
var commonKeybindList = []keybind{
	{NO_MOD, termbox.KeyCtrlS, NO_CH, ACTION_TURN_INPUT_MODE},
	{NO_MOD, termbox.KeyCtrlW, NO_CH, ACTION_MENTION},
	{NO_MOD, termbox.KeyCtrlF, NO_CH, ACTION_LIKE_TWEET},
	{NO_MOD, termbox.KeyCtrlV, NO_CH, ACTION_RETWEET},
	{NO_MOD, termbox.KeyCtrlO, NO_CH, ACTION_OPEN_URL},
	{NO_MOD, termbox.KeyCtrlP, NO_CH, ACTION_OPEN_IMAGES},

	{NO_MOD, termbox.KeyCtrlZ, NO_CH, ACTION_TURN_HOME_TIMELINE_MODE},
	{NO_MOD, termbox.KeyCtrlX, NO_CH, ACTION_TURN_MENTION_VIEW_MODE},
	{NO_MOD, termbox.KeyCtrlD, NO_CH, ACTION_TURN_USER_TIMELINE_MODE},
	{NO_MOD, termbox.KeyArrowRight, NO_CH, ACTION_TURN_CONVERSATION_VIEW_MODE},
	{termbox.ModAlt, NO_KEY, 'x', ACTION_TURN_COMMAND_MODE}, /* TODO: need ModAlt field */
	{NO_MOD, termbox.KeyCtrlQ, NO_CH, ACTION_QUIT},

	{NO_MOD, termbox.KeyArrowUp, NO_CH, ACTION_NEXT_TWEET},
	{NO_MOD, termbox.KeyArrowDown, NO_CH, ACTION_PREVIOUS_TWEET},
	{NO_MOD, termbox.KeyHome, NO_CH, ACTION_MOVE_TO_TOP_TWEET},
	{NO_MOD, termbox.KeyPgup, NO_CH, ACTION_MOVE_TO_TOP_TWEET},
	{NO_MOD, termbox.KeyEnd, NO_CH, ACTION_MOVE_TO_BOTTOM_TWEET},
	{NO_MOD, termbox.KeyPgdn, NO_CH, ACTION_MOVE_TO_BOTTOM_TWEET},
}

var homeTimelineKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_MENTIONS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_TWEETS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_TWEETS},
}

var inputModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyArrowLeft, NO_CH, ACTION_MOVE_LEFT},
	{NO_MOD, termbox.KeyArrowRight, NO_CH, ACTION_MOVE_RIGHT},
	{NO_MOD, termbox.KeyArrowUp, NO_CH, ACTION_MOVE_UP},
	{NO_MOD, termbox.KeyArrowDown, NO_CH, ACTION_MOVE_DOWN},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_INSERT_SPACE},
	{NO_MOD, termbox.KeyEsc, NO_CH, ACTION_EXIT_INPUT_MODE},
	{NO_MOD, termbox.KeyCtrlG, NO_CH, ACTION_EXIT_INPUT_MODE},
	{NO_MOD, termbox.KeyBackspace, NO_CH, ACTION_DELETE_RUNE},
	{NO_MOD, termbox.KeyBackspace2, NO_CH, ACTION_DELETE_RUNE},
	{NO_MOD, termbox.KeyCtrlA, NO_CH, ACTION_MOVE_LINE_TOP},
	{NO_MOD, termbox.KeyCtrlE, NO_CH, ACTION_MOVE_LINE_BOTTOM},
	{NO_MOD, termbox.KeyCtrlJ, NO_CH, ACTION_TURN_CONFIRM_MODE},
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_INSERT_NEW_LINE},
	{NO_MOD, termbox.KeyCtrlW, NO_CH, ACTION_TEXT_CUT},
	{NO_MOD, termbox.KeyCtrlY, NO_CH, ACTION_TEXT_PASTE},
}

var confirmModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEsc, NO_CH, ACTION_CANCEL_SUBMIT},
	{NO_MOD, termbox.KeyCtrlG, NO_CH, ACTION_CANCEL_SUBMIT},
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_SUBMIT_TWEET},
}

var mentionViewModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_MENTIONS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_MENTIONS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_MENTIONS},
}

var conversationModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyArrowLeft, NO_CH, ACTION_EXIT_CONVERSATION_MODE},
}

var userTimelineModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_USER_TWEETS},
}

var favoriteModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_USER_TWEETS},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_USER_TWEETS},
}

var listModeKeybindList = []keybind{
	{NO_MOD, termbox.KeyEnter, NO_CH, ACTION_LOAD_PREVIOUSE_LIST},
	{NO_MOD, termbox.KeySpace, NO_CH, ACTION_LOAD_PREVIOUSE_LIST},
	{NO_MOD, termbox.KeyCtrlR, NO_CH, ACTION_LOAD_NEW_LIST},
}

func (view *view) handleAction(ev termbox.Event, mode KeybindMode) Action {
	var keybindList []keybind
	switch mode {
	case KEYBIND_MODE_COMMON:
		keybindList = commonKeybindList
	case KEYBIND_MODE_CONVERSATION:
		keybindList = conversationModeKeybindList
	case KEYBIND_MODE_HOME_TIMELINE:
		keybindList = homeTimelineKeybindList
	case KEYBIND_MODE_INPUT:
		keybindList = inputModeKeybindList
	case KEYBIND_MODE_CONFIRM:
		keybindList = confirmModeKeybindList
	case KEYBIND_MODE_MENTION_VIEW:
		keybindList = mentionViewModeKeybindList
	case KEYBIND_MODE_USER_TIMELINE:
		keybindList = userTimelineModeKeybindList
	case KEYBIND_MODE_USER_FAVORITE:
		keybindList = favoriteModeKeybindList
	case KEYBIND_MODE_LIST_VIEW:
		keybindList = listModeKeybindList
	}
	for i := 0; i < len(keybindList); i++ {
		if (ev.Mod == keybindList[i].Mod) &&
			(ev.Key == keybindList[i].Key) &&
			(ev.Ch == keybindList[i].Ch) {
			return keybindList[i].Action
		}
	}
	return NO_ACTION
}
<|endoftext|>


<commit_before>package kickbox

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

// Client holds the HTTP client for interacting with the API, and the associated
// API key
type Client struct {
	apiKey string
	http   http.Client
}

// NewClient returns an instance of a Kickbox API Client
func NewClient(apiKey string) *Client {
	client := &Client{
		apiKey: apiKey,
		http:   http.Client{},
	}

	// Set the default timeout to 3 seconds
	client.SetTimeout(time.Second * 3)
	return client
}

// SetTimeout configures the request timeout value (includes connecting, waiting for a response, and reading the response)
func (c *Client) SetTimeout(time time.Duration) {
	c.http.Timeout = time
}

// Verify the given email address using Kickbox.io
func (c Client) Verify(address string) (*Result, error) {
	return c.verify(KickboxResultBuilder{}, c.url(address))
}

// Build the API endpoint given the email address and API key
func (c Client) url(address string) string {
	return fmt.Sprintf("https://api.kickbox.io/v2/verify?email=%s&apikey=%s", url.QueryEscape(address), url.QueryEscape(c.apiKey))
}

// Request and read the response of the HTTP request, returning a new Result struct
func (c Client) verify(rb ResultBuilder, url string) (*Result, error) {
	// Send our API request
	response, err := c.http.Get(url)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	// Did we get a HTTP 200?
	if response.StatusCode != 200 {
		msg := fmt.Sprintf("Kickbox API returned HTTP %d", response.StatusCode)
		return nil, errors.New(msg)
	}

	// Read the response
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	// Build our Result struct
	result, err := rb.NewResult(body)
	if err != nil {
		return nil, err
	}

	return result, nil
}
<commit_msg>Add the ability to customize the http Client<commit_after>package kickbox

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

// Client holds the HTTP client for interacting with the API, and the associated
// API key
type Client struct {
	apiKey string
	http   *http.Client
}

// NewClient returns an instance of a Kickbox API Client
func NewClient(apiKey string) *Client {
	client := &Client{
		apiKey: apiKey,
		http:   &http.Client{},
	}

	// Set the default timeout to 3 seconds
	client.SetTimeout(time.Second * 3)
	return client
}

// SetTimeout configures the request timeout value (includes connecting, waiting for a response, and reading the response)
func (c *Client) SetTimeout(time time.Duration) {
	c.http.Timeout = time
}

// SetHttpClient configures the request to use a custom http client.
func (c *Client) SetHttpClient(client *http.Client) {
	c.http = client
}

// Verify the given email address using Kickbox.io
func (c Client) Verify(address string) (*Result, error) {
	return c.verify(KickboxResultBuilder{}, c.url(address))
}

// Build the API endpoint given the email address and API key
func (c Client) url(address string) string {
	return fmt.Sprintf("https://api.kickbox.io/v2/verify?email=%s&apikey=%s", url.QueryEscape(address), url.QueryEscape(c.apiKey))
}

// Request and read the response of the HTTP request, returning a new Result struct
func (c Client) verify(rb ResultBuilder, url string) (*Result, error) {
	// Send our API request
	response, err := c.http.Get(url)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	// Did we get a HTTP 200?
	if response.StatusCode != 200 {
		msg := fmt.Sprintf("Kickbox API returned HTTP %d", response.StatusCode)
		return nil, errors.New(msg)
	}

	// Read the response
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	// Build our Result struct
	result, err := rb.NewResult(body)
	if err != nil {
		return nil, err
	}

	return result, nil
}
<|endoftext|>


<commit_before>package jwt

import (
	"encoding/base64"
	"errors"

	"github.com/dgrijalva/jwt-go"
	"github.com/micro/go-micro/v2/auth"
)

var (
	// ErrInvalidPrivateKey is returned when the service provided an invalid private key
	ErrInvalidPrivateKey = errors.New("An invalid private key was provided")

	// ErrEncodingToken is returned when the service encounters an error during encoding
	ErrEncodingToken = errors.New("An error occured while encoding the JWT")

	// ErrInvalidToken is returned when the token provided is not valid
	ErrInvalidToken = errors.New("An invalid token was provided")

	// ErrMissingToken is returned when no token is provided
	ErrMissingToken = errors.New("A valid JWT is required")
)

// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
	svc := new(svc)
	svc.Init(opts...)
	return svc
}

// svc is the JWT implementation of the Auth interface
type svc struct {
	options auth.Options
}

func (s *svc) String() string {
	return "jwt"
}

func (s *svc) Options() auth.Options {
	return s.options
}

func (s *svc) Init(opts ...auth.Option) error {
	for _, o := range opts {
		o(&s.options)
	}

	return nil
}

// AuthClaims to be encoded in the JWT
type AuthClaims struct {
	Id       string            `json:"id"`
	Roles    []*auth.Role      `json:"roles"`
	Metadata map[string]string `json:"metadata"`

	jwt.StandardClaims
}

// Generate a new JWT
func (s *svc) Generate(id string, ops ...auth.GenerateOption) (*auth.Account, error) {
	// decode the private key
	priv, err := base64.StdEncoding.DecodeString(s.options.PrivateKey)
	if err != nil {
		return nil, err
	}

	key, err := jwt.ParseRSAPrivateKeyFromPEM(priv)
	if err != nil {
		return nil, ErrEncodingToken
	}

	options := auth.NewGenerateOptions(ops...)
	account := jwt.NewWithClaims(jwt.SigningMethodRS256, AuthClaims{
		id, options.Roles, options.Metadata, jwt.StandardClaims{
			Subject:   id,
			ExpiresAt: options.Expiry.Unix(),
		},
	})

	token, err := account.SignedString(key)
	if err != nil {
		return nil, err
	}

	return &auth.Account{
		Id:       id,
		Token:    token,
		Roles:    options.Roles,
		Metadata: options.Metadata,
	}, nil
}

// Revoke an authorization account
func (s *svc) Revoke(token string) error {
	return nil
}

// Verify a JWT
func (s *svc) Verify(token string) (*auth.Account, error) {
	if token == "" {
		return nil, ErrMissingToken
	}

	// decode the public key
	pub, err := base64.StdEncoding.DecodeString(s.options.PublicKey)
	if err != nil {
		return nil, err
	}

	res, err := jwt.ParseWithClaims(token, &AuthClaims{}, func(token *jwt.Token) (interface{}, error) {
		return jwt.ParseRSAPublicKeyFromPEM(pub)
	})
	if err != nil {
		return nil, err
	}

	if !res.Valid {
		return nil, ErrInvalidToken
	}

	claims, ok := res.Claims.(*AuthClaims)
	if !ok {
		return nil, ErrInvalidToken
	}

	return &auth.Account{
		Id:       claims.Id,
		Metadata: claims.Metadata,
		Roles:    claims.Roles,
	}, nil
}
<commit_msg>Auth JWT ID Fix (#1359)<commit_after>package jwt

import (
	"encoding/base64"
	"errors"

	"github.com/dgrijalva/jwt-go"
	"github.com/micro/go-micro/v2/auth"
)

var (
	// ErrInvalidPrivateKey is returned when the service provided an invalid private key
	ErrInvalidPrivateKey = errors.New("An invalid private key was provided")

	// ErrEncodingToken is returned when the service encounters an error during encoding
	ErrEncodingToken = errors.New("An error occured while encoding the JWT")

	// ErrInvalidToken is returned when the token provided is not valid
	ErrInvalidToken = errors.New("An invalid token was provided")

	// ErrMissingToken is returned when no token is provided
	ErrMissingToken = errors.New("A valid JWT is required")
)

// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
	svc := new(svc)
	svc.Init(opts...)
	return svc
}

// svc is the JWT implementation of the Auth interface
type svc struct {
	options auth.Options
}

func (s *svc) String() string {
	return "jwt"
}

func (s *svc) Options() auth.Options {
	return s.options
}

func (s *svc) Init(opts ...auth.Option) error {
	for _, o := range opts {
		o(&s.options)
	}

	return nil
}

// AuthClaims to be encoded in the JWT
type AuthClaims struct {
	Roles    []*auth.Role      `json:"roles"`
	Metadata map[string]string `json:"metadata"`

	jwt.StandardClaims
}

// Generate a new JWT
func (s *svc) Generate(id string, ops ...auth.GenerateOption) (*auth.Account, error) {
	// decode the private key
	priv, err := base64.StdEncoding.DecodeString(s.options.PrivateKey)
	if err != nil {
		return nil, err
	}

	key, err := jwt.ParseRSAPrivateKeyFromPEM(priv)
	if err != nil {
		return nil, ErrEncodingToken
	}

	options := auth.NewGenerateOptions(ops...)
	account := jwt.NewWithClaims(jwt.SigningMethodRS256, AuthClaims{
		options.Roles, options.Metadata, jwt.StandardClaims{
			Subject:   id,
			ExpiresAt: options.Expiry.Unix(),
		},
	})

	token, err := account.SignedString(key)
	if err != nil {
		return nil, err
	}

	return &auth.Account{
		Id:       id,
		Token:    token,
		Roles:    options.Roles,
		Metadata: options.Metadata,
	}, nil
}

// Revoke an authorization account
func (s *svc) Revoke(token string) error {
	return nil
}

// Verify a JWT
func (s *svc) Verify(token string) (*auth.Account, error) {
	if token == "" {
		return nil, ErrMissingToken
	}

	// decode the public key
	pub, err := base64.StdEncoding.DecodeString(s.options.PublicKey)
	if err != nil {
		return nil, err
	}

	res, err := jwt.ParseWithClaims(token, &AuthClaims{}, func(token *jwt.Token) (interface{}, error) {
		return jwt.ParseRSAPublicKeyFromPEM(pub)
	})
	if err != nil {
		return nil, err
	}

	if !res.Valid {
		return nil, ErrInvalidToken
	}

	claims, ok := res.Claims.(*AuthClaims)
	if !ok {
		return nil, ErrInvalidToken
	}

	return &auth.Account{
		Id:       claims.Subject,
		Metadata: claims.Metadata,
		Roles:    claims.Roles,
	}, nil
}
<|endoftext|>


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

package p

import "fmt"

func permToString(perm uint32) string {
	ret := ""

	if perm&DMDIR != 0 {
		ret += "d"
	}

	if perm&DMAPPEND != 0 {
		ret += "a"
	}

	if perm&DMAUTH != 0 {
		ret += "A"
	}

	if perm&DMEXCL != 0 {
		ret += "l"
	}

	if perm&DMTMP != 0 {
		ret += "t"
	}

	if perm&DMDEVICE != 0 {
		ret += "D"
	}

	if perm&DMSOCKET != 0 {
		ret += "S"
	}

	if perm&DMNAMEDPIPE != 0 {
		ret += "P"
	}

	if perm&DMSYMLINK != 0 {
		ret += "L"
	}

	ret += fmt.Sprintf("%o", perm&0777)
	return ret
}

func (qid *Qid) String() string {
	b := ""
	if qid.Type&QTDIR != 0 {
		b += "d"
	}
	if qid.Type&QTAPPEND != 0 {
		b += "a"
	}
	if qid.Type&QTAUTH != 0 {
		b += "A"
	}
	if qid.Type&QTEXCL != 0 {
		b += "l"
	}
	if qid.Type&QTTMP != 0 {
		b += "t"
	}
	if qid.Type&QTSYMLINK != 0 {
		b += "L"
	}

	return fmt.Sprintf("(%x %x '%s')", qid.Path, qid.Version, b)
}

func (d *Dir) String() string {
	ret := fmt.Sprintf("'%s' '%s' '%s' '%s' q ", d.Name, d.Uid, d.Gid, d.Muid)
	ret += d.Qid.String() + " m " + permToString(d.Mode)
	ret += fmt.Sprintf(" at %d mt %d l %d t %d d %d", d.Atime, d.Mtime,
		d.Length, d.Type, d.Dev)

	/* dotu ? */
	ret += " ext " + d.Ext

	return ret
}

func (fc *Fcall) String() string {
	ret := ""

	switch fc.Type {
	default:
		ret = fmt.Sprintf("invalid call: %d", fc.Type)
	case Tversion:
		ret = fmt.Sprintf("Tversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
	case Rversion:
		ret = fmt.Sprintf("Rversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
	case Tauth:
		ret = fmt.Sprintf("Tauth tag %d afid %d uname '%s' nuname %d aname '%s'",
			fc.Tag, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
	case Rauth:
		ret = fmt.Sprintf("Rauth tag %d aqid %v", fc.Tag, fc.Qid)
	case Rattach:
		ret = fmt.Sprintf("Rattach tag %d aqid %v", fc.Tag, fc.Qid)
	case Tattach:
		ret = fmt.Sprintf("Tattach tag %d fid %d afid %d uname '%s' nuname %d aname '%s'",
			fc.Tag, fc.Fid, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
	case Tflush:
		ret = fmt.Sprintf("Tflush tag %d oldtag %d", fc.Tag, fc.Oldtag)
	case Rerror:
		ret = fmt.Sprintf("Rerror tag %d ename '%s' ecode %d", fc.Tag, fc.Error, fc.Errornum)
	case Twalk:
		ret = fmt.Sprintf("Twalk tag %d fid %d newfid %d ", fc.Tag, fc.Fid, fc.Newfid)
		for i := 0; i < len(fc.Wname); i++ {
			ret += fmt.Sprintf("%d:'%s' ", i, fc.Wname[i])
		}
	case Rwalk:
		ret = fmt.Sprintf("Rwalk tag %d ", fc.Tag)
		for i := 0; i < len(fc.Wqid); i++ {
			ret += fmt.Sprintf("%v ", &fc.Wqid[i])
		}
	case Topen:
		ret = fmt.Sprintf("Topen tag %d fid %d mode %x", fc.Tag, fc.Fid, fc.Mode)
	case Ropen:
		ret = fmt.Sprintf("Ropen tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
	case Rcreate:
		ret = fmt.Sprintf("Rcreate tag %d qid %v iounit %d", fc.Tag, fc.Qid, fc.Iounit)
	case Tcreate:
		ret = fmt.Sprintf("Tcreate tag %d fid %d name '%s' perm ", fc.Tag, fc.Fid, fc.Name)
		ret += permToString(fc.Perm)
		ret += fmt.Sprintf(" mode %x ", fc.Mode)
	case Tread:
		ret = fmt.Sprintf("Tread tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
	case Rread:
		ret = fmt.Sprintf("Rread tag %d count %d", fc.Tag, fc.Count)
	case Twrite:
		ret = fmt.Sprintf("Twrite tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
	case Rwrite:
		ret = fmt.Sprintf("Rwrite tag %d count %d", fc.Tag, fc.Count)
	case Tclunk:
		ret = fmt.Sprintf("Tclunk tag %d fid %d", fc.Tag, fc.Fid)
	case Rclunk:
		ret = fmt.Sprintf("Rclunk tag %d", fc.Tag)
	case Tremove:
		ret = fmt.Sprintf("Tremove tag %d fid %d", fc.Tag, fc.Fid)
	case Tstat:
		ret = fmt.Sprintf("Tstat tag %d fid %d", fc.Tag, fc.Fid)
	case Rstat:
		ret = fmt.Sprintf("Rstat tag %d st (%v)", fc.Tag, &fc.Dir)
	case Twstat:
		ret = fmt.Sprintf("Twstat tag %d fid %d st (%v)", fc.Tag, fc.Fid, &fc.Dir)
	case Rflush:
		ret = fmt.Sprintf("Rflush tag %d", fc.Tag)
	case Rremove:
		ret = fmt.Sprintf("Rremove tag %d", fc.Tag)
	case Rwstat:
		ret = fmt.Sprintf("Rwstat tag %d", fc.Tag)
	}

	return ret
}
<commit_msg>Fix from dadkins: display Qids properly.<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package p

import "fmt"

func permToString(perm uint32) string {
	ret := ""

	if perm&DMDIR != 0 {
		ret += "d"
	}

	if perm&DMAPPEND != 0 {
		ret += "a"
	}

	if perm&DMAUTH != 0 {
		ret += "A"
	}

	if perm&DMEXCL != 0 {
		ret += "l"
	}

	if perm&DMTMP != 0 {
		ret += "t"
	}

	if perm&DMDEVICE != 0 {
		ret += "D"
	}

	if perm&DMSOCKET != 0 {
		ret += "S"
	}

	if perm&DMNAMEDPIPE != 0 {
		ret += "P"
	}

	if perm&DMSYMLINK != 0 {
		ret += "L"
	}

	ret += fmt.Sprintf("%o", perm&0777)
	return ret
}

func (qid *Qid) String() string {
	b := ""
	if qid.Type&QTDIR != 0 {
		b += "d"
	}
	if qid.Type&QTAPPEND != 0 {
		b += "a"
	}
	if qid.Type&QTAUTH != 0 {
		b += "A"
	}
	if qid.Type&QTEXCL != 0 {
		b += "l"
	}
	if qid.Type&QTTMP != 0 {
		b += "t"
	}
	if qid.Type&QTSYMLINK != 0 {
		b += "L"
	}

	return fmt.Sprintf("(%x %x '%s')", qid.Path, qid.Version, b)
}

func (d *Dir) String() string {
	ret := fmt.Sprintf("'%s' '%s' '%s' '%s' q ", d.Name, d.Uid, d.Gid, d.Muid)
	ret += d.Qid.String() + " m " + permToString(d.Mode)
	ret += fmt.Sprintf(" at %d mt %d l %d t %d d %d", d.Atime, d.Mtime,
		d.Length, d.Type, d.Dev)

	/* dotu ? */
	ret += " ext " + d.Ext

	return ret
}

func (fc *Fcall) String() string {
	ret := ""

	switch fc.Type {
	default:
		ret = fmt.Sprintf("invalid call: %d", fc.Type)
	case Tversion:
		ret = fmt.Sprintf("Tversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
	case Rversion:
		ret = fmt.Sprintf("Rversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version)
	case Tauth:
		ret = fmt.Sprintf("Tauth tag %d afid %d uname '%s' nuname %d aname '%s'",
			fc.Tag, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
	case Rauth:
		ret = fmt.Sprintf("Rauth tag %d aqid %v", fc.Tag, &fc.Qid)
	case Rattach:
		ret = fmt.Sprintf("Rattach tag %d aqid %v", fc.Tag, &fc.Qid)
	case Tattach:
		ret = fmt.Sprintf("Tattach tag %d fid %d afid %d uname '%s' nuname %d aname '%s'",
			fc.Tag, fc.Fid, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname)
	case Tflush:
		ret = fmt.Sprintf("Tflush tag %d oldtag %d", fc.Tag, fc.Oldtag)
	case Rerror:
		ret = fmt.Sprintf("Rerror tag %d ename '%s' ecode %d", fc.Tag, fc.Error, fc.Errornum)
	case Twalk:
		ret = fmt.Sprintf("Twalk tag %d fid %d newfid %d ", fc.Tag, fc.Fid, fc.Newfid)
		for i := 0; i < len(fc.Wname); i++ {
			ret += fmt.Sprintf("%d:'%s' ", i, fc.Wname[i])
		}
	case Rwalk:
		ret = fmt.Sprintf("Rwalk tag %d ", fc.Tag)
		for i := 0; i < len(fc.Wqid); i++ {
			ret += fmt.Sprintf("%v ", &fc.Wqid[i])
		}
	case Topen:
		ret = fmt.Sprintf("Topen tag %d fid %d mode %x", fc.Tag, fc.Fid, fc.Mode)
	case Ropen:
		ret = fmt.Sprintf("Ropen tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
	case Rcreate:
		ret = fmt.Sprintf("Rcreate tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit)
	case Tcreate:
		ret = fmt.Sprintf("Tcreate tag %d fid %d name '%s' perm ", fc.Tag, fc.Fid, fc.Name)
		ret += permToString(fc.Perm)
		ret += fmt.Sprintf(" mode %x ", fc.Mode)
	case Tread:
		ret = fmt.Sprintf("Tread tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
	case Rread:
		ret = fmt.Sprintf("Rread tag %d count %d", fc.Tag, fc.Count)
	case Twrite:
		ret = fmt.Sprintf("Twrite tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count)
	case Rwrite:
		ret = fmt.Sprintf("Rwrite tag %d count %d", fc.Tag, fc.Count)
	case Tclunk:
		ret = fmt.Sprintf("Tclunk tag %d fid %d", fc.Tag, fc.Fid)
	case Rclunk:
		ret = fmt.Sprintf("Rclunk tag %d", fc.Tag)
	case Tremove:
		ret = fmt.Sprintf("Tremove tag %d fid %d", fc.Tag, fc.Fid)
	case Tstat:
		ret = fmt.Sprintf("Tstat tag %d fid %d", fc.Tag, fc.Fid)
	case Rstat:
		ret = fmt.Sprintf("Rstat tag %d st (%v)", fc.Tag, &fc.Dir)
	case Twstat:
		ret = fmt.Sprintf("Twstat tag %d fid %d st (%v)", fc.Tag, fc.Fid, &fc.Dir)
	case Rflush:
		ret = fmt.Sprintf("Rflush tag %d", fc.Tag)
	case Rremove:
		ret = fmt.Sprintf("Rremove tag %d", fc.Tag)
	case Rwstat:
		ret = fmt.Sprintf("Rwstat tag %d", fc.Tag)
	}

	return ret
}
<|endoftext|>


<commit_before>package sudoku

import (
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		defer grid.Done()
		grid.Load(TEST_GRID)
		grid.HumanSolve()
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	steps := grid.HumanSolution()

	if steps == nil {
		t.Log("Human solution returned 0 techniques.")
		t.Fail()
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}

}

func TestHumanSolveWithGuess(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	if !grid.LoadFromFile(puzzlePath("harddifficulty.sdk")) {
		t.Fatal("harddifficulty.sdk wasn't loaded")
	}

	steps := grid.HumanSolution()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		&SolveStep{
			CellList{
				grid.Cell(0, 0),
			},
			nil,
			IntSlice{1},
			nil,
			Techniques[3],
		},
		&SolveStep{
			CellList{
				grid.Cell(1, 0),
				grid.Cell(1, 1),
			},
			CellList{
				grid.Cell(1, 3),
				grid.Cell(1, 4),
			},
			IntSlice{1, 2},
			nil,
			Techniques[8],
		},
		&SolveStep{
			CellList{
				grid.Cell(2, 0),
			},
			nil,
			IntSlice{2},
			nil,
			Techniques[3],
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, we put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Next, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block.",
		"Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i])
			t.Fail()
		}
	}
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	difficulty := grid.Difficulty()

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 60)

	done := make(chan bool)

	go func() {
		_ = otherGrid.Difficulty()
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<commit_msg>In testing, use calcualteDifficulty(false) so that the tests complete faster.<commit_after>package sudoku

import (
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		defer grid.Done()
		grid.Load(TEST_GRID)
		grid.HumanSolve()
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	steps := grid.HumanSolution()

	if steps == nil {
		t.Log("Human solution returned 0 techniques.")
		t.Fail()
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}

}

func TestHumanSolveWithGuess(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	if !grid.LoadFromFile(puzzlePath("harddifficulty.sdk")) {
		t.Fatal("harddifficulty.sdk wasn't loaded")
	}

	steps := grid.HumanSolution()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		&SolveStep{
			CellList{
				grid.Cell(0, 0),
			},
			nil,
			IntSlice{1},
			nil,
			Techniques[3],
		},
		&SolveStep{
			CellList{
				grid.Cell(1, 0),
				grid.Cell(1, 1),
			},
			CellList{
				grid.Cell(1, 3),
				grid.Cell(1, 4),
			},
			IntSlice{1, 2},
			nil,
			Techniques[8],
		},
		&SolveStep{
			CellList{
				grid.Cell(2, 0),
			},
			nil,
			IntSlice{2},
			nil,
			Techniques[3],
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, we put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Next, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block.",
		"Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i])
			t.Fail()
		}
	}
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	//We use the cheaper one for testing so it completes faster.
	difficulty := grid.calcluateDifficulty(false)

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 60)

	done := make(chan bool)

	go func() {
		//We use the cheaper one for testing so it completes faster
		_ = otherGrid.calcluateDifficulty(false)
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"time"

	"github.com/jinzhu/now"
)

// Now is the func used for system time within gtm
// This allows for manipulating system time during testing
var Now = func() time.Time { return time.Now() }

type DateRange struct {
	Start time.Time
	End   time.Time
}

func (d DateRange) IsSet() bool {
	return !d.Start.IsZero() || !d.End.IsZero()
}

func (d DateRange) String() string {
	return fmt.Sprintf("%s - %s", d.Start.Format(time.UnixDate), d.End.Format(time.UnixDate))
}

func (r DateRange) Within(t time.Time) bool {
	switch {
	case !r.Start.IsZero() && !r.End.IsZero():
		return t.Equal(r.Start) || t.Equal(r.End) || (t.After(r.Start) && t.Before(r.End))
	case !r.Start.IsZero():
		return t.Equal(r.Start) || t.After(r.Start)
	case !r.End.IsZero():
		return t.Equal(r.End) || t.Before(r.End)
	default:
		return false
	}

}

func TodayRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfDay()
	end := now.EndOfDay()

	return DateRange{Start: start, End: end}
}

func YesterdayRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfDay().AddDate(0, 0, -1)
	end := start.AddDate(0, 0, 1).Add(-time.Nanosecond)

	return DateRange{Start: start, End: end}
}

func ThisWeekRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfWeek()
	end := now.EndOfWeek()

	return DateRange{End: end, Start: start}
}

func LastWeekRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfWeek().AddDate(0, 0, -7)
	end := start.AddDate(0, 0, 7).Add(-time.Nanosecond)

	return DateRange{End: end, Start: start}
}

func ThisMonthRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfMonth()
	end := now.EndOfMonth()

	return DateRange{End: end, Start: start}
}

func LastMonthRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfMonth().AddDate(0, -1, 0)
	end := start.AddDate(0, 1, 0).Add(-time.Nanosecond)

	return DateRange{End: end, Start: start}
}

func ThisYearRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfYear()
	end := now.EndOfYear()

	return DateRange{End: end, Start: start}
}

func LastYearRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfYear().AddDate(-1, 0, 0)
	end := start.AddDate(1, 0, 0).Add(-time.Nanosecond)

	return DateRange{End: end, Start: start}
}
<commit_msg>Add copyright notice<commit_after>// Copyright 2016 Michael Schenk. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package util

import (
	"fmt"
	"time"

	"github.com/jinzhu/now"
)

// Now is the func used for system time within gtm
// This allows for manipulating system time during testing
var Now = func() time.Time { return time.Now() }

type DateRange struct {
	Start time.Time
	End   time.Time
}

func (d DateRange) IsSet() bool {
	return !d.Start.IsZero() || !d.End.IsZero()
}

func (d DateRange) String() string {
	return fmt.Sprintf("%s - %s", d.Start.Format(time.UnixDate), d.End.Format(time.UnixDate))
}

func (r DateRange) Within(t time.Time) bool {
	switch {
	case !r.Start.IsZero() && !r.End.IsZero():
		return t.Equal(r.Start) || t.Equal(r.End) || (t.After(r.Start) && t.Before(r.End))
	case !r.Start.IsZero():
		return t.Equal(r.Start) || t.After(r.Start)
	case !r.End.IsZero():
		return t.Equal(r.End) || t.Before(r.End)
	default:
		return false
	}

}

func TodayRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfDay()
	end := now.EndOfDay()

	return DateRange{Start: start, End: end}
}

func YesterdayRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfDay().AddDate(0, 0, -1)
	end := start.AddDate(0, 0, 1).Add(-time.Nanosecond)

	return DateRange{Start: start, End: end}
}

func ThisWeekRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfWeek()
	end := now.EndOfWeek()

	return DateRange{End: end, Start: start}
}

func LastWeekRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfWeek().AddDate(0, 0, -7)
	end := start.AddDate(0, 0, 7).Add(-time.Nanosecond)

	return DateRange{End: end, Start: start}
}

func ThisMonthRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfMonth()
	end := now.EndOfMonth()

	return DateRange{End: end, Start: start}
}

func LastMonthRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfMonth().AddDate(0, -1, 0)
	end := start.AddDate(0, 1, 0).Add(-time.Nanosecond)

	return DateRange{End: end, Start: start}
}

func ThisYearRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfYear()
	end := now.EndOfYear()

	return DateRange{End: end, Start: start}
}

func LastYearRange() DateRange {
	now := now.New(Now())

	start := now.BeginningOfYear().AddDate(-1, 0, 0)
	end := start.AddDate(1, 0, 0).Add(-time.Nanosecond)

	return DateRange{End: end, Start: start}
}
<|endoftext|>


<commit_before>// Copyright 2022 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package filepermission

import (
	"os"

	"golang.org/x/tools/go/analysis"
)

// Name is the name of the analyzer.
const Name = "filepermission"

// Analyzer is the analyzer struct of prealloc.
var Analyzer = &analysis.Analyzer{
	Name: Name,
	Doc:  "Go files should not have execution permission",
	Run:  run,
}

func run(pass *analysis.Pass) (interface{}, error) {
	for _, f := range pass.Files {
		fn := pass.Fset.PositionFor(f.Pos(), false).Filename
		if fn != "" {
			stat, err := os.Stat(fn)
			if err != nil {
				return nil, err
			}
			if stat.Mode() != 0644 {
				pass.Reportf(f.Pos(), "[%s] source code file should not have execute permission %s", Name, stat.Mode())
			}
		}
	}

	return nil, nil
}
<commit_msg>lint: check file permission with &0111 (#37997)<commit_after>// Copyright 2022 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package filepermission

import (
	"os"

	"golang.org/x/tools/go/analysis"
)

// Name is the name of the analyzer.
const Name = "filepermission"

// Analyzer is the analyzer struct of prealloc.
var Analyzer = &analysis.Analyzer{
	Name: Name,
	Doc:  "Go files should not have execution permission",
	Run:  run,
}

func run(pass *analysis.Pass) (interface{}, error) {
	for _, f := range pass.Files {
		fn := pass.Fset.PositionFor(f.Pos(), false).Filename
		if fn != "" {
			stat, err := os.Stat(fn)
			if err != nil {
				return nil, err
			}
			if stat.Mode()&0111 != 0 {
				pass.Reportf(f.Pos(), "[%s] source code file should not have execute permission %s", Name, stat.Mode())
			}
		}
	}

	return nil, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package console

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"

	"github.com/thinkofdeath/steven/chat"
)

var (
	// stdout and a log file combind
	w io.Writer

	// For the possible option of scrolling in the
	// future
	historyBuffer [200]chat.AnyComponent

	defaultRegistry registry
)

func checkInit() {
	if w != nil {
		return
	}
	f, err := os.Create("steven-log.txt")
	if err != nil {
		panic(err)
	}
	w = io.MultiWriter(f, os.Stdout)
}

// Text appends the passed formatted string plus a new line to
// the log buffer. The formatting uses the same rules as fmt.
func Text(format string, args ...interface{}) {
	_, file, line, ok := runtime.Caller(1)
	if !ok {
		file = "unknown"
		line = 0
	}
	file = file[strings.LastIndexByte(file, '/')+1:]

	msg := &chat.TextComponent{
		Text: fmt.Sprintf("[%s:%d] ", file, line),
		Component: chat.Component{
			Color: chat.Aqua,
		},
	}
	msg.Extra = append(msg.Extra, chat.Wrap(&chat.TextComponent{
		Text: fmt.Sprintf(format, args...),
		Component: chat.Component{
			Color: chat.White,
		},
	}))
	Component(chat.Wrap(msg))
}

// Component appends the component to the log buffer.
func Component(c chat.AnyComponent) {
	checkInit()
	io.WriteString(w, c.String()+"\n")
	copy(historyBuffer[1:], historyBuffer[:])
	historyBuffer[0] = c
}

// History returns up to the requested number of lines from the log
// buffer.
//
// As a special case -1 will return the whole buffer.
func History(lines int) []chat.AnyComponent {
	if lines == -1 || lines > len(historyBuffer) {
		return historyBuffer[:]
	}
	return historyBuffer[:lines]
}
<commit_msg>console: use locks to protect the history buffer<commit_after>// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package console

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"sync"

	"github.com/thinkofdeath/steven/chat"
)

var (
	// stdout and a log file combind
	w io.Writer

	// For the possible option of scrolling in the
	// future
	historyBuffer [200]chat.AnyComponent
	lock          sync.Mutex

	defaultRegistry registry
)

func checkInit() {
	if w != nil {
		return
	}
	f, err := os.Create("steven-log.txt")
	if err != nil {
		panic(err)
	}
	w = io.MultiWriter(f, os.Stdout)
}

// Text appends the passed formatted string plus a new line to
// the log buffer. The formatting uses the same rules as fmt.
func Text(format string, args ...interface{}) {
	_, file, line, ok := runtime.Caller(1)
	if !ok {
		file = "unknown"
		line = 0
	}
	file = file[strings.LastIndexByte(file, '/')+1:]

	msg := &chat.TextComponent{
		Text: fmt.Sprintf("[%s:%d] ", file, line),
		Component: chat.Component{
			Color: chat.Aqua,
		},
	}
	msg.Extra = append(msg.Extra, chat.Wrap(&chat.TextComponent{
		Text: fmt.Sprintf(format, args...),
		Component: chat.Component{
			Color: chat.White,
		},
	}))
	Component(chat.Wrap(msg))
}

// Component appends the component to the log buffer.
func Component(c chat.AnyComponent) {
	lock.Lock()
	defer lock.Unlock()
	checkInit()
	io.WriteString(w, c.String()+"\n")
	copy(historyBuffer[1:], historyBuffer[:])
	historyBuffer[0] = c
}

// History returns up to the requested number of lines from the log
// buffer.
//
// As a special case -1 will return the whole buffer.
func History(lines int) []chat.AnyComponent {
	lock.Lock()
	defer lock.Unlock()
	if lines == -1 || lines > len(historyBuffer) {
		return historyBuffer[:]
	}
	return historyBuffer[:lines]
}
<|endoftext|>


<commit_before>package complete

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
)

func TestSimple(t *testing.T) {
	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		tests    = []struct {
			c string
			o []string
		}{
			{"hi", hinames},
			{"h", hnames},
			{"t", []string{"there"}},
		}
	)

	f := NewStringCompleter(allnames)
	debug = t.Logf
	for _, tst := range tests {
		o, err := f.Complete(tst.c)
		if err != nil {
			t.Errorf("Complete %v: got %v, want nil", tst.c, err)
			continue
		}
		if !reflect.DeepEqual(o, tst.o) {
			t.Errorf("Complete %v: got %v, want %v", tst.c, o, tst.o)
		}
	}
}

func TestFile(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "TestComplete")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)

	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		tests    = []struct {
			c string
			o []string
		}{
			{"hi", hinames},
			{"h", hnames},
			{"t", []string{"there"}},
		}
	)

	for _, n := range allnames {
		if err := ioutil.WriteFile(filepath.Join(tempDir, n), []byte{}, 0600); err != nil {
			t.Fatal(err)
		}
	}
	f := NewFileCompleter(tempDir)
	debug = t.Logf
	for _, tst := range tests {
		o, err := f.Complete(tst.c)
		if err != nil {
			t.Errorf("Complete %v: got %v, want nil", tst.c, err)
			continue
		}
		if !reflect.DeepEqual(o, tst.o) {
			t.Errorf("Complete %v: got %v, want %v", tst.c, o, tst.o)
		}
	}
}

func TestMulti(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "TestComplete")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)

	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		tests    = []struct {
			c string
			o []string
		}{
			{"hi", hinames},
			{"h", hnames},
			{"t", []string{"there"}},
			{"ahi", []string{"ahi", "ahil", "ahit"}},
			{"bh", []string{"bhi", "bhil", "bhit", "bhow"}},
		}
	)
	for _, p := range []string{"bin", "sbin"} {
		if err := os.MkdirAll(filepath.Join(tempDir, p), 0700); err != nil {
			t.Fatal(err)
		}
	}
	for _, n := range allnames {
		if err := ioutil.WriteFile(filepath.Join(tempDir, "bin", "a"+n), []byte{}, 0600); err != nil {
			t.Fatal(err)
		}
		if err := ioutil.WriteFile(filepath.Join(tempDir, "sbin", "b"+n), []byte{}, 0600); err != nil {
			t.Fatal(err)
		}
	}
	debug = t.Logf
	if err := os.Setenv("PATH", fmt.Sprintf("%s:%s", filepath.Join(tempDir, "bin"), filepath.Join(tempDir, "sbin"))); err != nil {
		t.Fatal(err)
	}
	p, err := NewPathCompleter()
	if err != nil {
		t.Fatal(err)
	}
	// note that since p is a Multi, this also checks nested Multis
	f := NewMultiCompleter(NewStringCompleter(allnames), p)

	for _, tst := range tests {
		o, err := f.Complete(tst.c)
		if err != nil {
			t.Errorf("Complete %v: got %v, want nil", tst.c, err)
			continue
		}
		if !reflect.DeepEqual(o, tst.o) {
			t.Errorf("Complete %v: got %v, want %v", tst.c, o, tst.o)
		}
		t.Logf("Check %v: found %v", tst, o)
	}
}

func TestInOut(t *testing.T) {
	var tests = []struct {
		i []string
		o string
	}{
		{[]string{"a", "b", "c", "d"}, "d"},
		{[]string{""}, ""},
		{[]string{}, ""},
	}
	for _, tst := range tests {
		l := NewLine()
		if len(tst.i) > 0 {
			l.Push(tst.i[0], tst.i[1:]...)
		}

		o := l.Pop()
		if o != tst.o {
			t.Errorf("tst %v: got %v, want %v", tst, o, tst.o)
		}
	}
}

func TestInOutRW(t *testing.T) {
	var els = []string{"ab", "bc", "de", "fgh"}
	var outs = []string{"ab", "abbc", "abbcde", "abbcdefgh"}

	l := NewLine()
	t.Logf("%v %v %v", els, outs, l)
	for i := range els {
		s := strings.Join(els[:i+1], "")
		l.Write([]byte(s))
		b, err := l.ReadAll()
		if err != nil {
			t.Errorf("ReadAll of %s: got %v, want nil", s, err)
		}
		if string(b) != outs[i] {
			t.Errorf("Read back %s: got %s, want %s", s, string(b), s)
		}
	}
}
<commit_msg>Simple line reader for completion<commit_after>package complete

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
)

func TestSimple(t *testing.T) {
	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		tests    = []struct {
			c string
			o []string
		}{
			{"hi", hinames},
			{"h", hnames},
			{"t", []string{"there"}},
		}
	)

	f := NewStringCompleter(allnames)
	debug = t.Logf
	for _, tst := range tests {
		o, err := f.Complete(tst.c)
		if err != nil {
			t.Errorf("Complete %v: got %v, want nil", tst.c, err)
			continue
		}
		if !reflect.DeepEqual(o, tst.o) {
			t.Errorf("Complete %v: got %v, want %v", tst.c, o, tst.o)
		}
	}
}

func TestFile(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "TestComplete")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)

	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		tests    = []struct {
			c string
			o []string
		}{
			{"hi", hinames},
			{"h", hnames},
			{"t", []string{"there"}},
		}
	)

	for _, n := range allnames {
		if err := ioutil.WriteFile(filepath.Join(tempDir, n), []byte{}, 0600); err != nil {
			t.Fatal(err)
		}
	}
	f := NewFileCompleter(tempDir)
	debug = t.Logf
	for _, tst := range tests {
		o, err := f.Complete(tst.c)
		if err != nil {
			t.Errorf("Complete %v: got %v, want nil", tst.c, err)
			continue
		}
		if !reflect.DeepEqual(o, tst.o) {
			t.Errorf("Complete %v: got %v, want %v", tst.c, o, tst.o)
		}
	}
}

func TestMulti(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "TestComplete")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)

	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		tests    = []struct {
			c string
			o []string
		}{
			{"hi", hinames},
			{"h", hnames},
			{"t", []string{"there"}},
			{"ahi", []string{"ahi", "ahil", "ahit"}},
			{"bh", []string{"bhi", "bhil", "bhit", "bhow"}},
		}
	)
	for _, p := range []string{"bin", "sbin"} {
		if err := os.MkdirAll(filepath.Join(tempDir, p), 0700); err != nil {
			t.Fatal(err)
		}
	}
	for _, n := range allnames {
		if err := ioutil.WriteFile(filepath.Join(tempDir, "bin", "a"+n), []byte{}, 0600); err != nil {
			t.Fatal(err)
		}
		if err := ioutil.WriteFile(filepath.Join(tempDir, "sbin", "b"+n), []byte{}, 0600); err != nil {
			t.Fatal(err)
		}
	}
	debug = t.Logf
	if err := os.Setenv("PATH", fmt.Sprintf("%s:%s", filepath.Join(tempDir, "bin"), filepath.Join(tempDir, "sbin"))); err != nil {
		t.Fatal(err)
	}
	p, err := NewPathCompleter()
	if err != nil {
		t.Fatal(err)
	}
	// note that since p is a Multi, this also checks nested Multis
	f := NewMultiCompleter(NewStringCompleter(allnames), p)

	for _, tst := range tests {
		o, err := f.Complete(tst.c)
		if err != nil {
			t.Errorf("Complete %v: got %v, want nil", tst.c, err)
			continue
		}
		if !reflect.DeepEqual(o, tst.o) {
			t.Errorf("Complete %v: got %v, want %v", tst.c, o, tst.o)
		}
		t.Logf("Check %v: found %v", tst, o)
	}
}

func TestInOut(t *testing.T) {
	var tests = []struct {
		i []string
		o string
	}{
		{[]string{"a", "b", "c", "d"}, "d"},
		{[]string{""}, ""},
		{[]string{}, ""},
	}
	for _, tst := range tests {
		l := NewLine()
		if len(tst.i) > 0 {
			l.Push(tst.i[0], tst.i[1:]...)
		}

		o := l.Pop()
		if o != tst.o {
			t.Errorf("tst %v: got %v, want %v", tst, o, tst.o)
		}
	}
}

func TestInOutRW(t *testing.T) {
	var els = []string{"ab", "bc", "de", "fgh"}
	var outs = []string{"ab", "abbc", "abbcde", "abbcdefgh"}

	l := NewLine()
	t.Logf("%v %v %v", els, outs, l)
	for i := range els {
		s := strings.Join(els[:i+1], "")
		l.Write([]byte(s))
		b, err := l.ReadAll()
		if err != nil {
			t.Errorf("ReadAll of %s: got %v, want nil", s, err)
		}
		if string(b) != outs[i] {
			t.Errorf("Read back %s: got %s, want %s", s, string(b), s)
		}
	}
}

func TestLineReader(t*testing.T) {
	var (
		hinames  = []string{"hi", "hil", "hit"}
		hnames   = append(hinames, "how")
		allnames = append(hnames, "there")
		r = bytes.NewBufferString("ther\t")
	)
	cr, cw := io.Pipe()
	f := NewStringCompleter(allnames)
	debug = t.Logf

	l := NewLineReader(f, r, cw)
	var out []byte
	go func() {
		var err error
		out, err = ioutil.ReadAll(cr)
		if err != nil {
			t.Errorf("reading console io.Pipe: got %v, want nil", err)
		}
		if string(out) != "there" {
			t.Errorf("console out: got %v, want ther", string(out))
		}
	}()

	s, err := l.ReadOne()

	if err != nil {
		t.Fatal(err)
	}
	if len(s) != 1 {
		t.Fatalf("Got %d choices, want 1", len(s))
	}
	if s[0] != "there" {
		t.Errorf("Got %v choices, want there", s[0])
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package features

import (
	"k8s.io/apimachinery/pkg/util/runtime"

	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/component-base/featuregate"
)

const (
	// Every feature gate should add method here following this template:
	//
	// // owner: @username
	// // alpha: v1.4
	// MyFeature featuregate.Feature = "MyFeature"
	//
	// Feature gates should be listed in alphabetical, case-sensitive
	// (upper before any lower case character) order. This reduces the risk
	// of code conflicts because changes are more likely to be scattered
	// across the file.

	// owner: @smarterclayton
	// alpha: v1.8
	// beta: v1.9
	//
	// Allow API clients to retrieve resource lists in chunks rather than
	// all at once.
	APIListChunking featuregate.Feature = "APIListChunking"

	// owner: @MikeSpreitzer @yue9944882
	// alpha: v1.18
	// beta: v1.20
	//
	// Enables managing request concurrency with prioritization and fairness at each server.
	// The FeatureGate was introduced in release 1.15 but the feature
	// was not really implemented before 1.18.
	APIPriorityAndFairness featuregate.Feature = "APIPriorityAndFairness"

	// owner: @ilackams
	// alpha: v1.7
	// beta: v1.16
	//
	// Enables compression of REST responses (GET and LIST only)
	APIResponseCompression featuregate.Feature = "APIResponseCompression"

	// owner: @roycaihw
	// alpha: v1.20
	//
	// Assigns each kube-apiserver an ID in a cluster.
	APIServerIdentity featuregate.Feature = "APIServerIdentity"

	// owner: @dashpole
	// alpha: v1.22
	//
	// Add support for distributed tracing in the API Server
	APIServerTracing featuregate.Feature = "APIServerTracing"

	// owner: @tallclair
	// alpha: v1.7
	// beta: v1.8
	// GA: v1.12
	//
	// AdvancedAuditing enables a much more general API auditing pipeline, which includes support for
	// pluggable output backends and an audit policy specifying how different requests should be
	// audited.
	AdvancedAuditing featuregate.Feature = "AdvancedAuditing"

	// owner: @cici37
	// kep: http://kep.k8s.io/2876
	// alpha: v1.23
	//
	// Enables expression validation for Custom Resource
	CustomResourceValidationExpressions featuregate.Feature = "CustomResourceValidationExpressions"

	// owner: @apelisse
	// alpha: v1.12
	// beta: v1.13
	// stable: v1.18
	//
	// Allow requests to be processed but not stored, so that
	// validation, merging, mutation can be tested without
	// committing.
	DryRun featuregate.Feature = "DryRun"

	// owner: @wojtek-t
	// alpha: v1.20
	// beta: v1.21
	// GA: v1.24
	//
	// Allows for updating watchcache resource version with progress notify events.
	EfficientWatchResumption featuregate.Feature = "EfficientWatchResumption"

	// owner: @jiahuif
	// kep: http://kep.k8s.io/2887
	// alpha: v1.23
	// beta: v1.24
	//
	// Enables populating "enum" field of OpenAPI schemas
	// in the spec returned from kube-apiserver.
	OpenAPIEnums featuregate.Feature = "OpenAPIEnums"

	// owner: @jefftree
	// kep: http://kep.k8s.io/2896
	// alpha: v1.23
	// beta: v1.24
	//
	// Enables kubernetes to publish OpenAPI v3
	OpenAPIV3 featuregate.Feature = "OpenAPIV3"

	// owner: @caesarxuchao
	// alpha: v1.15
	// beta: v1.16
	//
	// Allow apiservers to show a count of remaining items in the response
	// to a chunking list request.
	RemainingItemCount featuregate.Feature = "RemainingItemCount"

	// owner: @wojtek-t
	// alpha: v1.16
	// beta: v1.20
	// GA: v1.24
	//
	// Deprecates and removes SelfLink from ObjectMeta and ListMeta.
	RemoveSelfLink featuregate.Feature = "RemoveSelfLink"

	// owner: @apelisse, @lavalamp
	// alpha: v1.14
	// beta: v1.16
	// stable: v1.22
	//
	// Server-side apply. Merging happens on the server.
	ServerSideApply featuregate.Feature = "ServerSideApply"

	// owner: @kevindelgado
	// kep: http://kep.k8s.io/2885
	// alpha: v1.23
	// beta: v1.24
	//
	// Enables server-side field validation.
	ServerSideFieldValidation featuregate.Feature = "ServerSideFieldValidation"

	// owner: @caesarxuchao @roycaihw
	// alpha: v1.20
	//
	// Enable the storage version API.
	StorageVersionAPI featuregate.Feature = "StorageVersionAPI"

	// owner: @caesarxuchao
	// alpha: v1.14
	// beta: v1.15
	//
	// Allow apiservers to expose the storage version hash in the discovery
	// document.
	StorageVersionHash featuregate.Feature = "StorageVersionHash"

	// owner: @wojtek-t
	// alpha: v1.15
	// beta: v1.16
	// GA: v1.17
	//
	// Enables support for watch bookmark events.
	WatchBookmark featuregate.Feature = "WatchBookmark"
)

func init() {
	runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultKubernetesFeatureGates))
}

// defaultKubernetesFeatureGates consists of all known Kubernetes-specific feature keys.
// To add a new feature, define a key for it above and add it here. The features will be
// available throughout Kubernetes binaries.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	APIListChunking: {Default: true, PreRelease: featuregate.Beta},

	APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},

	APIResponseCompression: {Default: true, PreRelease: featuregate.Beta},

	APIServerIdentity: {Default: false, PreRelease: featuregate.Alpha},

	APIServerTracing: {Default: false, PreRelease: featuregate.Alpha},

	AdvancedAuditing: {Default: true, PreRelease: featuregate.GA},

	CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha},

	DryRun: {Default: true, PreRelease: featuregate.GA},

	EfficientWatchResumption: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},

	OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta},

	OpenAPIV3: {Default: true, PreRelease: featuregate.Beta},

	RemainingItemCount: {Default: true, PreRelease: featuregate.Beta},

	RemoveSelfLink: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},

	ServerSideApply: {Default: true, PreRelease: featuregate.GA},

	ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta},

	StorageVersionAPI: {Default: false, PreRelease: featuregate.Alpha},

	StorageVersionHash: {Default: true, PreRelease: featuregate.Beta},

	WatchBookmark: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
}
<commit_msg>Promote feature CustomResourceValidationExpressions to beta<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package features

import (
	"k8s.io/apimachinery/pkg/util/runtime"

	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/component-base/featuregate"
)

const (
	// Every feature gate should add method here following this template:
	//
	// // owner: @username
	// // alpha: v1.4
	// MyFeature featuregate.Feature = "MyFeature"
	//
	// Feature gates should be listed in alphabetical, case-sensitive
	// (upper before any lower case character) order. This reduces the risk
	// of code conflicts because changes are more likely to be scattered
	// across the file.

	// owner: @smarterclayton
	// alpha: v1.8
	// beta: v1.9
	//
	// Allow API clients to retrieve resource lists in chunks rather than
	// all at once.
	APIListChunking featuregate.Feature = "APIListChunking"

	// owner: @MikeSpreitzer @yue9944882
	// alpha: v1.18
	// beta: v1.20
	//
	// Enables managing request concurrency with prioritization and fairness at each server.
	// The FeatureGate was introduced in release 1.15 but the feature
	// was not really implemented before 1.18.
	APIPriorityAndFairness featuregate.Feature = "APIPriorityAndFairness"

	// owner: @ilackams
	// alpha: v1.7
	// beta: v1.16
	//
	// Enables compression of REST responses (GET and LIST only)
	APIResponseCompression featuregate.Feature = "APIResponseCompression"

	// owner: @roycaihw
	// alpha: v1.20
	//
	// Assigns each kube-apiserver an ID in a cluster.
	APIServerIdentity featuregate.Feature = "APIServerIdentity"

	// owner: @dashpole
	// alpha: v1.22
	//
	// Add support for distributed tracing in the API Server
	APIServerTracing featuregate.Feature = "APIServerTracing"

	// owner: @tallclair
	// alpha: v1.7
	// beta: v1.8
	// GA: v1.12
	//
	// AdvancedAuditing enables a much more general API auditing pipeline, which includes support for
	// pluggable output backends and an audit policy specifying how different requests should be
	// audited.
	AdvancedAuditing featuregate.Feature = "AdvancedAuditing"

	// owner: @cici37
	// kep: http://kep.k8s.io/2876
	// alpha: v1.23
	// beta: v1.25
	//
	// Enables expression validation for Custom Resource
	CustomResourceValidationExpressions featuregate.Feature = "CustomResourceValidationExpressions"

	// owner: @apelisse
	// alpha: v1.12
	// beta: v1.13
	// stable: v1.18
	//
	// Allow requests to be processed but not stored, so that
	// validation, merging, mutation can be tested without
	// committing.
	DryRun featuregate.Feature = "DryRun"

	// owner: @wojtek-t
	// alpha: v1.20
	// beta: v1.21
	// GA: v1.24
	//
	// Allows for updating watchcache resource version with progress notify events.
	EfficientWatchResumption featuregate.Feature = "EfficientWatchResumption"

	// owner: @jiahuif
	// kep: http://kep.k8s.io/2887
	// alpha: v1.23
	// beta: v1.24
	//
	// Enables populating "enum" field of OpenAPI schemas
	// in the spec returned from kube-apiserver.
	OpenAPIEnums featuregate.Feature = "OpenAPIEnums"

	// owner: @jefftree
	// kep: http://kep.k8s.io/2896
	// alpha: v1.23
	// beta: v1.24
	//
	// Enables kubernetes to publish OpenAPI v3
	OpenAPIV3 featuregate.Feature = "OpenAPIV3"

	// owner: @caesarxuchao
	// alpha: v1.15
	// beta: v1.16
	//
	// Allow apiservers to show a count of remaining items in the response
	// to a chunking list request.
	RemainingItemCount featuregate.Feature = "RemainingItemCount"

	// owner: @wojtek-t
	// alpha: v1.16
	// beta: v1.20
	// GA: v1.24
	//
	// Deprecates and removes SelfLink from ObjectMeta and ListMeta.
	RemoveSelfLink featuregate.Feature = "RemoveSelfLink"

	// owner: @apelisse, @lavalamp
	// alpha: v1.14
	// beta: v1.16
	// stable: v1.22
	//
	// Server-side apply. Merging happens on the server.
	ServerSideApply featuregate.Feature = "ServerSideApply"

	// owner: @kevindelgado
	// kep: http://kep.k8s.io/2885
	// alpha: v1.23
	// beta: v1.24
	//
	// Enables server-side field validation.
	ServerSideFieldValidation featuregate.Feature = "ServerSideFieldValidation"

	// owner: @caesarxuchao @roycaihw
	// alpha: v1.20
	//
	// Enable the storage version API.
	StorageVersionAPI featuregate.Feature = "StorageVersionAPI"

	// owner: @caesarxuchao
	// alpha: v1.14
	// beta: v1.15
	//
	// Allow apiservers to expose the storage version hash in the discovery
	// document.
	StorageVersionHash featuregate.Feature = "StorageVersionHash"

	// owner: @wojtek-t
	// alpha: v1.15
	// beta: v1.16
	// GA: v1.17
	//
	// Enables support for watch bookmark events.
	WatchBookmark featuregate.Feature = "WatchBookmark"
)

func init() {
	runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultKubernetesFeatureGates))
}

// defaultKubernetesFeatureGates consists of all known Kubernetes-specific feature keys.
// To add a new feature, define a key for it above and add it here. The features will be
// available throughout Kubernetes binaries.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
	APIListChunking: {Default: true, PreRelease: featuregate.Beta},

	APIPriorityAndFairness: {Default: true, PreRelease: featuregate.Beta},

	APIResponseCompression: {Default: true, PreRelease: featuregate.Beta},

	APIServerIdentity: {Default: false, PreRelease: featuregate.Alpha},

	APIServerTracing: {Default: false, PreRelease: featuregate.Alpha},

	AdvancedAuditing: {Default: true, PreRelease: featuregate.GA},

	CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.Beta},

	DryRun: {Default: true, PreRelease: featuregate.GA},

	EfficientWatchResumption: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},

	OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta},

	OpenAPIV3: {Default: true, PreRelease: featuregate.Beta},

	RemainingItemCount: {Default: true, PreRelease: featuregate.Beta},

	RemoveSelfLink: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},

	ServerSideApply: {Default: true, PreRelease: featuregate.GA},

	ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta},

	StorageVersionAPI: {Default: false, PreRelease: featuregate.Alpha},

	StorageVersionHash: {Default: true, PreRelease: featuregate.Beta},

	WatchBookmark: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
}
<|endoftext|>


<commit_before>/* Utility functions and methods. Should probably absorbe what's in "common.go"
 * right now. */

/*
 * Copyright (c) 2013-2017, Jeremy Bingham (<jeremy@goiardi.gl>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
Package util contains various utility functions that are useful across all of goiardi.
*/
package util

import (
	"fmt"
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/gerror"
	"net/http"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

// NoDBConfigured is an error for when no database has been configured for use,
// yet an SQL function is being called.
var NoDBConfigured = gerror.StatusError("no db configured, but you tried to use one", http.StatusInternalServerError)

// GoiardiObj is an interface for helping goiardi/chef objects, like cookbooks,
// roles, etc., be able to easily make URLs and be identified by name.
type GoiardiObj interface {
	GetName() string
	URLType() string
}

// Gerror is an error type that wraps around the goiardi Error type.
type Gerror interface {
	gerror.Error
}

// Errorf creates a new Gerror, with a formatted error string. A convenience
// wrapper around error.Errorf.
func Errorf(format string, a ...interface{}) Gerror {
	return gerror.Errorf(format, a...)
}

// CastErr will easily cast a different kind of error to a Gerror. A convenience
// wrapper around error.CastErr.
func CastErr(err error) Gerror {
	return gerror.CastErr(err)
}

// ObjURL crafts a URL for an object.
func ObjURL(obj GoiardiObj) string {
	baseURL := config.ServerBaseURL()
	fullURL := fmt.Sprintf("%s/%s/%s", baseURL, obj.URLType(), obj.GetName())
	return fullURL
}

// CustomObjURL crafts a URL for a Goiardi object with additional path elements.
func CustomObjURL(obj GoiardiObj, path string) string {
	chkPath(&path)
	return fmt.Sprintf("%s%s", ObjURL(obj), path)
}

// CustomURL crafts a URL from the provided path, without providing an object.
func CustomURL(path string) string {
	chkPath(&path)
	return fmt.Sprintf("%s%s", config.ServerBaseURL(), path)
}

func chkPath(p *string) {
	if (*p)[0] != '/' {
		*p = fmt.Sprintf("/%s", *p)
	}
}

// FlattenObj flattens an object and expand its keys into a map[string]string so
// it's suitable for indexing, either with solr (eventually) or with the whipped
// up replacement for local mode. Objects fed into this function *must* have the
// "json" tag set for their struct members.
func FlattenObj(obj interface{}) map[string]interface{} {
	expanded := make(map[string]interface{})
	s := reflect.ValueOf(obj).Elem()
	for i := 0; i < s.NumField(); i++ {
		if !s.Field(i).CanInterface() {
			continue
		}
		v := s.Field(i).Interface()
		key := s.Type().Field(i).Tag.Get("json")
		var mergeKey string
		if key == "automatic" || key == "normal" || key == "default" || key == "override" || key == "raw_data" {
			mergeKey = ""
		} else {
			mergeKey = key
		}
		subExpand := DeepMerge(mergeKey, v)
		/* Now merge the returned map */
		for k, u := range subExpand {
			expanded[k] = u
		}
	}
	return expanded
}

// MapifyObject turns an object into a map[string]interface{}. Useful for when
// you have a slice of objects that you need to trim, mutilate, fold, etc.
// before returning them as JSON.
func MapifyObject(obj interface{}) map[string]interface{} {
	mapified := make(map[string]interface{})
	s := reflect.ValueOf(obj).Elem()
	for i := 0; i < s.NumField(); i++ {
		if !s.Field(i).CanInterface() {
			continue
		}
		v := s.Field(i).Interface()
		key := s.Type().Field(i).Tag.Get("json")
		mapified[key] = v
	}
	return mapified
}

// Indexify prepares a flattened object for indexing by turning it into a sorted
// slice of strings formatted like "key:value".
func Indexify(flattened map[string]interface{}) []string {
	var readyToIndex []string
	// keep values in the index down to a reasonable size
	maxValLen := config.Config.IndexValTrim
	for k, v := range flattened {
		switch v := v.(type) {
		case string:
			//v = IndexEscapeStr(v)
			v = TrimStringMax(v, maxValLen)
			line := fmt.Sprintf("%s:%s", k, v)
			readyToIndex = append(readyToIndex, line)
		case []string:
			sort.Strings(v)
			v = RemoveDupStrings(v)
			for _, w := range v {
				//w = IndexEscapeStr(w)
				w = TrimStringMax(w, maxValLen)
				line := fmt.Sprintf("%s:%s", k, w)
				readyToIndex = append(readyToIndex, line)
			}
		default:
			err := fmt.Errorf("We should never have been able to reach this state. Key %s had a value %v of type %T", k, v, v)
			panic(err)
		}
	}
	sort.Strings(readyToIndex)
	return readyToIndex
}

// IndexEscapeStr escapes values to index in the database, so characters that
// need to be escaped for Solr are properly found when using the trie or
// postgres based searches.
func IndexEscapeStr(s string) string {
	s = strings.Replace(s, "[", "\\[", -1)
	s = strings.Replace(s, "]", "\\]", -1)
	s = strings.Replace(s, "::", "\\:\\:", -1)
	return s
}

// DeepMerge merges disparate data structures into a flat hash.
func DeepMerge(key string, source interface{}) map[string]interface{} {
	merger := make(map[string]interface{})
	var sep string
	if config.Config.DotSearch {
		sep = "."
	} else {
		sep = "_"
	}
	switch v := source.(type) {
	case map[string]interface{}:
		/* We also need to get things like
		 * "default_attributes:key" indexed. */
		topLev := make([]string, len(v))
		n := 0
		for k, u := range v {
			if key != "" && !config.Config.UsePostgreSQL {
				topLev[n] = k
				n++
			}
			var nkey string
			if key == "" {
				nkey = k
			} else {
				nkey = fmt.Sprintf("%s%s%s", key, sep, k)
			}
			nm := DeepMerge(nkey, u)
			for j, q := range nm {
				merger[j] = q
			}
		}
		if key != "" && !config.Config.UsePostgreSQL {
			merger[key] = topLev
		}
	case map[string]string:
		/* We also need to get things like
		 * "default_attributes:key" indexed. */
		topLev := make([]string, len(v))
		n := 0
		for k, u := range v {
			if key != "" && !config.Config.UsePostgreSQL {
				topLev[n] = k
				n++
			}
			var nkey string
			if key == "" {
				nkey = k
			} else {
				nkey = fmt.Sprintf("%s%s%s", key, sep, k)
			}
			merger[nkey] = u
		}
		if key != "" && !config.Config.UsePostgreSQL {
			merger[key] = topLev
		}

	case []interface{}:
		km := make([]string, len(v))
		for i, w := range v {
			km[i] = stringify(w)
		}
		merger[key] = km
	case []string:
		km := make([]string, len(v))
		for i, w := range v {
			km[i] = stringify(w)
		}
		merger[key] = km
		/* If this is the run list, break recipes and roles out
		 * into their own separate indexes as well. */
		if key == "run_list" {
			roleMatch := regexp.MustCompile(`^(recipe|role)\[(.*)\]`)
			var roles []string
			var recipes []string
			for _, w := range v {
				rItem := roleMatch.FindStringSubmatch(stringify(w))
				if rItem != nil {
					rType := rItem[1]
					rThing := rItem[2]
					if rType == "role" {
						roles = append(roles, rThing)
					} else if rType == "recipe" {
						recipes = append(recipes, rThing)
					}
				}
			}
			if len(roles) > 0 {
				merger["role"] = roles
			}
			if len(recipes) > 0 {
				merger["recipe"] = recipes
			}
		}
	default:
		merger[key] = stringify(v)
	}
	return merger
}

func stringify(source interface{}) string {
	switch s := source.(type) {
	case string:
		return s
	case uint8, uint16, uint32, uint64:
		n := reflect.ValueOf(s).Uint()
		str := strconv.FormatUint(n, 10)
		return str
	case int8, int16, int32, int64:
		n := reflect.ValueOf(s).Int()
		str := strconv.FormatInt(n, 10)
		return str
	case float32, float64:
		n := reflect.ValueOf(s).Float()
		str := strconv.FormatFloat(n, 'f', -1, 64)
		return str
	case bool:
		str := strconv.FormatBool(s)
		return str
	default:
		/* Just send back whatever %v gives */
		str := fmt.Sprintf("%v", s)
		return str
	}
}

// PgSearchKey removes characters from search term fields that make the ltree
// data type unhappy. This leads to the postgres-based search being, perhaps,
// somewhat less precise than the solr (or ersatz solr) based search, but at the
// same time one that's less resource demanding and covers almost all known use
// cases. Potential bug: Postgres considers some, but not all, unicode letters
// as being alphanumeric; i.e. golang and postgres both consider 'ü' to be a
// letter, but golang accepts 'ሀ' as a letter while postgres does not. This is
// reasonably unlikely to be an issue, but if you're using lots of non-European
// characters in your attributes this could be a problem. We're accepting more
// than raw ASCII alnum however because it's better behavior and because
// Postgres does accept at least some other alphabets as being alphanumeric.
func PgSearchKey(key string) string {
	re := regexp.MustCompile(`[^\pL\pN_\.]`)
	bs := regexp.MustCompile(`_{2,}`)
	ps := regexp.MustCompile(`\.{2,}`) // repeated . will cause trouble too
	return pgKeyReplace(key, re, bs, ps)
}

// PgSearchQueryKey is very similar to PgSearchKey, except that it preserves the
// Solr wildcard charactes '*' and '?' in the queries.
func PgSearchQueryKey(key string) string {
	re := regexp.MustCompile(`[^\pL\pN_\.\*\?]`)
	bs := regexp.MustCompile(`_{2,}`)
	ps := regexp.MustCompile(`\.{2,}`)
	return pgKeyReplace(key, re, bs, ps)
}

func pgKeyReplace(key string, re, bs, ps *regexp.Regexp) string {
	k := re.ReplaceAllString(key, "_")
	k = bs.ReplaceAllString(k, "_")
	k = ps.ReplaceAllString(k, ".")
	k = strings.Trim(k, "_")
	// on the off hand chance we get leading or trailing dots
	k = strings.Trim(k, ".")
	// finally, if converting search query syntax, convert all _ to '.'.
	// This may need to be revisited in more detail if we find ourselves
	// needing more finesse with escaping underscores.
	if config.Config.ConvertSearch {
		k = strings.Replace(k, "_", ".", -1)
		k = ps.ReplaceAllString(k, ".")
	}
	return k
}
<commit_msg>object flattening changes from over the eclipse weekend<commit_after>/* Utility functions and methods. Should probably absorbe what's in "common.go"
 * right now. */

/*
 * Copyright (c) 2013-2017, Jeremy Bingham (<jeremy@goiardi.gl>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
Package util contains various utility functions that are useful across all of goiardi.
*/
package util

import (
	"fmt"
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/gerror"
	"net/http"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"log"
)

// NoDBConfigured is an error for when no database has been configured for use,
// yet an SQL function is being called.
var NoDBConfigured = gerror.StatusError("no db configured, but you tried to use one", http.StatusInternalServerError)

// GoiardiObj is an interface for helping goiardi/chef objects, like cookbooks,
// roles, etc., be able to easily make URLs and be identified by name.
type GoiardiObj interface {
	GetName() string
	URLType() string
}

// Gerror is an error type that wraps around the goiardi Error type.
type Gerror interface {
	gerror.Error
}

// Errorf creates a new Gerror, with a formatted error string. A convenience
// wrapper around error.Errorf.
func Errorf(format string, a ...interface{}) Gerror {
	return gerror.Errorf(format, a...)
}

// CastErr will easily cast a different kind of error to a Gerror. A convenience
// wrapper around error.CastErr.
func CastErr(err error) Gerror {
	return gerror.CastErr(err)
}

// ObjURL crafts a URL for an object.
func ObjURL(obj GoiardiObj) string {
	baseURL := config.ServerBaseURL()
	fullURL := fmt.Sprintf("%s/%s/%s", baseURL, obj.URLType(), obj.GetName())
	return fullURL
}

// CustomObjURL crafts a URL for a Goiardi object with additional path elements.
func CustomObjURL(obj GoiardiObj, path string) string {
	chkPath(&path)
	return fmt.Sprintf("%s%s", ObjURL(obj), path)
}

// CustomURL crafts a URL from the provided path, without providing an object.
func CustomURL(path string) string {
	chkPath(&path)
	return fmt.Sprintf("%s%s", config.ServerBaseURL(), path)
}

func chkPath(p *string) {
	if (*p)[0] != '/' {
		*p = fmt.Sprintf("/%s", *p)
	}
}

// FlattenObj flattens an object and expand its keys into a map[string]string so
// it's suitable for indexing, either with solr (eventually) or with the whipped
// up replacement for local mode. Objects fed into this function *must* have the
// "json" tag set for their struct members.
func FlattenObj(obj interface{}) map[string]interface{} {
	expanded := make(map[string]interface{})
	s := reflect.ValueOf(obj).Elem()
	for i := 0; i < s.NumField(); i++ {
		if !s.Field(i).CanInterface() {
			continue
		}
		v := s.Field(i).Interface()
		key := s.Type().Field(i).Tag.Get("json")
		var mergeKey string
		if key == "automatic" || key == "normal" || key == "default" || key == "override" || key == "raw_data" {
			mergeKey = ""
		} else {
			mergeKey = key
		}
		subExpand := DeepMerge(mergeKey, v)
		/* Now merge the returned map */
		for k, u := range subExpand {
			expanded[k] = u
		}
	}
	return expanded
}

// MapifyObject turns an object into a map[string]interface{}. Useful for when
// you have a slice of objects that you need to trim, mutilate, fold, etc.
// before returning them as JSON.
func MapifyObject(obj interface{}) map[string]interface{} {
	mapified := make(map[string]interface{})
	s := reflect.ValueOf(obj).Elem()
	for i := 0; i < s.NumField(); i++ {
		if !s.Field(i).CanInterface() {
			continue
		}
		v := s.Field(i).Interface()
		key := s.Type().Field(i).Tag.Get("json")
		mapified[key] = v
	}
	return mapified
}

// Indexify prepares a flattened object for indexing by turning it into a sorted
// slice of strings formatted like "key:value".
func Indexify(flattened map[string]interface{}) []string {
	var readyToIndex []string
	// keep values in the index down to a reasonable size
	maxValLen := config.Config.IndexValTrim
	for k, v := range flattened {
		switch v := v.(type) {
		case string:
			//v = IndexEscapeStr(v)
			v = TrimStringMax(v, maxValLen)
			line := fmt.Sprintf("%s:%s", k, v)
			readyToIndex = append(readyToIndex, line)
		case []string:
			sort.Strings(v)
			v = RemoveDupStrings(v)
			for _, w := range v {
				//w = IndexEscapeStr(w)
				w = TrimStringMax(w, maxValLen)
				line := fmt.Sprintf("%s:%s", k, w)
				readyToIndex = append(readyToIndex, line)
			}
		default:
			err := fmt.Errorf("We should never have been able to reach this state. Key %s had a value %v of type %T", k, v, v)
			panic(err)
		}
	}
	sort.Strings(readyToIndex)
	return readyToIndex
}

// IndexEscapeStr escapes values to index in the database, so characters that
// need to be escaped for Solr are properly found when using the trie or
// postgres based searches.
func IndexEscapeStr(s string) string {
	s = strings.Replace(s, "[", "\\[", -1)
	s = strings.Replace(s, "]", "\\]", -1)
	s = strings.Replace(s, "::", "\\:\\:", -1)
	return s
}

// DeepMerge merges disparate data structures into a flat hash.
func DeepMerge(key string, source interface{}) map[string]interface{} {
	merger := make(map[string]interface{})
	var sep string
	if config.Config.DotSearch {
		sep = "."
	} else {
		sep = "_"
	}
	switch v := source.(type) {
	case map[string]interface{}:
		/* We also need to get things like
		 * "default_attributes:key" indexed. */
		topLev := make([]string, len(v))
		n := 0
		for k, u := range v {
			if key != "" && !config.Config.UsePostgreSQL {
				topLev[n] = k
				n++
			}
			nkey := getNKey(key, k, sep)
			nm := DeepMerge(nkey, u)
			for j, q := range nm {
				merger[j] = q
			}
		}
		if key != "" && !config.Config.UsePostgreSQL {
			merger[key] = topLev
		}
	case map[string]string:
		/* We also need to get things like
		 * "default_attributes:key" indexed. */
		topLev := make([]string, len(v))
		n := 0
		for k, u := range v {
			if key != "" && !config.Config.UsePostgreSQL {
				topLev[n] = k
				n++
			}
			nkey := getNKey(key, k, sep)

			merger[nkey] = u
		}
		if key != "" && !config.Config.UsePostgreSQL {
			merger[key] = topLev
		}

	case []interface{}:
		km := make([]string, 0, len(v))
		mapMerge := make(map[string][]string)
		for i, w := range v {
			// If it's an array of maps or arrays, deep merge them
			// properly. Otherwise, stringify as best we can.
			vRef := reflect.ValueOf(w)
			if vRef.Kind() == reflect.Map {
				interMap := DeepMerge("", w)
				log.Printf("[]interface{} %d map: %+v", i, interMap)
				for imk, imv := range interMap {
					nk := getNKey(key, imk, sep)
					// Anything that's come back from
					// DeepMerge should be a string.
					mapMerge[nk] = mergeInterfaceMapChildren(mapMerge[nk], imv)
				}
			/*
			} else if vRef.Kind() == reflect.Slice {
				for _, sv := range wSlice {
					smMerge := DeepMerge("", sv)
					// WARNING: This *may* be a little iffy
					// still, there are some very weird
					// possibilities under this that need
					// more testing.
					for smk, smv := range smMerge {
						if smk == "" {
							km = mergeInterfaceMapChildren(km, smv)
						} else {
							nk := getNKey(key, smk, sep)
							mapMerge[nk] = mergeInterfaceMapChildren(mapMerge[nk], smv)
						}
					}
				}
			*/
			} else {
				s := stringify(w)
				km = append(km, s)
			}
		}
		for mmi, mmv := range mapMerge {
			merger[mmi] = mmv
		}
		merger[key] = km
	case []string:
		km := make([]string, len(v))
		for i, w := range v {
			km[i] = stringify(w)
		}
		merger[key] = km
		/* If this is the run list, break recipes and roles out
		 * into their own separate indexes as well. */
		if key == "run_list" {
			roleMatch := regexp.MustCompile(`^(recipe|role)\[(.*)\]`)
			var roles []string
			var recipes []string
			for _, w := range v {
				rItem := roleMatch.FindStringSubmatch(stringify(w))
				if rItem != nil {
					rType := rItem[1]
					rThing := rItem[2]
					if rType == "role" {
						roles = append(roles, rThing)
					} else if rType == "recipe" {
						recipes = append(recipes, rThing)
					}
				}
			}
			if len(roles) > 0 {
				merger["role"] = roles
			}
			if len(recipes) > 0 {
				merger["recipe"] = recipes
			}
		}
	default:
		merger[key] = stringify(v)
	}
	return merger
}

func getNKey(key string, subkey string, sep string) string {
	var nkey string
	if key == "" {
		nkey = subkey
	} else {
		nkey = strings.Join([]string{key,subkey}, sep)
	}
	return nkey
}

func mergeInterfaceMapChildren(strArr []string, val interface{}) []string {
	if reflect.ValueOf(val).Kind() == reflect.Slice {
		strArr = append(strArr, val.([]string)...)
	} else {
		strArr = append(strArr, val.(string))
	}
	return strArr
}

func stringify(source interface{}) string {
	switch s := source.(type) {
	case string:
		return s
	case uint8, uint16, uint32, uint64:
		n := reflect.ValueOf(s).Uint()
		str := strconv.FormatUint(n, 10)
		return str
	case int8, int16, int32, int64:
		n := reflect.ValueOf(s).Int()
		str := strconv.FormatInt(n, 10)
		return str
	case float32, float64:
		n := reflect.ValueOf(s).Float()
		str := strconv.FormatFloat(n, 'f', -1, 64)
		return str
	case bool:
		str := strconv.FormatBool(s)
		return str
	default:
		/* Just send back whatever %v gives */
		str := fmt.Sprintf("%v", s)
		return str
	}
}

// PgSearchKey removes characters from search term fields that make the ltree
// data type unhappy. This leads to the postgres-based search being, perhaps,
// somewhat less precise than the solr (or ersatz solr) based search, but at the
// same time one that's less resource demanding and covers almost all known use
// cases. Potential bug: Postgres considers some, but not all, unicode letters
// as being alphanumeric; i.e. golang and postgres both consider 'ü' to be a
// letter, but golang accepts 'ሀ' as a letter while postgres does not. This is
// reasonably unlikely to be an issue, but if you're using lots of non-European
// characters in your attributes this could be a problem. We're accepting more
// than raw ASCII alnum however because it's better behavior and because
// Postgres does accept at least some other alphabets as being alphanumeric.
func PgSearchKey(key string) string {
	re := regexp.MustCompile(`[^\pL\pN_\.]`)
	bs := regexp.MustCompile(`_{2,}`)
	ps := regexp.MustCompile(`\.{2,}`) // repeated . will cause trouble too
	return pgKeyReplace(key, re, bs, ps)
}

// PgSearchQueryKey is very similar to PgSearchKey, except that it preserves the
// Solr wildcard charactes '*' and '?' in the queries.
func PgSearchQueryKey(key string) string {
	re := regexp.MustCompile(`[^\pL\pN_\.\*\?]`)
	bs := regexp.MustCompile(`_{2,}`)
	ps := regexp.MustCompile(`\.{2,}`)
	return pgKeyReplace(key, re, bs, ps)
}

func pgKeyReplace(key string, re, bs, ps *regexp.Regexp) string {
	k := re.ReplaceAllString(key, "_")
	k = bs.ReplaceAllString(k, "_")
	k = ps.ReplaceAllString(k, ".")
	k = strings.Trim(k, "_")
	// on the off hand chance we get leading or trailing dots
	k = strings.Trim(k, ".")
	// finally, if converting search query syntax, convert all _ to '.'.
	// This may need to be revisited in more detail if we find ourselves
	// needing more finesse with escaping underscores.
	if config.Config.ConvertSearch {
		k = strings.Replace(k, "_", ".", -1)
		k = ps.ReplaceAllString(k, ".")
	}
	return k
}
<|endoftext|>