<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 提供对代码块的语法进行解析
package syntax

import (
	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/types"
	"github.com/caixw/apidoc/vars"
	"github.com/issue9/is"
)

// Parse 分析一段代码，并将结果保存到 d 中。
//
// 若代码块没有 api 文档定义，则会返回空值。
// data 该代码块的内容；
func Parse(d *types.Doc, data []rune) *types.SyntaxError {
	l := newLexer(data)

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIDoc):
			return l.scanAPIDoc(d)
		case l.matchTag(vars.API):
			return l.scanAPI(d)
		case l.match(vars.API): // 不认识标签
			return l.syntaxError(locale.ErrUnknownTopTag, l.readWord())
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for

	return nil
}

// 解析 @apidoc 及其子标签
//
// @apidoc title of doc
// @apiVersion 2.0
// @apiBaseURL https://api.caixw.io
// @apiLicense MIT https://opensource.org/licenses/MIT
//
// @apiContent
// content1
// content2
func (l *lexer) scanAPIDoc(d *types.Doc) *types.SyntaxError {
	if len(d.Title) > 0 || len(d.Version) > 0 {
		return l.syntaxError(locale.ErrDuplicateTag, vars.APIDoc)
	}

	t := l.readTag()
	d.Title = t.readLine()
	if len(d.Title) == 0 {
		return l.syntaxError(locale.ErrTagArgNotEnough, vars.APIDoc)
	}
	if !t.atEOF() {
		return l.syntaxError(locale.ErrTagArgTooMuch, vars.APIDoc)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIVersion):
			t := l.readTag()
			d.Version = t.readLine()
			if len(d.Version) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIVersion)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIVersion)
			}
		case l.matchTag(vars.APIBaseURL):
			t := l.readTag()
			d.BaseURL = t.readLine()
			if len(d.BaseURL) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIBaseURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIBaseURL)
			}
		case l.matchTag(vars.APILicense):
			t := l.readTag()
			d.LicenseName = t.readWord()
			d.LicenseURL = t.readLine()
			if len(d.LicenseName) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APILicense)
			}
			if len(d.LicenseURL) > 0 && !is.URL(d.LicenseURL) {
				return t.syntaxError(locale.ErrSecondArgMustURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APILicense)
			}
		case l.matchTag(vars.APIContent):
			d.Content = string(l.data[l.pos:])
		case l.match(vars.API): // 不认识的标签
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return nil
}

// 解析 @api 及其子标签
func (l *lexer) scanAPI(d *types.Doc) (err *types.SyntaxError) {
	api := &types.API{}
	t := l.readTag()
	api.Method = t.readWord()
	api.URL = t.readWord()
	api.Summary = t.readLine()

	if len(api.Method) == 0 || len(api.URL) == 0 || len(api.Summary) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.API)
	}

	api.Description = t.readEnd()
LOOP:
	for {
		switch {
		case l.matchTag(vars.APIIgnore):
			return nil
		case l.matchTag(vars.APIGroup):
			err = l.scanGroup(api)
		case l.matchTag(vars.APIQuery):
			err = l.scanAPIQueries(api)
		case l.matchTag(vars.APIParam):
			err = l.scanAPIParams(api)
		case l.matchTag(vars.APIRequest):
			err = l.scanAPIRequest(api)
		case l.matchTag(vars.APIError):
			api.Error, err = l.scanResponse(vars.APIError)
		case l.matchTag(vars.APISuccess):
			api.Success, err = l.scanResponse(vars.APISuccess)
		case l.match(vars.API): // 不认识的标签
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}

		if err != nil {
			return err
		}
	}

	if api.Success == nil {
		return &types.SyntaxError{Message: locale.ErrSuccessNotEmpty}
	}

	if len(api.Group) == 0 {
		api.Group = vars.DefaultGroupName
	}

	d.NewAPI(api)
	return nil
}

func (l *lexer) scanGroup(api *types.API) *types.SyntaxError {
	t := l.readTag()

	api.Group = t.readWord()
	if len(api.Group) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIGroup)
	}

	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIGroup)
	}

	return nil
}

func (l *lexer) scanAPIQueries(api *types.API) *types.SyntaxError {
	if api.Queries == nil {
		api.Queries = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIQuery)
	if err != nil {
		return err
	}
	api.Queries = append(api.Queries, p)
	return nil
}

func (l *lexer) scanAPIParams(api *types.API) *types.SyntaxError {
	if api.Params == nil {
		api.Params = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIParam)
	if err != nil {
		return err
	}
	api.Params = append(api.Params, p)
	return nil
}

// 解析 @apiRequest 及其子标签
func (l *lexer) scanAPIRequest(api *types.API) *types.SyntaxError {
	t := l.readTag()
	r := &types.Request{
		Type:     t.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}
	if !t.atEOF() {
		return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIRequest)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			r.Headers[string(key)] = string(val)
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return err
			}
			r.Params = append(r.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return err
			}
			r.Examples = append(r.Examples, e)
		case l.match(vars.API): // 其它api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。

		} // end switch
	} // end for

	api.Request = r
	return nil
}

// 解析 @apiSuccess 或是 @apiError 及其子标签。
func (l *lexer) scanResponse(tagName string) (*types.Response, *types.SyntaxError) {
	tag := l.readTag()
	resp := &types.Response{
		Code:     tag.readWord(),
		Summary:  tag.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}

	if len(resp.Code) == 0 || len(resp.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	if !tag.atEOF() {
		return nil, tag.syntaxError(locale.ErrTagArgTooMuch, tagName)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return nil, t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return nil, t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			resp.Headers[key] = val
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return nil, err
			}
			resp.Params = append(resp.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return nil, err
			}
			resp.Examples = append(resp.Examples, e)
		case l.match(vars.API): // 其它api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return resp, nil
}

// 解析 @apiExample 标签
func (l *lexer) scanAPIExample() (*types.Example, *types.SyntaxError) {
	tag := l.readTag()
	example := &types.Example{
		Type: tag.readWord(),
		Code: tag.readEnd(),
	}

	if len(example.Type) == 0 || len(example.Code) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, vars.APIExample)
	}

	return example, nil
}

// 解析 @apiParam 标签
func (l *lexer) scanAPIParam(tagName string) (*types.Param, *types.SyntaxError) {
	p := &types.Param{}

	tag := l.readTag()
	p.Name = tag.readWord()
	p.Type = tag.readWord()
	p.Summary = tag.readEnd()
	if len(p.Name) == 0 || len(p.Type) == 0 || len(p.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	return p, nil
}
<commit_msg>去掉不必要的跳转代码<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 提供对代码块的语法进行解析
package syntax

import (
	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/types"
	"github.com/caixw/apidoc/vars"
	"github.com/issue9/is"
)

// Parse 分析一段代码，并将结果保存到 d 中。
//
// 若代码块没有 api 文档定义，则会返回空值。
// data 该代码块的内容；
func Parse(d *types.Doc, data []rune) *types.SyntaxError {
	l := newLexer(data)

	for {
		switch {
		case l.matchTag(vars.APIDoc):
			return l.scanAPIDoc(d)
		case l.matchTag(vars.API):
			return l.scanAPI(d)
		case l.match(vars.API): // 不认识标签
			return l.syntaxError(locale.ErrUnknownTopTag, l.readWord())
		default:
			if l.atEOF() {
				return nil
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @apidoc 及其子标签
//
// @apidoc title of doc
// @apiVersion 2.0
// @apiBaseURL https://api.caixw.io
// @apiLicense MIT https://opensource.org/licenses/MIT
//
// @apiContent
// content1
// content2
func (l *lexer) scanAPIDoc(d *types.Doc) *types.SyntaxError {
	if len(d.Title) > 0 || len(d.Version) > 0 {
		return l.syntaxError(locale.ErrDuplicateTag, vars.APIDoc)
	}

	t := l.readTag()
	d.Title = t.readLine()
	if len(d.Title) == 0 {
		return l.syntaxError(locale.ErrTagArgNotEnough, vars.APIDoc)
	}
	if !t.atEOF() {
		return l.syntaxError(locale.ErrTagArgTooMuch, vars.APIDoc)
	}

	for {
		switch {
		case l.matchTag(vars.APIVersion):
			t := l.readTag()
			d.Version = t.readLine()
			if len(d.Version) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIVersion)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIVersion)
			}
		case l.matchTag(vars.APIBaseURL):
			t := l.readTag()
			d.BaseURL = t.readLine()
			if len(d.BaseURL) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIBaseURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIBaseURL)
			}
		case l.matchTag(vars.APILicense):
			t := l.readTag()
			d.LicenseName = t.readWord()
			d.LicenseURL = t.readLine()
			if len(d.LicenseName) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APILicense)
			}
			if len(d.LicenseURL) > 0 && !is.URL(d.LicenseURL) {
				return t.syntaxError(locale.ErrSecondArgMustURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APILicense)
			}
		case l.matchTag(vars.APIContent):
			d.Content = string(l.data[l.pos:])
		case l.match(vars.API): // 不认识的标签
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				return nil
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @api 及其子标签
func (l *lexer) scanAPI(d *types.Doc) (err *types.SyntaxError) {
	api := &types.API{}
	t := l.readTag()
	api.Method = t.readWord()
	api.URL = t.readWord()
	api.Summary = t.readLine()

	if len(api.Method) == 0 || len(api.URL) == 0 || len(api.Summary) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.API)
	}

	api.Description = t.readEnd()
LOOP:
	for {
		switch {
		case l.matchTag(vars.APIIgnore):
			return nil
		case l.matchTag(vars.APIGroup):
			err = l.scanGroup(api)
		case l.matchTag(vars.APIQuery):
			err = l.scanAPIQueries(api)
		case l.matchTag(vars.APIParam):
			err = l.scanAPIParams(api)
		case l.matchTag(vars.APIRequest):
			err = l.scanAPIRequest(api)
		case l.matchTag(vars.APIError):
			api.Error, err = l.scanResponse(vars.APIError)
		case l.matchTag(vars.APISuccess):
			api.Success, err = l.scanResponse(vars.APISuccess)
		case l.match(vars.API): // 不认识的标签
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}

		if err != nil {
			return err
		}
	}

	if api.Success == nil {
		return &types.SyntaxError{Message: locale.ErrSuccessNotEmpty}
	}

	if len(api.Group) == 0 {
		api.Group = vars.DefaultGroupName
	}

	d.NewAPI(api)
	return nil
}

func (l *lexer) scanGroup(api *types.API) *types.SyntaxError {
	t := l.readTag()

	api.Group = t.readWord()
	if len(api.Group) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIGroup)
	}

	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIGroup)
	}

	return nil
}

func (l *lexer) scanAPIQueries(api *types.API) *types.SyntaxError {
	if api.Queries == nil {
		api.Queries = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIQuery)
	if err != nil {
		return err
	}
	api.Queries = append(api.Queries, p)
	return nil
}

func (l *lexer) scanAPIParams(api *types.API) *types.SyntaxError {
	if api.Params == nil {
		api.Params = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIParam)
	if err != nil {
		return err
	}
	api.Params = append(api.Params, p)
	return nil
}

// 解析 @apiRequest 及其子标签
func (l *lexer) scanAPIRequest(api *types.API) *types.SyntaxError {
	t := l.readTag()
	r := &types.Request{
		Type:     t.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}
	if !t.atEOF() {
		return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIRequest)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			r.Headers[string(key)] = string(val)
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return err
			}
			r.Params = append(r.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return err
			}
			r.Examples = append(r.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。

		} // end switch
	} // end for

	api.Request = r
	return nil
}

// 解析 @apiSuccess 或是 @apiError 及其子标签。
func (l *lexer) scanResponse(tagName string) (*types.Response, *types.SyntaxError) {
	tag := l.readTag()
	resp := &types.Response{
		Code:     tag.readWord(),
		Summary:  tag.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}

	if len(resp.Code) == 0 || len(resp.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	if !tag.atEOF() {
		return nil, tag.syntaxError(locale.ErrTagArgTooMuch, tagName)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return nil, t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return nil, t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			resp.Headers[key] = val
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return nil, err
			}
			resp.Params = append(resp.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return nil, err
			}
			resp.Examples = append(resp.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return resp, nil
}

// 解析 @apiExample 标签
func (l *lexer) scanAPIExample() (*types.Example, *types.SyntaxError) {
	tag := l.readTag()
	example := &types.Example{
		Type: tag.readWord(),
		Code: tag.readEnd(),
	}

	if len(example.Type) == 0 || len(example.Code) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, vars.APIExample)
	}

	return example, nil
}

// 解析 @apiParam 标签
func (l *lexer) scanAPIParam(tagName string) (*types.Param, *types.SyntaxError) {
	p := &types.Param{}

	tag := l.readTag()
	p.Name = tag.readWord()
	p.Type = tag.readWord()
	p.Summary = tag.readEnd()
	if len(p.Name) == 0 || len(p.Type) == 0 || len(p.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	return p, nil
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gempir/bitraft/pkg/helix"
	"github.com/gempir/bitraft/pkg/slice"
	"github.com/gempir/bitraft/pkg/store"
	"github.com/labstack/echo/v4"
	log "github.com/sirupsen/logrus"
)

type UserConfig struct {
	Editors   []string
	Protected Protected
}

type Protected struct {
	EditorFor     []string
	CurrentUserID string
}

func createDefaultUserConfig() UserConfig {
	return UserConfig{
		Editors: []string{},
		Protected: Protected{
			EditorFor:     []string{},
			CurrentUserID: "",
		},
	}
}

func (c *UserConfig) isEditorFor(user string) bool {
	for _, editor := range c.Protected.EditorFor {
		if editor == user {
			return true
		}
	}

	return false
}

func (c *UserConfig) getEditorDifference(newEditors []string) (removed []string, added []string) {
	return slice.Diff(c.Editors, newEditors)
}

func (s *Server) handleUserConfig(c echo.Context) error {
	auth, _, err := s.authenticate(c)
	if err != nil {
		return err
	}

	if c.Request().Method == http.MethodGet {
		userConfig := s.getUserConfig(auth.Data.UserID)

		if c.QueryParam("managing") != "" {
			ownerUserID, err := s.checkEditor(c, userConfig)
			if err != nil {
				return echo.NewHTTPError(http.StatusBadRequest, err.Error())
			}

			userConfig = s.getUserConfig(ownerUserID)
		}

		userConfig = s.convertUserConfig(userConfig, true)

		return c.JSON(http.StatusOK, userConfig)

	} else if c.Request().Method == http.MethodPost {
		body, err := ioutil.ReadAll(c.Request().Body)
		if err != nil {
			log.Errorf("Failed reading update body: %s", err)
			return echo.NewHTTPError(http.StatusInternalServerError, "Failure saving body "+err.Error())
		}

		var newConfig UserConfig
		if err := json.Unmarshal(body, &newConfig); err != nil {
			log.Errorf("Failed unmarshalling userConfig: %s", err)
			return echo.NewHTTPError(http.StatusInternalServerError, "Failure unmarshalling config "+err.Error())
		}

		if c.QueryParam("managing") != "" {
			return echo.NewHTTPError(http.StatusForbidden, "editors are not allowed to edit userConfig yet")
		}

		err = s.processConfig(auth.Data.UserID, newConfig, c)
		if err != nil {
			log.Errorf("failed processing config: %s", err)
			return echo.NewHTTPError(http.StatusBadRequest, "failed processing config: "+err.Error())
		}

		return c.JSON(http.StatusOK, nil)
	}

	return nil
}

func (s *Server) getUserConfig(userID string) UserConfig {
	var editors []store.Editor
	s.db.Where("owner_twitch_id = ? OR editor_twitch_id = ?", userID, userID).Find(&editors)

	uCfg := createDefaultUserConfig()
	uCfg.Protected.CurrentUserID = userID

	for _, editor := range editors {
		if editor.OwnerTwitchID == userID {
			uCfg.Editors = append(uCfg.Editors, editor.EditorTwitchID)
		}
		if editor.EditorTwitchID == userID {
			uCfg.Protected.EditorFor = append(uCfg.Protected.EditorFor, editor.OwnerTwitchID)
		}
	}

	return uCfg
}

func (s *Server) convertUserConfig(uCfg UserConfig, toNames bool) UserConfig {
	all := uCfg.Editors
	all = append(all, uCfg.Protected.EditorFor...)

	var err error
	var userData map[string]helix.UserData
	if toNames {
		userData, err = s.helixClient.GetUsersByUserIds(all)
	} else {
		userData, err = s.helixClient.GetUsersByUsernames(all)
	}
	if err != nil {
		log.Errorf("Failed to get editors %s", err)
		return UserConfig{}
	}

	editors := []string{}
	for _, editor := range uCfg.Editors {
		data, ok := userData[editor]
		if !ok {
			continue
		}

		if toNames {
			editors = append(editors, data.Login)
		} else {
			editors = append(editors, data.ID)
		}
	}
	uCfg.Editors = editors

	editorFor := []string{}
	for _, editor := range uCfg.Protected.EditorFor {
		data, ok := userData[editor]
		if !ok {
			continue
		}

		if toNames {
			editorFor = append(editorFor, data.Login)
		} else {
			editorFor = append(editorFor, data.ID)
		}
	}
	uCfg.Protected.EditorFor = editorFor

	return uCfg
}

func (s *Server) checkEditor(c echo.Context, userConfig UserConfig) (string, error) {
	managing := c.QueryParam("managing")

	if managing == "" {
		return "", nil
	}

	userData, err := s.helixClient.GetUsersByUsernames([]string{managing})
	if err != nil || len(userData) == 0 {
		return "", errors.New("can't find editor")
	}

	isEditor := false
	for _, editor := range userConfig.Protected.EditorFor {
		if editor == userData[managing].ID {
			isEditor = true
		}
	}

	if !isEditor {
		return "", errors.New("User is not editor")
	}

	return userData[managing].ID, nil
}

func (s *Server) checkIsEditor(editorUserID string, ownerUserID string) error {
	if editorUserID == ownerUserID {
		return nil
	}

	userConfig := s.getUserConfig(ownerUserID)

	for _, editor := range userConfig.Editors {
		if editor == editorUserID {
			return nil
		}
	}

	return echo.NewHTTPError(http.StatusForbidden, "user is not editor")
}

func (s *Server) addEditors(ownerId string, userIds []string) {
	if len(userIds) == 0 {
		return
	}

	var editors []store.Editor
	for _, id := range userIds {
		editors = append(editors, store.Editor{OwnerTwitchID: ownerId, EditorTwitchID: id})
	}

	s.db.Create(&editors)
}

func (s *Server) removeEditors(ownerId string, userIds []string) {
	if len(userIds) == 0 {
		return
	}

	s.db.Delete(store.Editor{}, "editor_twitch_id IN (?) AND owner_user_id = ?", strings.Join(userIds, ","), ownerId)
}

func (s *Server) processConfig(userID string, newConfig UserConfig, c echo.Context) error {
	newUserIDConfig := s.convertUserConfig(newConfig, false)
	oldConfig := s.getUserConfig(userID)
	added, removed := oldConfig.getEditorDifference(newUserIDConfig.Editors)

	s.addEditors(userID, added)
	s.removeEditors(userID, removed)

	// for _, reward := range newConfig.Rewards {
	// 	if reward.GetType() == TYPE_BTTV {
	// 		oldRewardId := ""
	// 		for _, oldReward := range oldConfig.Rewards {
	// 			if oldReward.GetType() == TYPE_BTTV {
	// 				oldRewardId = oldReward.GetConfig().ID
	// 			}
	// 		}

	// 		_, err := s.createOrUpdateChannelPointReward(saveTarget, reward, oldRewardId)
	// 		if err != nil {
	// 			return UserConfig{}, err
	// 		}

	// 	}
	// }

	// if isNew {
	// 	log.Infof("Created new config for: %s, subscribing webhooks", userID)
	// 	s.subscribeChannelPoints(userID)
	// }

	return nil
}
<commit_msg>fix display issue for editor<commit_after>package server

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gempir/bitraft/pkg/helix"
	"github.com/gempir/bitraft/pkg/slice"
	"github.com/gempir/bitraft/pkg/store"
	"github.com/labstack/echo/v4"
	log "github.com/sirupsen/logrus"
)

type UserConfig struct {
	Editors   []string
	Protected Protected
}

type Protected struct {
	EditorFor     []string
	CurrentUserID string
}

func createDefaultUserConfig() UserConfig {
	return UserConfig{
		Editors: []string{},
		Protected: Protected{
			EditorFor:     []string{},
			CurrentUserID: "",
		},
	}
}

func (c *UserConfig) isEditorFor(user string) bool {
	for _, editor := range c.Protected.EditorFor {
		if editor == user {
			return true
		}
	}

	return false
}

func (c *UserConfig) getEditorDifference(newEditors []string) (removed []string, added []string) {
	return slice.Diff(c.Editors, newEditors)
}

func (s *Server) handleUserConfig(c echo.Context) error {
	auth, _, err := s.authenticate(c)
	if err != nil {
		return err
	}

	if c.Request().Method == http.MethodGet {
		userConfig := s.getUserConfig(auth.Data.UserID)

		if c.QueryParam("managing") != "" {
			ownerUserID, err := s.checkEditor(c, userConfig)
			if err != nil {
				return echo.NewHTTPError(http.StatusBadRequest, err.Error())
			}

			editorFor := userConfig.Protected.EditorFor
			userConfig = s.getUserConfig(ownerUserID)

			userConfig.Editors = []string{}
			userConfig.Protected.EditorFor = editorFor
		}

		userConfig = s.convertUserConfig(userConfig, true)

		return c.JSON(http.StatusOK, userConfig)

	} else if c.Request().Method == http.MethodPost {
		body, err := ioutil.ReadAll(c.Request().Body)
		if err != nil {
			log.Errorf("Failed reading update body: %s", err)
			return echo.NewHTTPError(http.StatusInternalServerError, "Failure saving body "+err.Error())
		}

		var newConfig UserConfig
		if err := json.Unmarshal(body, &newConfig); err != nil {
			log.Errorf("Failed unmarshalling userConfig: %s", err)
			return echo.NewHTTPError(http.StatusInternalServerError, "Failure unmarshalling config "+err.Error())
		}

		if c.QueryParam("managing") != "" {
			return echo.NewHTTPError(http.StatusForbidden, "editors are not allowed to edit userConfig yet")
		}

		err = s.processConfig(auth.Data.UserID, newConfig, c)
		if err != nil {
			log.Errorf("failed processing config: %s", err)
			return echo.NewHTTPError(http.StatusBadRequest, "failed processing config: "+err.Error())
		}

		return c.JSON(http.StatusOK, nil)
	}

	return nil
}

func (s *Server) getUserConfig(userID string) UserConfig {
	var editors []store.Editor
	s.db.Where("owner_twitch_id = ? OR editor_twitch_id = ?", userID, userID).Find(&editors)

	uCfg := createDefaultUserConfig()
	uCfg.Protected.CurrentUserID = userID

	for _, editor := range editors {
		if editor.OwnerTwitchID == userID {
			uCfg.Editors = append(uCfg.Editors, editor.EditorTwitchID)
		}
		if editor.EditorTwitchID == userID {
			uCfg.Protected.EditorFor = append(uCfg.Protected.EditorFor, editor.OwnerTwitchID)
		}
	}

	return uCfg
}

func (s *Server) convertUserConfig(uCfg UserConfig, toNames bool) UserConfig {
	all := uCfg.Editors
	all = append(all, uCfg.Protected.EditorFor...)

	var err error
	var userData map[string]helix.UserData
	if toNames {
		userData, err = s.helixClient.GetUsersByUserIds(all)
	} else {
		userData, err = s.helixClient.GetUsersByUsernames(all)
	}
	if err != nil {
		log.Errorf("Failed to get editors %s", err)
		return UserConfig{}
	}

	editors := []string{}
	for _, editor := range uCfg.Editors {
		data, ok := userData[editor]
		if !ok {
			continue
		}

		if toNames {
			editors = append(editors, data.Login)
		} else {
			editors = append(editors, data.ID)
		}
	}
	uCfg.Editors = editors

	editorFor := []string{}
	for _, editor := range uCfg.Protected.EditorFor {
		data, ok := userData[editor]
		if !ok {
			continue
		}

		if toNames {
			editorFor = append(editorFor, data.Login)
		} else {
			editorFor = append(editorFor, data.ID)
		}
	}
	uCfg.Protected.EditorFor = editorFor

	return uCfg
}

func (s *Server) checkEditor(c echo.Context, userConfig UserConfig) (string, error) {
	managing := c.QueryParam("managing")

	if managing == "" {
		return "", nil
	}

	userData, err := s.helixClient.GetUsersByUsernames([]string{managing})
	if err != nil || len(userData) == 0 {
		return "", errors.New("can't find editor")
	}

	isEditor := false
	for _, editor := range userConfig.Protected.EditorFor {
		if editor == userData[managing].ID {
			isEditor = true
		}
	}

	if !isEditor {
		return "", errors.New("User is not editor")
	}

	return userData[managing].ID, nil
}

func (s *Server) checkIsEditor(editorUserID string, ownerUserID string) error {
	if editorUserID == ownerUserID {
		return nil
	}

	userConfig := s.getUserConfig(ownerUserID)

	for _, editor := range userConfig.Editors {
		if editor == editorUserID {
			return nil
		}
	}

	return echo.NewHTTPError(http.StatusForbidden, "user is not editor")
}

func (s *Server) addEditors(ownerId string, userIds []string) {
	if len(userIds) == 0 {
		return
	}

	var editors []store.Editor
	for _, id := range userIds {
		editors = append(editors, store.Editor{OwnerTwitchID: ownerId, EditorTwitchID: id})
	}

	s.db.Create(&editors)
}

func (s *Server) removeEditors(ownerId string, userIds []string) {
	if len(userIds) == 0 {
		return
	}

	s.db.Delete(store.Editor{}, "editor_twitch_id IN (?) AND owner_user_id = ?", strings.Join(userIds, ","), ownerId)
}

func (s *Server) processConfig(userID string, newConfig UserConfig, c echo.Context) error {
	newUserIDConfig := s.convertUserConfig(newConfig, false)
	oldConfig := s.getUserConfig(userID)
	added, removed := oldConfig.getEditorDifference(newUserIDConfig.Editors)

	s.addEditors(userID, added)
	s.removeEditors(userID, removed)

	// for _, reward := range newConfig.Rewards {
	// 	if reward.GetType() == TYPE_BTTV {
	// 		oldRewardId := ""
	// 		for _, oldReward := range oldConfig.Rewards {
	// 			if oldReward.GetType() == TYPE_BTTV {
	// 				oldRewardId = oldReward.GetConfig().ID
	// 			}
	// 		}

	// 		_, err := s.createOrUpdateChannelPointReward(saveTarget, reward, oldRewardId)
	// 		if err != nil {
	// 			return UserConfig{}, err
	// 		}

	// 	}
	// }

	// if isNew {
	// 	log.Infof("Created new config for: %s, subscribing webhooks", userID)
	// 	s.subscribeChannelPoints(userID)
	// }

	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"io"

	"github.com/pachyderm/pachyderm/src/client/admin"
	"github.com/pachyderm/pachyderm/src/client/pkg/grpcutil"
	"github.com/pachyderm/pachyderm/src/client/pkg/pbutil"
)

// InspectCluster retrieves cluster state
func (c APIClient) InspectCluster() (*admin.ClusterInfo, error) {
	clusterInfo, err := c.AdminAPIClient.InspectCluster(c.Ctx(), nil)
	if err != nil {
		return nil, grpcutil.ScrubGRPC(err)
	}
	return clusterInfo, nil
}

// Extract all cluster state, call f with each operation.
func (c APIClient) Extract(objects bool, f func(op *admin.Op) error) error {
	extractClient, err := c.AdminAPIClient.Extract(c.Ctx(), &admin.ExtractRequest{NoObjects: !objects})
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	for {
		op, err := extractClient.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			return grpcutil.ScrubGRPC(err)
		}
		if err := f(op); err != nil {
			return err
		}
	}
	return nil
}

// ExtractAll cluster state as a slice of operations.
func (c APIClient) ExtractAll(objects bool) ([]*admin.Op, error) {
	var result []*admin.Op
	if err := c.Extract(objects, func(op *admin.Op) error {
		result = append(result, op)
		return nil
	}); err != nil {
		return nil, err
	}
	return result, nil
}

// ExtractWriter extracts all cluster state and marshals it to w.
func (c APIClient) ExtractWriter(objects bool, w io.Writer) error {
	writer := pbutil.NewWriter(w)
	return c.Extract(objects, func(op *admin.Op) error {
		return writer.Write(op)
	})
}

// ExtractURL extracts all cluster state and marshalls it to object storage.
func (c APIClient) ExtractURL(url string) error {
	extractClient, err := c.AdminAPIClient.Extract(c.Ctx(), &admin.ExtractRequest{URL: url})
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	resp, err := extractClient.Recv()
	if err == nil {
		return fmt.Errorf("unexpected response from extract: %v", resp)
	}
	if err != io.EOF {
		return err
	}
	return nil
}

// Restore cluster state from an extract series of operations.
func (c APIClient) Restore(ops []*admin.Op) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	for _, op := range ops {
		if err := restoreClient.Send(&admin.RestoreRequest{Op: op}); err != nil {
			return grpcutil.ScrubGRPC(err)
		}
	}
	return nil
}

// RestoreReader restores cluster state from a reader containing marshaled ops.
// Such as those written by ExtractWriter.
func (c APIClient) RestoreReader(r io.Reader) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	reader := pbutil.NewReader(r)
	op := &admin.Op{}
	for {
		if err := reader.Read(op); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		if err := restoreClient.Send(&admin.RestoreRequest{Op: op}); err != nil {
			return grpcutil.ScrubGRPC(err)
		}
	}
	return nil
}

// RestoreFrom restores state from another cluster which can be access through otherC.
func (c APIClient) RestoreFrom(objects bool, otherC *APIClient) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	return otherC.Extract(objects, func(op *admin.Op) error {
		return restoreClient.Send(&admin.RestoreRequest{Op: op})
	})
}

// RestoreURL restures cluster state from object storage.
func (c APIClient) RestoreURL(url string) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	return grpcutil.ScrubGRPC(restoreClient.Send(&admin.RestoreRequest{URL: url}))
}
<commit_msg>Adds ExtractPipeline to client.<commit_after>package client

import (
	"fmt"
	"io"

	"github.com/pachyderm/pachyderm/src/client/admin"
	"github.com/pachyderm/pachyderm/src/client/pkg/grpcutil"
	"github.com/pachyderm/pachyderm/src/client/pkg/pbutil"
	"github.com/pachyderm/pachyderm/src/client/pps"
)

// InspectCluster retrieves cluster state
func (c APIClient) InspectCluster() (*admin.ClusterInfo, error) {
	clusterInfo, err := c.AdminAPIClient.InspectCluster(c.Ctx(), nil)
	if err != nil {
		return nil, grpcutil.ScrubGRPC(err)
	}
	return clusterInfo, nil
}

// Extract all cluster state, call f with each operation.
func (c APIClient) Extract(objects bool, f func(op *admin.Op) error) error {
	extractClient, err := c.AdminAPIClient.Extract(c.Ctx(), &admin.ExtractRequest{NoObjects: !objects})
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	for {
		op, err := extractClient.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			return grpcutil.ScrubGRPC(err)
		}
		if err := f(op); err != nil {
			return err
		}
	}
	return nil
}

// ExtractAll cluster state as a slice of operations.
func (c APIClient) ExtractAll(objects bool) ([]*admin.Op, error) {
	var result []*admin.Op
	if err := c.Extract(objects, func(op *admin.Op) error {
		result = append(result, op)
		return nil
	}); err != nil {
		return nil, err
	}
	return result, nil
}

// ExtractWriter extracts all cluster state and marshals it to w.
func (c APIClient) ExtractWriter(objects bool, w io.Writer) error {
	writer := pbutil.NewWriter(w)
	return c.Extract(objects, func(op *admin.Op) error {
		return writer.Write(op)
	})
}

// ExtractURL extracts all cluster state and marshalls it to object storage.
func (c APIClient) ExtractURL(url string) error {
	extractClient, err := c.AdminAPIClient.Extract(c.Ctx(), &admin.ExtractRequest{URL: url})
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	resp, err := extractClient.Recv()
	if err == nil {
		return fmt.Errorf("unexpected response from extract: %v", resp)
	}
	if err != io.EOF {
		return err
	}
	return nil
}

func (c APIClient) ExtractPipeline(pipelineName string) (*pps.CreatePipelineRequest, error) {
	op, err := c.AdminAPIClient.ExtractPipeline(c.Ctx(), &admin.ExtractPipelineRequest{Pipeline: NewPipeline(pipelineName)})
	if err != nil {
		return nil, grpcutil.ScrubGRPC(err)
	}
	if op.Op1_7 == nil || op.Op1_7.Pipeline == nil {
		return nil, fmt.Errorf("malformed response is missing pipeline")
	}
	return op.Op1_7.Pipeline, nil
}

// Restore cluster state from an extract series of operations.
func (c APIClient) Restore(ops []*admin.Op) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	for _, op := range ops {
		if err := restoreClient.Send(&admin.RestoreRequest{Op: op}); err != nil {
			return grpcutil.ScrubGRPC(err)
		}
	}
	return nil
}

// RestoreReader restores cluster state from a reader containing marshaled ops.
// Such as those written by ExtractWriter.
func (c APIClient) RestoreReader(r io.Reader) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	reader := pbutil.NewReader(r)
	op := &admin.Op{}
	for {
		if err := reader.Read(op); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		if err := restoreClient.Send(&admin.RestoreRequest{Op: op}); err != nil {
			return grpcutil.ScrubGRPC(err)
		}
	}
	return nil
}

// RestoreFrom restores state from another cluster which can be access through otherC.
func (c APIClient) RestoreFrom(objects bool, otherC *APIClient) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	return otherC.Extract(objects, func(op *admin.Op) error {
		return restoreClient.Send(&admin.RestoreRequest{Op: op})
	})
}

// RestoreURL restures cluster state from object storage.
func (c APIClient) RestoreURL(url string) (retErr error) {
	restoreClient, err := c.AdminAPIClient.Restore(c.Ctx())
	if err != nil {
		return grpcutil.ScrubGRPC(err)
	}
	defer func() {
		if _, err := restoreClient.CloseAndRecv(); err != nil && retErr == nil {
			retErr = grpcutil.ScrubGRPC(err)
		}
	}()
	return grpcutil.ScrubGRPC(restoreClient.Send(&admin.RestoreRequest{URL: url}))
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path/filepath"
	"time"

	"github.com/codegangsta/cli"
	"github.com/davecgh/go-spew/spew"
)

var spd = spew.ConfigState{Indent: "\t"} //, DisableMethods: true}

var (
	GDS_CONFIG_DIR       = "$HOME/.config/gds"
	GDS_CONTEXT_FILENAME = "context_" + time.Now().Format(time.RFC3339) + ".json"
	GDS_CONFIG_NAME      = "config.yaml"
)

func main() {
	app := cli.NewApp()

	app.Name = "Ghetto Device Storage (gds)"
	app.Version = "0.0.1"
	app.Email = "jeezusjr@gmail.com"
	app.Author = "Jesus Alvarez"
	app.Usage = "Large data backups to dissimilar devices."

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config-dir,C",
			Value: GDS_CONFIG_DIR,
			Usage: "Change the default configuration directory.",
		},
		cli.StringFlag{
			Name:  "config-file,c",
			Value: filepath.Join("$GDS_CONFIG_DIR", GDS_CONFIG_NAME),
			Usage: "Load configuration from path.",
		},
		cli.StringFlag{
			Name:  "context-file,d",
			Value: filepath.Join("$GDS_CONFIG_DIR", GDS_CONTEXT_FILENAME),
			Usage: "the parent directory of sync context files.",
		},
		cli.BoolFlag{
			Name:  "no-dev-context,n",
			Usage: "Do not save a copy of the sync context to the last device.",
		},
	}
	app.Commands = []cli.Command{
		NewSyncCommand(),
	}
	app.Run(os.Args)
}
<commit_msg>cli: Simplify config file arguments<commit_after>package main

import (
	"os"
	"path/filepath"
	"time"

	"github.com/codegangsta/cli"
	"github.com/davecgh/go-spew/spew"
)

var spd = spew.ConfigState{Indent: "\t"} //, DisableMethods: true}

var (
	GDS_CONFIG_DIR       = "$HOME/.config/gds"
	GDS_CONTEXT_FILENAME = "context_" + time.Now().Format(time.RFC3339) + ".json"
	GDS_CONFIG_NAME      = "config.yaml"
)

func main() {
	app := cli.NewApp()

	app.Name = "Ghetto Device Storage (gds)"
	app.Version = "0.0.1"
	app.Email = "jeezusjr@gmail.com"
	app.Author = "Jesus Alvarez"
	app.Usage = "Large data backups to dissimilar devices."

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config-dir,C",
			Value: GDS_CONFIG_DIR,
			Usage: "Change the default configuration directory.",
		},
		cli.StringFlag{
			Name:  "config,c",
			Value: filepath.Join("$GDS_CONFIG_DIR", GDS_CONFIG_NAME),
			Usage: "Load configuration from path.",
		},
		cli.StringFlag{
			Name:  "context,d",
			Value: filepath.Join("$GDS_CONFIG_DIR", GDS_CONTEXT_FILENAME),
			Usage: "the parent directory of sync context files.",
		},
		cli.BoolFlag{
			Name:  "no-dev-context,n",
			Usage: "Do not save a copy of the sync context to the last device.",
		},
	}
	app.Commands = []cli.Command{
		NewSyncCommand(),
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package build

import (
	"database/sql"
	"log"
	"os"

	_ "github.com/mattn/go-sqlite3" // SQLite driver used in database/sql
)

type sqlContainer struct {
	db       *sql.DB
	filename string
}

func (c *sqlContainer) Purge() error {
	err := os.Remove(c.filename)
	if err != nil && os.IsNotExist(err) {
		return err
	}
	return nil
}

func (c *sqlContainer) Init() error {
	var err error

	if c.db, err = sql.Open("sqlite3", c.filename); err != nil {
		return err
	}

	sqlStmt := `
	create table if not exists builds (
		id text not null primary key,
		projectid text not null,
		created int64 not null,
		script text not null,
		executortype text not null);
	create table if not exists stages (
		id integer not null primary key autoincrement,
		build text not null,
		type text not null,
		timestamp int64 not null,
		name text not null,
		data text);
	create table if not exists output (
		id integer not null primary key autoincrement,
		build text not null,
		data text);
	`
	_, err = c.db.Exec(sqlStmt)
	if err != nil {
		log.Printf("%q: %s\n", err, sqlStmt)
		return err
	}
	return nil
}

func (c sqlContainer) Close() error {
	return c.db.Close()
}

func (c *sqlContainer) Builds() []string {
	builds := []string{}

	rows, err := c.db.Query("select id from builds order by created desc")
	if err != nil {
		log.Println(err)
		return nil
	}
	defer rows.Close()
	for rows.Next() {
		var id string
		err = rows.Scan(&id)
		if err != nil {
			log.Println(err)
			return nil
		}
		builds = append(builds, id)
	}
	err = rows.Err()
	if err != nil {
		log.Println(err)
		return nil
	}
	return builds
}

func (c *sqlContainer) Build(ID string) (Build, error) {
	build := defaultBuild{BID: ID}
	stmt, err := c.db.Prepare("select created, projectid, script, executortype from builds where id = ?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = stmt.QueryRow(ID).Scan(&build.BCreated, &build.BProjectID, &build.BScript, &build.BExecutorType)
	if err == sql.ErrNoRows {
		return nil, ErrNotFound
	} else if err != nil {
		return nil, err
	}

	stmt, err = c.db.Prepare("select type, timestamp, name, data from stages where build = ?")
	if err != nil {
		return nil, err
	}
	rows, err := stmt.Query(ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		stage := Stage{}
		err = rows.Scan((*string)(&stage.Type), &stage.Timestamp, &stage.Name, &stage.Data)
		if err != nil {
			return nil, err
		}
		err = build.AddStage(stage)
		if err != nil {
			return nil, err
		}
	}

	stmt, err = c.db.Prepare("select data from output where build = ? order by id")
	if err != nil {
		return nil, err
	}
	rows, err = stmt.Query(ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		data := []byte{}
		err = rows.Scan(&data)
		if err != nil {
			return nil, err
		}
		build.output = append(build.output, data...)
	}

	return &build, nil
}

func (c *sqlContainer) New(b Buildable) (Build, error) {
	build, err := New(b)
	if err != nil {
		return nil, err
	}
	stmt, err := c.db.Prepare("insert into builds(id, projectid, created, script, executortype) values(?, ?, ?, ?, ?)")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	_, err = stmt.Exec(build.ID(), build.ProjectID(), build.Created(), build.Script(), build.ExecutorType())
	if err != nil {
		return nil, err
	}
	return build, nil
}

func (c *sqlContainer) AddStage(buildID string, stage Stage) error {
	buf := Stage{}
	previous := &buf
	tx, err := c.db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare("select type, timestamp, name, data from stages where build = ? order by id desc limit 1")
	if err != nil {
		return err
	}
	t := ""
	err = stmt.QueryRow(buildID).Scan(&t, &buf.Timestamp, &buf.Name, &buf.Data)
	buf.Type = StageType(t)
	if err == sql.ErrNoRows {
		previous = nil
	} else if err != nil {
		return err
	}
	err = stage.ValidateWithPredecessor(previous)
	if err != nil {
		return err
	}
	stmt, err = tx.Prepare("insert into stages (build, type, timestamp, name, data) values (?, ?, ?, ?, ?)")
	if err != nil {
		return err
	}

	_, err = stmt.Exec(buildID, string(stage.Type), stage.Timestamp, stage.Name, stage.Data)
	if err != nil {
		return err
	}
	return tx.Commit()

}

func (c *sqlContainer) Output(buildID string, output []byte) error {
	stmt, err := c.db.Prepare("insert into output(build, data) values(?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(buildID, output)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>SQL migrations<commit_after>package build

import (
	"database/sql"
	"log"
	"os"

	_ "github.com/mattn/go-sqlite3" // SQLite driver used in database/sql
)

type sqlContainer struct {
	db       *sql.DB
	filename string
}

func (c *sqlContainer) Purge() error {
	err := os.Remove(c.filename)
	if err != nil && os.IsNotExist(err) {
		return err
	}
	return nil
}

func (c *sqlContainer) Init() error {
	var err error
	var migrationLevel int

	if c.db, err = sql.Open("sqlite3", c.filename); err != nil {
		return err
	}

	_ = c.db.QueryRow("select level from migrationlevel").Scan(&migrationLevel)

	for i := migrationLevel + 1; i < len(migrations); i++ {
		if err := c.applyMigration(i, migrations[i]); err != nil {
			return err
		}
	}

	return nil
}

func (c sqlContainer) Close() error {
	return c.db.Close()
}

func (c *sqlContainer) Builds() []string {
	builds := []string{}

	rows, err := c.db.Query("select id from builds order by created desc")
	if err != nil {
		log.Println(err)
		return nil
	}
	defer rows.Close()
	for rows.Next() {
		var id string
		err = rows.Scan(&id)
		if err != nil {
			log.Println(err)
			return nil
		}
		builds = append(builds, id)
	}
	err = rows.Err()
	if err != nil {
		log.Println(err)
		return nil
	}
	return builds
}

func (c *sqlContainer) Build(ID string) (Build, error) {
	build := defaultBuild{BID: ID}
	stmt, err := c.db.Prepare("select created, projectid, script, executortype from builds where id = ?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = stmt.QueryRow(ID).Scan(&build.BCreated, &build.BProjectID, &build.BScript, &build.BExecutorType)
	if err == sql.ErrNoRows {
		return nil, ErrNotFound
	} else if err != nil {
		return nil, err
	}

	stmt, err = c.db.Prepare("select type, timestamp, name, data from stages where build = ?")
	if err != nil {
		return nil, err
	}
	rows, err := stmt.Query(ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		stage := Stage{}
		err = rows.Scan((*string)(&stage.Type), &stage.Timestamp, &stage.Name, &stage.Data)
		if err != nil {
			return nil, err
		}
		err = build.AddStage(stage)
		if err != nil {
			return nil, err
		}
	}

	stmt, err = c.db.Prepare("select data from output where build = ? order by id")
	if err != nil {
		return nil, err
	}
	rows, err = stmt.Query(ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		data := []byte{}
		err = rows.Scan(&data)
		if err != nil {
			return nil, err
		}
		build.output = append(build.output, data...)
	}

	return &build, nil
}

func (c *sqlContainer) New(b Buildable) (Build, error) {
	build, err := New(b)
	if err != nil {
		return nil, err
	}
	stmt, err := c.db.Prepare("insert into builds(id, projectid, created, script, executortype) values(?, ?, ?, ?, ?)")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	_, err = stmt.Exec(build.ID(), build.ProjectID(), build.Created(), build.Script(), build.ExecutorType())
	if err != nil {
		return nil, err
	}
	return build, nil
}

func (c *sqlContainer) AddStage(buildID string, stage Stage) error {
	buf := Stage{}
	previous := &buf
	tx, err := c.db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare("select type, timestamp, name, data from stages where build = ? order by id desc limit 1")
	if err != nil {
		return err
	}
	t := ""
	err = stmt.QueryRow(buildID).Scan(&t, &buf.Timestamp, &buf.Name, &buf.Data)
	buf.Type = StageType(t)
	if err == sql.ErrNoRows {
		previous = nil
	} else if err != nil {
		return err
	}
	err = stage.ValidateWithPredecessor(previous)
	if err != nil {
		return err
	}
	stmt, err = tx.Prepare("insert into stages (build, type, timestamp, name, data) values (?, ?, ?, ?, ?)")
	if err != nil {
		return err
	}

	_, err = stmt.Exec(buildID, string(stage.Type), stage.Timestamp, stage.Name, stage.Data)
	if err != nil {
		return err
	}
	return tx.Commit()

}

func (c *sqlContainer) Output(buildID string, output []byte) error {
	stmt, err := c.db.Prepare("insert into output(build, data) values(?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(buildID, output)
	if err != nil {
		return err
	}
	return nil
}

func (c sqlContainer) applyMigration(level int, migration string) error {
	log.Printf("Applying migration %d: %s\n", level, migration)
	tx, err := c.db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()
	if _, err = tx.Exec(migration); err != nil {
		log.Printf("%q: %s\n", err, migration)
		return err
	}
	update := `update migrationlevel set level = ?`
	stmt, err := tx.Prepare(update)
	if err != nil {
		return err
	}
	if _, err = stmt.Exec(level); err != nil {
		log.Printf("%q: %s\n", err, update)
		return err
	}
	if err := tx.Commit(); err != nil {
		return err
	}
	return nil
}

var migrations = []string{
	``, // initial migration level 0
	`create table if not exists migrationlevel (level int64);`,
	`insert into migrationlevel (level) values (1);`,
	`create table if not exists builds (
		id text not null primary key,
		projectid text not null,
		created int64 not null,
		script text not null,
		executortype text not null);`,
	`create table if not exists stages (
		id integer not null primary key autoincrement,
		build text not null,
		type text not null,
		timestamp int64 not null,
		name text not null,
		data text);`,
	`create table if not exists output (
		id integer not null primary key autoincrement,
		build text not null,
		data text);`,
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

// Returns a bitmask of possible Bishop moves from the given square.
func (p *Position) bishopMoves(square int) Bitmask {
	return p.bishopMovesAt(square, p.board)
}

// Returns a bitmask of possible Rook moves from the given square.
func (p *Position) rookMoves(square int) Bitmask {
	return p.rookMovesAt(square, p.board)
}

// Returns a bitmask of possible Bishop moves from the given square wherees
// other pieces on the board are represented by the explicit parameter.
func (p *Position) bishopMovesAt(square int, board Bitmask) Bitmask {
	magic := ((bishopMagic[square].mask & board) * bishopMagic[square].magic) >> 55
	return bishopMagicMoves[square][magic]
}

// Returns a bitmask of possible Rook moves from the given square wherees other
// pieces on the board are represented by the explicit parameter.
func (p *Position) rookMovesAt(square int, board Bitmask) Bitmask {
	magic := ((rookMagic[square].mask & board) * rookMagic[square].magic) >> 52
	return rookMagicMoves[square][magic]
}

func (p *Position) targets(square int) Bitmask {
	return p.targetsFor(square, p.pieces[square])
}

func (p *Position) targetsFor(square int, piece Piece) (bitmask Bitmask) {
	switch kind, color := piece.kind(), piece.color(); kind {
	case Pawn:
		bitmask = pawnMoves[color][square] & p.outposts[color^1]
		//
		// If the square in front of the pawn is empty then add it as possible
		// target.
		//
		if target := square + eight[color]; p.board.isClear(target) {
			bitmask.set(target)
			//
			// If the pawn is in its initial position and two squares in front of
			// the pawn are empty then add the second square as possible target.
			//
			if RelRow(square, color) == 1 {
				if target += eight[color]; p.board.isClear(target) {
					bitmask.set(target)
				}
			}
		}
		//
		// If the last move set the en-passant square and it is diagonally adjacent
		// to the current pawn, then add en-passant to the pawn's attack targets.
		//
		if p.enpassant != 0 && maskPawn[color][p.enpassant].isSet(square) {
			bitmask.set(p.enpassant)
		}
	case Knight:
		bitmask = knightMoves[square] & ^p.outposts[color]
	case Bishop:
		bitmask = p.bishopMoves(square) & ^p.outposts[color]
	case Rook:
		bitmask = p.rookMoves(square) & ^p.outposts[color]
	case Queen:
		bitmask = (p.bishopMoves(square) | p.rookMoves(square)) & ^p.outposts[color]
	case King:
		bitmask = kingMoves[square] & ^p.outposts[color]
	}
	return
}

func (p *Position) attacks(square int) Bitmask {
	return p.attacksFor(square, p.pieces[square])
}

func (p *Position) attacksFor(square int, piece Piece) (bitmask Bitmask) {
	switch kind, color := piece.kind(), piece.color(); kind {
	case Pawn:
		return pawnMoves[color][square]
	case Knight:
		return knightMoves[square]
	case Bishop:
		return p.bishopMoves(square)
	case Rook:
		return p.rookMoves(square)
	case Queen:
		return p.bishopMoves(square) | p.rookMoves(square)
	case King:
		return kingMoves[square]
	}
	return
}

func (p *Position) xrayAttacks(square int) Bitmask {
	return p.xrayAttacksFor(square, p.pieces[square])
}

func (p *Position) xrayAttacksFor(square int, piece Piece) (bitmask Bitmask) {
	switch kind, color := piece.kind(), piece.color(); kind {
	case Bishop:
		board := p.board ^ p.outposts[queen(color)]
		return p.bishopMovesAt(square, board)
	case Rook:
		board := p.board ^ p.outposts[queen(color)]
		return p.rookMovesAt(square, board)
	}
	return p.attacksFor(square, piece)
}

func (p *Position) allAttacks(color int) (bitmask Bitmask) {
	bitmask = p.pawnAttacks(color) | p.knightAttacks(color) | p.kingAttacks(color)

	outposts := p.outposts[bishop(color)] | p.outposts[queen(color)]
	for outposts != 0 {
		bitmask |= p.bishopMoves(outposts.pop())
	}

	outposts = p.outposts[rook(color)] | p.outposts[queen(color)]
	for outposts != 0 {
		bitmask |= p.rookMoves(outposts.pop())
	}
	return
}

// Returns a bitmask of pieces that attack given square. The resulting bitmask
// only counts pieces of requested color.
//
// This method is used in static exchange evaluation so instead of using current
// board bitmask (p.board) we pass the one that gets continuously updated during
// the evaluation.
func (p *Position) attackers(square, color int, board Bitmask) Bitmask {
	attackers := knightMoves[square] & p.outposts[knight(color)]
	attackers |= maskPawn[color][square] & p.outposts[pawn(color)]
	attackers |= kingMoves[square] & p.outposts[king(color)]
	attackers |= p.rookMovesAt(square, board) & (p.outposts[rook(color)] | p.outposts[queen(color)])
	attackers |= p.bishopMovesAt(square, board) & (p.outposts[bishop(color)] | p.outposts[queen(color)])

	return attackers
}

func (p *Position) isAttacked(square, color int) bool {
	return (knightMoves[square] & p.outposts[knight(color)]) != 0 ||
		(maskPawn[color][square] & p.outposts[pawn(color)]) != 0 ||
		(kingMoves[square] & p.outposts[king(color)]) != 0 ||
		(p.rookMoves(square) & (p.outposts[rook(color)]|p.outposts[queen(color)])) != 0 ||
		(p.bishopMoves(square) & (p.outposts[bishop(color)]|p.outposts[queen(color)])) != 0
}

func (p *Position) pawnAttacks(color int) (bitmask Bitmask) {
	if color == White {
		bitmask = (p.outposts[Pawn] & ^maskFile[0]) << 7
		bitmask |= (p.outposts[Pawn] & ^maskFile[7]) << 9
	} else {
		bitmask = (p.outposts[BlackPawn] & ^maskFile[0]) >> 9
		bitmask |= (p.outposts[BlackPawn] & ^maskFile[7]) >> 7
	}
	return
}

func (p *Position) knightAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[knight(color)]
	for outposts != 0 {
		bitmask |= knightMoves[outposts.pop()]
	}
	return
}

func (p *Position) bishopAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[bishop(color)]
	for outposts != 0 {
		bitmask |= p.bishopMoves(outposts.pop())
	}
	return
}

func (p *Position) rookAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[rook(color)]
	for outposts != 0 {
		bitmask |= p.rookMoves(outposts.pop())
	}
	return
}

func (p *Position) queenAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[queen(color)]
	for outposts != 0 {
		square := outposts.pop()
		bitmask |= p.rookMoves(square) | p.bishopMoves(square)
	}
	return
}

func (p *Position) kingAttacks(color int) Bitmask {
	return kingMoves[p.king[color]]
}

func (p *Position) strongestPiece(color int, targets Bitmask) Piece {
	if targets & p.outposts[queen(color)] != 0 {
		return queen(color)
	}
	if targets & p.outposts[rook(color)] != 0 {
		return rook(color)
	}
	if targets & p.outposts[bishop(color)] != 0 {
		return bishop(color)
	}
	if targets & p.outposts[knight(color)] != 0 {
		return knight(color)
	}
	if targets & p.outposts[pawn(color)] != 0 {
		return pawn(color)
	}
	return Piece(0)
}<commit_msg>Fixed Xray attacks for rooks<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

// Returns a bitmask of possible Bishop moves from the given square.
func (p *Position) bishopMoves(square int) Bitmask {
	return p.bishopMovesAt(square, p.board)
}

// Returns a bitmask of possible Rook moves from the given square.
func (p *Position) rookMoves(square int) Bitmask {
	return p.rookMovesAt(square, p.board)
}

// Returns a bitmask of possible Bishop moves from the given square wherees
// other pieces on the board are represented by the explicit parameter.
func (p *Position) bishopMovesAt(square int, board Bitmask) Bitmask {
	magic := ((bishopMagic[square].mask & board) * bishopMagic[square].magic) >> 55
	return bishopMagicMoves[square][magic]
}

// Returns a bitmask of possible Rook moves from the given square wherees other
// pieces on the board are represented by the explicit parameter.
func (p *Position) rookMovesAt(square int, board Bitmask) Bitmask {
	magic := ((rookMagic[square].mask & board) * rookMagic[square].magic) >> 52
	return rookMagicMoves[square][magic]
}

func (p *Position) targets(square int) Bitmask {
	return p.targetsFor(square, p.pieces[square])
}

func (p *Position) targetsFor(square int, piece Piece) (bitmask Bitmask) {
	switch kind, color := piece.kind(), piece.color(); kind {
	case Pawn:
		bitmask = pawnMoves[color][square] & p.outposts[color^1]
		//
		// If the square in front of the pawn is empty then add it as possible
		// target.
		//
		if target := square + eight[color]; p.board.isClear(target) {
			bitmask.set(target)
			//
			// If the pawn is in its initial position and two squares in front of
			// the pawn are empty then add the second square as possible target.
			//
			if RelRow(square, color) == 1 {
				if target += eight[color]; p.board.isClear(target) {
					bitmask.set(target)
				}
			}
		}
		//
		// If the last move set the en-passant square and it is diagonally adjacent
		// to the current pawn, then add en-passant to the pawn's attack targets.
		//
		if p.enpassant != 0 && maskPawn[color][p.enpassant].isSet(square) {
			bitmask.set(p.enpassant)
		}
	case Knight:
		bitmask = knightMoves[square] & ^p.outposts[color]
	case Bishop:
		bitmask = p.bishopMoves(square) & ^p.outposts[color]
	case Rook:
		bitmask = p.rookMoves(square) & ^p.outposts[color]
	case Queen:
		bitmask = (p.bishopMoves(square) | p.rookMoves(square)) & ^p.outposts[color]
	case King:
		bitmask = kingMoves[square] & ^p.outposts[color]
	}
	return
}

func (p *Position) attacks(square int) Bitmask {
	return p.attacksFor(square, p.pieces[square])
}

func (p *Position) attacksFor(square int, piece Piece) (bitmask Bitmask) {
	switch kind, color := piece.kind(), piece.color(); kind {
	case Pawn:
		return pawnMoves[color][square]
	case Knight:
		return knightMoves[square]
	case Bishop:
		return p.bishopMoves(square)
	case Rook:
		return p.rookMoves(square)
	case Queen:
		return p.bishopMoves(square) | p.rookMoves(square)
	case King:
		return kingMoves[square]
	}
	return
}

func (p *Position) xrayAttacks(square int) Bitmask {
	return p.xrayAttacksFor(square, p.pieces[square])
}

func (p *Position) xrayAttacksFor(square int, piece Piece) (bitmask Bitmask) {
	switch kind, color := piece.kind(), piece.color(); kind {
	case Bishop:
		board := p.board ^ p.outposts[queen(color)]
		return p.bishopMovesAt(square, board)
	case Rook:
		board := p.board ^ p.outposts[rook(color)] ^ p.outposts[queen(color)]
		return p.rookMovesAt(square, board)
	}
	return p.attacksFor(square, piece)
}

func (p *Position) allAttacks(color int) (bitmask Bitmask) {
	bitmask = p.pawnAttacks(color) | p.knightAttacks(color) | p.kingAttacks(color)

	outposts := p.outposts[bishop(color)] | p.outposts[queen(color)]
	for outposts != 0 {
		bitmask |= p.bishopMoves(outposts.pop())
	}

	outposts = p.outposts[rook(color)] | p.outposts[queen(color)]
	for outposts != 0 {
		bitmask |= p.rookMoves(outposts.pop())
	}
	return
}

// Returns a bitmask of pieces that attack given square. The resulting bitmask
// only counts pieces of requested color.
//
// This method is used in static exchange evaluation so instead of using current
// board bitmask (p.board) we pass the one that gets continuously updated during
// the evaluation.
func (p *Position) attackers(square, color int, board Bitmask) Bitmask {
	attackers := knightMoves[square] & p.outposts[knight(color)]
	attackers |= maskPawn[color][square] & p.outposts[pawn(color)]
	attackers |= kingMoves[square] & p.outposts[king(color)]
	attackers |= p.rookMovesAt(square, board) & (p.outposts[rook(color)] | p.outposts[queen(color)])
	attackers |= p.bishopMovesAt(square, board) & (p.outposts[bishop(color)] | p.outposts[queen(color)])

	return attackers
}

func (p *Position) isAttacked(square, color int) bool {
	return (knightMoves[square] & p.outposts[knight(color)]) != 0 ||
		(maskPawn[color][square] & p.outposts[pawn(color)]) != 0 ||
		(kingMoves[square] & p.outposts[king(color)]) != 0 ||
		(p.rookMoves(square) & (p.outposts[rook(color)]|p.outposts[queen(color)])) != 0 ||
		(p.bishopMoves(square) & (p.outposts[bishop(color)]|p.outposts[queen(color)])) != 0
}

func (p *Position) pawnAttacks(color int) (bitmask Bitmask) {
	if color == White {
		bitmask = (p.outposts[Pawn] & ^maskFile[0]) << 7
		bitmask |= (p.outposts[Pawn] & ^maskFile[7]) << 9
	} else {
		bitmask = (p.outposts[BlackPawn] & ^maskFile[0]) >> 9
		bitmask |= (p.outposts[BlackPawn] & ^maskFile[7]) >> 7
	}
	return
}

func (p *Position) knightAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[knight(color)]
	for outposts != 0 {
		bitmask |= knightMoves[outposts.pop()]
	}
	return
}

func (p *Position) bishopAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[bishop(color)]
	for outposts != 0 {
		bitmask |= p.bishopMoves(outposts.pop())
	}
	return
}

func (p *Position) rookAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[rook(color)]
	for outposts != 0 {
		bitmask |= p.rookMoves(outposts.pop())
	}
	return
}

func (p *Position) queenAttacks(color int) (bitmask Bitmask) {
	outposts := p.outposts[queen(color)]
	for outposts != 0 {
		square := outposts.pop()
		bitmask |= p.rookMoves(square) | p.bishopMoves(square)
	}
	return
}

func (p *Position) kingAttacks(color int) Bitmask {
	return kingMoves[p.king[color]]
}

func (p *Position) strongestPiece(color int, targets Bitmask) Piece {
	if targets & p.outposts[queen(color)] != 0 {
		return queen(color)
	}
	if targets & p.outposts[rook(color)] != 0 {
		return rook(color)
	}
	if targets & p.outposts[bishop(color)] != 0 {
		return bishop(color)
	}
	if targets & p.outposts[knight(color)] != 0 {
		return knight(color)
	}
	if targets & p.outposts[pawn(color)] != 0 {
		return pawn(color)
	}
	return Piece(0)
}<|endoftext|>


<commit_before>package tracking

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/jobrapp/mixpanel"
	"net/http"
	"net/url"
	"strconv"
)

// track event
func (mp *Client) Track(event UserEvent, opts ...EventOptions) error {
	u, err := mp.CreateTrackingUrl(event, opts...)
	if err != nil {
		return err
	}
	resp, err := http.Get(u)
	if err != nil {
		return err
	}
	io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	return nil
}

func (mp *Client) CreateTrackingUrl(event UserEvent, opts ...EventOptions) (string, error) {
	data := &eventData{
		Event: event.Name,
		Properties: map[string]interface{}{
			"token":       mp.Token,
			"distinct_id": event.DistinctId,
			"time":        mixpanel.Now(), // default to now
		},
	}
	if event.Ip != "" {
		data.Properties["ip"] = event.Ip
	}
	if event.Time.IsZero() == false {
		data.Properties["time"] = mixpanel.TimeToMPFmt(event.Time)
	}
	for k, v := range event.Properties {
		data.Properties[k] = v
	}
	marshaledData, err := json.Marshal(data)
	if err != nil {
		return "", err
	}
	params := url.Values{}
	params.Set("data", base64.StdEncoding.EncodeToString(marshaledData))
	if len(opts) > 0 {
		opt := opts[0]
		if opt.Ip != 0 {
			params.Set("ip", strconv.Itoa(opt.Ip))
		}
		if opt.Redirect != "" {
			params.Set("redirect", opt.Redirect)
		}
		if opt.Img != 0 {
			params.Set("img", strconv.Itoa(opt.Img))
		}
		if opt.Callback != "" {
			params.Set("callback", opt.Callback)
		}
		if opt.Verbose != 0 {
			params.Set("verbose", strconv.Itoa(opt.Verbose))
		}
	}
	return fmt.Sprintf("%s/%s/?%s", host, trackPath, params.Encode()), nil
}

// engage
func (mp *Client) Set(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Set = fields
	return mp.sendEngage(data)
}
func (mp *Client) SetOnce(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.SetOnce = fields
	return mp.sendEngage(data)
}
func (mp *Client) Add(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Add = fields
	return mp.sendEngage(data)
}
func (mp *Client) Append(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Append = fields
	return mp.sendEngage(data)
}
func (mp *Client) Union(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Union = fields
	return mp.sendEngage(data)
}
func (mp *Client) Unset(distinctId string, fields []string, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Unset = fields
	return mp.sendEngage(data)
}
func (mp *Client) Delete(distinctId string, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Delete = "delete"
	return mp.sendEngage(data)
}

// create alias
func (mp *Client) CreateAlias(distinctId, newId string) error {
	return mp.Track(UserEvent{
		DistinctId: distinctId,
		Name:       "$create_alias",
		Properties: map[string]interface{}{
			"alias": newId,
		},
	})
}
<commit_msg>missing io imports<commit_after>package tracking

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/jobrapp/mixpanel"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

// track event
func (mp *Client) Track(event UserEvent, opts ...EventOptions) error {
	u, err := mp.CreateTrackingUrl(event, opts...)
	if err != nil {
		return err
	}
	resp, err := http.Get(u)
	if err != nil {
		return err
	}
	io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	return nil
}

func (mp *Client) CreateTrackingUrl(event UserEvent, opts ...EventOptions) (string, error) {
	data := &eventData{
		Event: event.Name,
		Properties: map[string]interface{}{
			"token":       mp.Token,
			"distinct_id": event.DistinctId,
			"time":        mixpanel.Now(), // default to now
		},
	}
	if event.Ip != "" {
		data.Properties["ip"] = event.Ip
	}
	if event.Time.IsZero() == false {
		data.Properties["time"] = mixpanel.TimeToMPFmt(event.Time)
	}
	for k, v := range event.Properties {
		data.Properties[k] = v
	}
	marshaledData, err := json.Marshal(data)
	if err != nil {
		return "", err
	}
	params := url.Values{}
	params.Set("data", base64.StdEncoding.EncodeToString(marshaledData))
	if len(opts) > 0 {
		opt := opts[0]
		if opt.Ip != 0 {
			params.Set("ip", strconv.Itoa(opt.Ip))
		}
		if opt.Redirect != "" {
			params.Set("redirect", opt.Redirect)
		}
		if opt.Img != 0 {
			params.Set("img", strconv.Itoa(opt.Img))
		}
		if opt.Callback != "" {
			params.Set("callback", opt.Callback)
		}
		if opt.Verbose != 0 {
			params.Set("verbose", strconv.Itoa(opt.Verbose))
		}
	}
	return fmt.Sprintf("%s/%s/?%s", host, trackPath, params.Encode()), nil
}

// engage
func (mp *Client) Set(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Set = fields
	return mp.sendEngage(data)
}
func (mp *Client) SetOnce(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.SetOnce = fields
	return mp.sendEngage(data)
}
func (mp *Client) Add(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Add = fields
	return mp.sendEngage(data)
}
func (mp *Client) Append(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Append = fields
	return mp.sendEngage(data)
}
func (mp *Client) Union(distinctId string, fields map[string]interface{}, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Union = fields
	return mp.sendEngage(data)
}
func (mp *Client) Unset(distinctId string, fields []string, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Unset = fields
	return mp.sendEngage(data)
}
func (mp *Client) Delete(distinctId string, opts ...EngageOptions) error {
	data := mp.setBaseData(distinctId, opts)
	data.Delete = "delete"
	return mp.sendEngage(data)
}

// create alias
func (mp *Client) CreateAlias(distinctId, newId string) error {
	return mp.Track(UserEvent{
		DistinctId: distinctId,
		Name:       "$create_alias",
		Properties: map[string]interface{}{
			"alias": newId,
		},
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 The Prometheus 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 relabel

import (
	"reflect"
	"testing"

	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/config"
)

func TestRelabel(t *testing.T) {
	tests := []struct {
		input   model.LabelSet
		relabel []*config.RelabelConfig
		output  model.LabelSet
	}{
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch${1}-ch${1}",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "choo-choo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a", "b"},
					Regex:        config.MustNewRegexp("f(.*);(.*)r"),
					TargetLabel:  model.LabelName("a"),
					Separator:    ";",
					Replacement:  "b${1}${2}m", // boobam
					Action:       config.RelabelReplace,
				},
				{
					SourceLabels: model.LabelNames{"c", "a"},
					Regex:        config.MustNewRegexp("(b).*b(.*)ba(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1$2$2$3",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boobam",
				"b": "bar",
				"c": "baz",
				"d": "boooom",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				}, {
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch$1-ch$1",
					Action:       config.RelabelReplace,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "abc",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*(b).*"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "abc",
				"d": "b",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f|o"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelKeep,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f.*"),
					Action:       config.RelabelKeep,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			// No replacement must be applied if there is no match.
			input: model.LabelSet{
				"a": "boo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f"),
					TargetLabel:  model.LabelName("b"),
					Replacement:  "bar",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"c"},
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Action:       config.RelabelHashMod,
					Modulus:      1000,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "976",
			},
		},
		{
			input: model.LabelSet{
				"a":  "foo",
				"b1": "bar",
				"b2": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("(b.*)"),
					Replacement: "bar_${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":      "foo",
				"b1":     "bar",
				"b2":     "baz",
				"bar_b1": "bar",
				"bar_b2": "baz",
			},
		},
		{
			input: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("__meta_(my.*)"),
					Replacement: "${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
				"my_bar":        "aaa",
				"my_baz":        "bbb",
			},
		},
		{ // valid case
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a":    "some-name-value",
				"name": "value",
			},
		},
		{ // invalid replacement ""
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${3}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // invalid target_label ""
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("${3}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // more complex real-life like usecase
			input: model.LabelSet{
				"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)path:(/[^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("__metrics_path__"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)job:([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("job"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)label:([^=]+)=([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("__meta_sd_add_label_${1}"),
				},
				{
					Regex:       config.MustNewRegexp("__meta_sd_add_label_(.*)"),
					Replacement: "${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"__meta_sd_tags":   "path:/secret,job:some-job,label:foo=bar",
				"__metrics_path__": "/secret",
				"job":              "some-job",
				"__meta_sd_add_label_foo": "bar",
				"foo": "bar",
			},
		},
	}

	for i, test := range tests {
		res := Process(test.input, test.relabel...)

		if !reflect.DeepEqual(res, test.output) {
			t.Errorf("Test %d: relabel output mismatch: expected %#v, got %#v", i+1, test.output, res)
		}
	}
}
<commit_msg>test label name validation<commit_after>// Copyright 2015 The Prometheus 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 relabel

import (
	"reflect"
	"testing"

	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/config"
)

func TestRelabel(t *testing.T) {
	tests := []struct {
		input   model.LabelSet
		relabel []*config.RelabelConfig
		output  model.LabelSet
	}{
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch${1}-ch${1}",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "choo-choo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a", "b"},
					Regex:        config.MustNewRegexp("f(.*);(.*)r"),
					TargetLabel:  model.LabelName("a"),
					Separator:    ";",
					Replacement:  "b${1}${2}m", // boobam
					Action:       config.RelabelReplace,
				},
				{
					SourceLabels: model.LabelNames{"c", "a"},
					Regex:        config.MustNewRegexp("(b).*b(.*)ba(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1$2$2$3",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boobam",
				"b": "bar",
				"c": "baz",
				"d": "boooom",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				}, {
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch$1-ch$1",
					Action:       config.RelabelReplace,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "abc",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*(b).*"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "abc",
				"d": "b",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f|o"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelKeep,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f.*"),
					Action:       config.RelabelKeep,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			// No replacement must be applied if there is no match.
			input: model.LabelSet{
				"a": "boo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f"),
					TargetLabel:  model.LabelName("b"),
					Replacement:  "bar",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"c"},
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Action:       config.RelabelHashMod,
					Modulus:      1000,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "976",
			},
		},
		{
			input: model.LabelSet{
				"a":  "foo",
				"b1": "bar",
				"b2": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("(b.*)"),
					Replacement: "bar_${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":      "foo",
				"b1":     "bar",
				"b2":     "baz",
				"bar_b1": "bar",
				"bar_b2": "baz",
			},
		},
		{
			input: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("__meta_(my.*)"),
					Replacement: "${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
				"my_bar":        "aaa",
				"my_baz":        "bbb",
			},
		},
		{ // valid case
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a":    "some-name-value",
				"name": "value",
			},
		},
		{ // invalid replacement ""
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${3}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // invalid target_labels
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("${3}"),
				},
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("0${3}"),
				},
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("-${3}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // more complex real-life like usecase
			input: model.LabelSet{
				"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)path:(/[^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("__metrics_path__"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)job:([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("job"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)label:([^=]+)=([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"__meta_sd_tags":   "path:/secret,job:some-job,label:foo=bar",
				"__metrics_path__": "/secret",
				"job":              "some-job",
				"foo":              "bar",
			},
		},
	}

	for i, test := range tests {
		res := Process(test.input, test.relabel...)

		if !reflect.DeepEqual(res, test.output) {
			t.Errorf("Test %d: relabel output mismatch: expected %#v, got %#v", i+1, test.output, res)
		}
	}
}
<|endoftext|>


<commit_before>package tchannel_test

import (
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	. "github.com/uber/tchannel-go"
	"github.com/uber/tchannel-go/raw"
	"github.com/uber/tchannel-go/testutils"
)

func BenchmarkRelay(b *testing.B) {
	const (
		numServers = 5
		numClients = 5
	)
	data := testutils.RandBytes(1000)

	ch, err := NewChannel("relay", &ChannelOptions{
		RelayHosts: testutils.NewSimpleRelayHosts(map[string][]string{}),
	})
	require.NoError(b, err, "Failed to create relay")
	require.NoError(b, ch.ListenAndServe("127.0.0.1:0"), "relay listen failed")

	servers := make([]*Channel, numServers)
	for i := 0; i < numServers; i++ {
		servers[i] = testutils.NewServer(b, testutils.NewOpts().SetServiceName("test"))
		servers[i].Register(raw.Wrap(newTestHandler(b)), "echo")
		ch.RelayHosts().(*testutils.SimpleRelayHosts).Add("test", servers[i].PeerInfo().HostPort)
	}

	clients := make([]*Channel, numServers)
	for i := 0; i < numClients; i++ {
		clients[i] = testutils.NewClient(b, nil)
		clients[i].Peers().Add(ch.PeerInfo().HostPort)
	}

	b.ResetTimer()

	benchMore := testutils.Decrementor(b.N)

	var wg sync.WaitGroup
	wg.Add(numClients)
	for i := 0; i < numClients; i++ {
		go func(c *Channel) {
			defer wg.Done()

			sc := c.GetSubChannel("test")
			for benchMore() {
				ctx, cancel := NewContext(time.Second)
				defer cancel()

				_, _, _, err := raw.CallSC(ctx, sc, "echo", nil, data)
				assert.NoError(b, err, "call failed")
			}

		}(clients[i])
	}
	wg.Wait()
}
<commit_msg>Relay benchmarks should start client/server as separate processes<commit_after>package tchannel_test

import (
	"fmt"
	"sync"
	"testing"
	"time"

	. "github.com/uber/tchannel-go"

	"github.com/uber/tchannel-go/atomic"
	"github.com/uber/tchannel-go/benchmark"
	"github.com/uber/tchannel-go/testutils"

	"github.com/bmizerany/perks/quantile"
	"github.com/stretchr/testify/require"
)

type benchmarkParams struct {
	servers, clients int
	requestSize      int
}

type workerControl struct {
	start        sync.WaitGroup
	unblockStart chan struct{}
	done         sync.WaitGroup
}

func init() {
	benchmark.BenchmarkDir = "./benchmark/"
}

func newWorkerControl(numWorkers int) *workerControl {
	wc := &workerControl{
		unblockStart: make(chan struct{}),
	}
	wc.start.Add(numWorkers)
	wc.done.Add(numWorkers)
	return wc
}

func (c *workerControl) WaitForStart(f func()) {
	c.start.Wait()
	f()
	close(c.unblockStart)
}

func (c *workerControl) WaitForEnd() {
	c.done.Wait()
}

func (c *workerControl) WorkerStart() {
	c.start.Done()
	<-c.unblockStart
}

func (c *workerControl) WorkerDone() {
	c.done.Done()
}

func defaultParams() benchmarkParams {
	return benchmarkParams{
		servers:     2,
		clients:     2,
		requestSize: 1024,
	}
}

func closeAndVerify(b *testing.B, ch *Channel) {
	ch.Close()
	isChanClosed := func() bool {
		return ch.State() == ChannelClosed
	}
	if !testutils.WaitFor(time.Second, isChanClosed) {
		b.Errorf("Timed out waiting for channel to close, state: %v", ch.State())
	}
}

func benchmarkRelay(b *testing.B, p benchmarkParams) {
	b.SetBytes(int64(p.requestSize))
	b.ReportAllocs()

	relayHosts := testutils.NewSimpleRelayHosts(map[string][]string{})
	ch, err := NewChannel("relay", &ChannelOptions{
		RelayHosts: relayHosts,
	})
	defer closeAndVerify(b, ch)
	require.NoError(b, err, "Failed to create relay")
	require.NoError(b, ch.ListenAndServe("127.0.0.1:0"), "relay listen failed")

	servers := make([]benchmark.Server, p.servers)
	for i := range servers {
		servers[i] = benchmark.NewServer(
			benchmark.WithServiceName("svc"),
			benchmark.WithRequestSize(p.requestSize),
			benchmark.WithExternalProcess(),
		)
		defer servers[i].Close()
		relayHosts.Add("svc", servers[i].HostPort())
	}

	clients := make([]benchmark.Client, p.clients)
	for i := range clients {
		clients[i] = benchmark.NewClient([]string{ch.PeerInfo().HostPort},
			benchmark.WithServiceName("svc"),
			benchmark.WithRequestSize(p.requestSize),
			benchmark.WithExternalProcess(),
		)
		defer clients[i].Close()
	}

	quantileVals := []float64{0.50, 0.95, 0.99, 1.0}
	quantiles := make([]*quantile.Stream, p.clients)
	for i := range quantiles {
		quantiles[i] = quantile.NewTargeted(quantileVals...)
	}

	var errors atomic.Int64
	var success atomic.Int64

	wc := newWorkerControl(p.clients)
	benchMore := testutils.Decrementor(b.N)

	for i, c := range clients {
		go func(i int, c benchmark.Client) {
			// Do a warm up call.
			c.RawCall()

			wc.WorkerStart()
			defer wc.WorkerDone()

			for benchMore() {
				d, err := c.RawCall()
				if err == nil {
					quantiles[i].Insert(float64(d))
					success.Inc()
				} else {
					errors.Inc()
				}
			}
		}(i, c)
	}

	var started time.Time
	wc.WaitForStart(func() {
		b.ResetTimer()
		started = time.Now()
	})
	wc.WaitForEnd()
	duration := time.Since(started)
	if errors.Load() > 0 {
		b.Errorf("Errors during benchmarl: %v", errors.Load())
	}

	fmt.Printf("\nb.N: %v Duration: %v Requests = %v RPS = %0.2f\n", b.N, duration, success.Load(), float64(success.Load())/duration.Seconds())

	// Merge all the quantiles into 1
	for _, q := range quantiles[1:] {
		quantiles[0].Merge(q.Samples())
	}

	for _, q := range quantileVals {
		fmt.Printf("  %0.4f = %v\n", q, time.Duration(quantiles[0].Query(q)))
	}
	fmt.Println()
}

func BenchmarkRelay2Servers2Clients1k(b *testing.B) {
	p := defaultParams()
	p.clients = 2
	p.servers = 2
	benchmarkRelay(b, p)
}

func BenchmarkRelay2Servers4Clients1k(b *testing.B) {
	p := defaultParams()
	p.clients = 2
	p.servers = 2
	benchmarkRelay(b, p)
}

func BenchmarkRelay2Servers2Clients4k(b *testing.B) {
	p := defaultParams()
	p.requestSize = 4 * 1024
	p.clients = 2
	p.servers = 2
	benchmarkRelay(b, p)
}
<|endoftext|>


<commit_before>package repeating_otp

import (
	distance2 "github.com/cngkaygusuz/matasano-challenges/distance"
	"log"
	"github.com/cngkaygusuz/matasano-challenges/common"
	"github.com/cngkaygusuz/matasano-challenges/scotp/scoring"
)

const MIN_KEY_SIZE = 4
const MAX_KEY_SIZE = 60

func Solve(ciphertext []byte) (plaintext, key []byte ) {
	key_sizes := guess_key_size(ciphertext)

	var max_score int
	for _, ks := range key_sizes {
		candidate_plaintext, candidate_key, score := solve_for_keysize(ciphertext, ks)

		if score > max_score {
			max_score = score
			plaintext = candidate_plaintext
			key = candidate_key
		}
	}

	return
}

func solve_for_keysize(ciphertext []byte, key_size int) (plaintext, key []byte, score int) {
	bins := common.Bin(ciphertext)
	bins_transposed := common.Transpose(bins)

	transposed_solved := make(byte[][], len(bins_transposed))
	key = make(byte[], key_size)

	for i, bt := range bins_transposed {
		round_solution , round_key, round_score := scoring.Solve(bt)
		transposed_solved[i] = round_solution
		key[i] = round_key
		score += round_score
	}

	plaintext := common.Transpose(transposed_solved)
	return
}

func guess_key_size(ciphertext []byte) []int {
	retval := []int{-1, -1, -1}
	scores := []float64{1000000.0, 1000000.0, 1000000.0}

	for key_size := MIN_KEY_SIZE; key_size <= MAX_KEY_SIZE; key_size++ {
		current_score := get_score_for_size(ciphertext, key_size)
		ind := get_biggest_smaller_than(current_score, scores)
		if ind != -1 {
			scores[ind] = current_score
			retval[ind] = key_size
		}
	}

	log.Printf("key sizes: %#v", retval)
	log.Printf("scores: %#v", scores)

	return retval
}

func get_biggest_smaller_than(current_score float64, input []float64, ) int {
	ind := -1
	diff := 100000000.0

	for i, score := range input {
		if current_score > score {
			continue
		}

		current_diff := score - current_score
		if current_diff < diff {
			diff = current_diff
			ind = i
		}
	}

	return ind
}

func get_score_for_size(ciphertext []byte, key_size int) float64 {
	first := ciphertext[0:key_size]
	second := ciphertext[key_size : key_size*2]
	distance := distance2.Hamming(first, second)
	return float64(distance) / float64(key_size)
}
<commit_msg>Finalize solver impl<commit_after>package repeating_otp

import (
	distance2 "github.com/cngkaygusuz/matasano-challenges/distance"
	"log"
	"github.com/cngkaygusuz/matasano-challenges/common"
	"github.com/cngkaygusuz/matasano-challenges/scotp/scoring"
)

const MIN_KEY_SIZE = 4
const MAX_KEY_SIZE = 60

func Solve(ciphertext []byte) (plaintext, key []byte ) {
	key_sizes := guess_key_size(ciphertext)

	var max_score int
	for _, ks := range key_sizes {
		candidate_plaintext, candidate_key, score := solve_for_keysize(ciphertext, ks)

		if score > max_score {
			max_score = score
			plaintext = candidate_plaintext
			key = candidate_key
		}
	}

	return
}

func solve_for_keysize(ciphertext []byte, key_size int) (plaintext, key []byte, score int) {
	bins := common.Bin(ciphertext, key_size)
	bins_transposed := common.Transpose(bins)

	transposed_solved := make([][]byte, len(bins_transposed))
	key = make([]byte, key_size)

	for i, bt := range bins_transposed {
		round_solution , round_key, round_score := scoring.Solve(bt)
		transposed_solved[i] = round_solution
		key[i] = round_key
		score += round_score
	}

	plaintext_binned := common.Transpose(transposed_solved)
	plaintext = common.Unbin(plaintext_binned)
	return
}

func guess_key_size(ciphertext []byte) []int {
	retval := []int{-1, -1, -1}
	scores := []float64{1000000.0, 1000000.0, 1000000.0}

	for key_size := MIN_KEY_SIZE; key_size <= MAX_KEY_SIZE; key_size++ {
		current_score := get_score_for_size(ciphertext, key_size)
		ind := get_biggest_smaller_than(current_score, scores)
		if ind != -1 {
			scores[ind] = current_score
			retval[ind] = key_size
		}
	}

	log.Printf("key sizes: %#v", retval)
	log.Printf("scores: %#v", scores)

	return retval
}

func get_biggest_smaller_than(current_score float64, input []float64, ) int {
	ind := -1
	diff := 100000000.0

	for i, score := range input {
		if current_score > score {
			continue
		}

		current_diff := score - current_score
		if current_diff < diff {
			diff = current_diff
			ind = i
		}
	}

	return ind
}

func get_score_for_size(ciphertext []byte, key_size int) float64 {
	first := ciphertext[0:key_size]
	second := ciphertext[key_size : key_size*2]
	distance := distance2.Hamming(first, second)
	return float64(distance) / float64(key_size)
}
<|endoftext|>