<commit_before>package commandclass

import "errors"

const (
	CommandSecurityVersion                      uint8 = 0x01
	CommandNetworkKeySet                              = 0x06
	CommandNetworkKeyVerify                           = 0x07
	CommandSecurityCommandsSupportedGet               = 0x02
	CommandSecurityCommandsSupportedReport            = 0x03
	CommandSecurityMessageEncapsulation               = 0x81
	CommandSecurityMessageEncapsulationNonceGet       = 0xC1
	CommandSecurityNonceGet                           = 0x40
	CommandSecurityNonceReport                        = 0x80
	CommandSecuritySchemeGet                          = 0x04
	CommandSecuritySchemeInherit                      = 0x08
	CommandSecuritySchemeReport                       = 0x05
)

type SecuritySchemeGet struct {
	CommandClass             uint8
	Command                  uint8
	SupportedSecuritySchemes byte
}

type SecurityNonceGet struct {
	CommandClass uint8
	Command      uint8
}

type SecurityNonceReport struct {
	CommandClass uint8
	Command      uint8
	Nonce        []byte
}

func NewSecuritySchemeGet() []byte {
	return []byte{
		CommandClassSecurity,
		CommandSecuritySchemeGet,
		0x0,
	}
}

func NewSecurityNonceGet() []byte {
	return []byte{
		CommandClassSecurity,
		CommandSecurityNonceGet,
	}
}

func NewSecurityNonceReport(nonce []byte) []byte {
	buf := []byte{
		CommandClassSecurity,
		CommandSecurityNonceReport,
	}

	return append(buf, nonce...)
}

func NewSecurityNetworkKeySet(key []byte) []byte {
	buf := []byte{
		CommandClassSecurity,
		CommandNetworkKeySet,
	}

	return append(buf, key...)
}

func NewSecurityMessageEncapsulation(iv, payload, hmac []byte, receiverNonceId byte) []byte {
	buf := []byte{
		CommandClassSecurity,
		CommandSecurityMessageEncapsulation,
	}

	buf = append(buf, iv...)
	buf = append(buf, payload...)
	buf = append(buf, receiverNonceId)
	buf = append(buf, hmac...)

	return buf
}

func ParseSecurityNonceReport(command []byte) *SecurityNonceReport {
	return &SecurityNonceReport{
		CommandClass: command[0],
		Command:      command[1],
		Nonce:        command[2:],
	}
}

func ParseCommandClassSecurity(command []byte) (interface{}, error) {
	if command[0] != CommandClassSecurity {
		return nil, errors.New("Not security command class")
	}

	switch command[1] {
	case CommandSecurityNonceReport:
		return ParseSecurityNonceReport(command), nil
	default:
		return nil, errors.New("Unhandled command")
	}
}
<commit_msg>parse security commands supported report<commit_after>package commandclass

import "errors"

const (
	CommandSecurityVersion                      uint8 = 0x01
	CommandNetworkKeySet                              = 0x06
	CommandNetworkKeyVerify                           = 0x07
	CommandSecurityCommandsSupportedGet               = 0x02
	CommandSecurityCommandsSupportedReport            = 0x03
	CommandSecurityMessageEncapsulation               = 0x81
	CommandSecurityMessageEncapsulationNonceGet       = 0xC1
	CommandSecurityNonceGet                           = 0x40
	CommandSecurityNonceReport                        = 0x80
	CommandSecuritySchemeGet                          = 0x04
	CommandSecuritySchemeInherit                      = 0x08
	CommandSecuritySchemeReport                       = 0x05
)

const SecurityCommandsSupportedReportCommandClassMark = 0xEF

type SecuritySchemeGet struct {
	CommandClass             uint8
	Command                  uint8
	SupportedSecuritySchemes byte
}

type SecurityNonceGet struct {
	CommandClass uint8
	Command      uint8
}

type SecurityNonceReport struct {
	CommandClass uint8
	Command      uint8
	Nonce        []byte
}

type SecurityCommandsSupportedReport struct {
	CommandClass             uint8
	Command                  uint8
	RemainingFrames          uint8
	SupportedCommandClasses  []byte
	ControlledCommandClasses []byte
}

type SecurityMessageEncapsulation struct {
	CommandClass     uint8
	Command          uint8
	SenderNonce      []byte
	EncryptedPayload []byte
	ReceiverNonceId  byte
	Hmac             []byte
}

func NewSecuritySchemeGet() []byte {
	return []byte{
		CommandClassSecurity,
		CommandSecuritySchemeGet,
		0x0,
	}
}

func NewSecurityNonceGet() []byte {
	return []byte{
		CommandClassSecurity,
		CommandSecurityNonceGet,
	}
}

func NewSecurityNonceReport(nonce []byte) []byte {
	buf := []byte{
		CommandClassSecurity,
		CommandSecurityNonceReport,
	}

	return append(buf, nonce...)
}

func NewSecurityNetworkKeySet(key []byte) []byte {
	buf := []byte{
		CommandClassSecurity,
		CommandNetworkKeySet,
	}

	return append(buf, key...)
}

func NewSecurityMessageEncapsulation(iv, payload, hmac []byte, receiverNonceId byte) []byte {
	buf := []byte{
		CommandClassSecurity,
		CommandSecurityMessageEncapsulation,
	}

	buf = append(buf, iv...)
	buf = append(buf, payload...)
	buf = append(buf, receiverNonceId)
	buf = append(buf, hmac...)

	return buf
}

func ParseSecurityCommandsSupportedReport(data []byte) *SecurityCommandsSupportedReport {
	cc := &SecurityCommandsSupportedReport{
		CommandClass:    data[0],
		Command:         data[1],
		RemainingFrames: data[2],
	}

	supportedCommandClasses := []byte{}
	controlledCommandClasses := []byte{}

	var i int
	for i = 3; i < len(data); i++ {
		if data[i] == SecurityCommandsSupportedReportCommandClassMark {
			break
		}

		supportedCommandClasses = append(supportedCommandClasses, data[i])
	}

	i += 1 // skip command class mark

	for i < len(data) {
		controlledCommandClasses = append(controlledCommandClasses, data[i])
	}

	cc.SupportedCommandClasses = supportedCommandClasses
	cc.ControlledCommandClasses = controlledCommandClasses

	return cc
}

func ParseSecurityMessageEncapsulation(data []byte) *SecurityMessageEncapsulation {
	payloadLen := len(data) - 19

	cmd := &SecurityMessageEncapsulation{
		CommandClass:     data[0],
		Command:          data[1],
		SenderNonce:      data[2:10],
		EncryptedPayload: data[10 : 10+payloadLen],
		ReceiverNonceId:  data[10+payloadLen],
		Hmac:             data[11+payloadLen:],
	}

	return cmd
}

func ParseSecurityNonceReport(command []byte) *SecurityNonceReport {
	return &SecurityNonceReport{
		CommandClass: command[0],
		Command:      command[1],
		Nonce:        command[2:],
	}
}

func ParseCommandClassSecurity(command []byte) (interface{}, error) {
	if command[0] != CommandClassSecurity {
		return nil, errors.New("Not security command class")
	}

	switch command[1] {
	case CommandSecurityNonceReport:
		return ParseSecurityNonceReport(command), nil
	default:
		return nil, errors.New("Unhandled command")
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package manager

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"

	"github.com/ernestio/ernest-cli/helper"
	"github.com/ernestio/ernest-cli/model"
	"github.com/ernestio/ernest-cli/view"
	"github.com/fatih/color"
)

// ListBuilds ...
func (m *Manager) ListBuilds(project, env, token string) (builds []model.Build, err error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/", "GET", []byte(""), token, "")
	if err != nil {
		if resp == nil {
			return nil, ErrConnectionRefused
		}
		if resp.StatusCode == 403 {
			return builds, errors.New("You don't have permissions to perform this action")
		}
		if resp.StatusCode == 404 {
			return builds, errors.New("Specified environment name does not exist")
		}
		return nil, err
	}
	err = json.Unmarshal([]byte(body), &builds)
	if err != nil {
		return nil, err
	}
	return builds, err
}

// BuildStatus ...
func (m *Manager) BuildStatus(token, project, env, index string) (build model.Build, err error) {
	buildID, err := m.BuildIDFromIndex(token, project, env, index)
	if err != nil {
		return build, err
	}

	return m.BuildStatusByID(token, project, env, buildID)
}

// BuildIDFromIndex ...
func (m *Manager) BuildIDFromIndex(token, project, env, index string) (string, error) {
	builds, _ := m.ListBuilds(project, env, token)
	num, _ := strconv.Atoi(index)
	if num < 1 || num > len(builds) {
		return "", errors.New("Invalid build ID")
	}
	num = len(builds) - num
	return builds[num].ID, nil
}

// BuildStatusByID ...
func (m *Manager) BuildStatusByID(token, project, env, buildID string) (build model.Build, err error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/"+buildID, "GET", []byte(""), token, "")
	if err != nil {
		if resp == nil {
			return build, ErrConnectionRefused
		}
		if resp.StatusCode == 403 {
			return build, errors.New("You don't have permissions to perform this action")
		}
		if resp.StatusCode == 404 {
			return build, errors.New("Specified build not found")
		}
		return build, err
	}
	if body == "null" {
		return build, errors.New("Unexpected endpoint response : " + string(body))
	}
	err = json.Unmarshal([]byte(body), &build)
	return build, err
}

// BuildDefinitionByID ...
func (m *Manager) BuildDefinitionByID(token, project, env, buildID string) ([]byte, error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/"+buildID+"/definition/", "GET", []byte(""), token, "")
	if err != nil {
		if resp == nil {
			return nil, ErrConnectionRefused
		}
		if resp.StatusCode == 403 {
			return nil, errors.New("You don't have permissions to perform this action")
		}
		if resp.StatusCode == 404 {
			return nil, errors.New("Specified build not found")
		}
		return nil, err
	}
	if body == "null" {
		return nil, errors.New("Unexpected endpoint response : " + string(body))
	}

	return []byte(body), err
}

// LatestBuildDefinition ...
func (m *Manager) LatestBuildDefinition(token, project, env string) ([]byte, error) {
	id, err := m.LatestBuildID(token, project, env)
	if err != nil {
		return nil, err
	}

	return m.BuildDefinitionByID(token, project, env, id)
}

// BuildDefinitionFromIndex ...
func (m *Manager) BuildDefinitionFromIndex(token, project, env, index string) ([]byte, error) {
	id, err := m.BuildIDFromIndex(token, project, env, index)
	if err != nil {
		return nil, err
	}

	return m.BuildDefinitionByID(token, project, env, id)
}

// LatestBuildID ...
func (m *Manager) LatestBuildID(token, project, env string) (string, error) {
	builds, err := m.ListBuilds(project, env, token)
	if err != nil {
		return "", err
	}

	if len(builds) < 1 {
		return "", errors.New("Specified build not found")
	}

	return builds[0].ID, nil
}

// BuildMappingChanges ...
func (m *Manager) BuildMappingChanges(token, project, env, build string) (string, error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/"+build+"/mapping/?changes=true", "GET", nil, token, "application/json")
	if err != nil {
		if resp == nil {
			return "", ErrConnectionRefused
		}
		var internalError struct {
			Message string `json:"message"`
		}
		if err := json.Unmarshal([]byte(body), &internalError); err != nil {
			return "", errors.New(body)
		}
		return "", errors.New(internalError.Message)
	}

	return body, nil
}

// LatestBuildStatus ...
func (m *Manager) LatestBuildStatus(token, project, env string) (build model.Build, err error) {
	id, err := m.LatestBuildID(token, project, env)
	if err != nil {
		return build, err
	}

	return m.BuildStatusByID(token, project, env, id)
}

// Apply : Applies a yaml to create / update a new env
func (m *Manager) Apply(token, path string, credentials map[string]interface{}, monit, dry bool) (string, error) {
	var d model.Definition

	payload, err := ioutil.ReadFile(path)
	if err != nil {
		return "", errors.New("You should specify a valid template path or store an ernest.yml on the current folder")
	}

	err = d.Load(payload)
	if err != nil {
		return "", errors.New("Could not process definition yaml")
	}

	_, err = m.EnvStatus(token, d.Project, d.Name)
	if err != nil {
		err = m.CreateEnv(token, d.Name, d.Project, credentials, nil)
		if err != nil {
			return "", err
		}
	}

	// Load any imported files
	if err := d.LoadFileImports(); err != nil {
		return "", err
	}

	return m.ApplyEnv(d, token, credentials, monit, dry)
}

// Import : Imports an existing env
func (m *Manager) Import(token string, name string, project string, filters []string) (streamID string, err error) {
	_, err = m.EnvStatus(token, project, name)
	if err != nil {
		err = m.CreateEnv(token, name, project, nil, nil)
		if err != nil {
			return "", err
		}
	}

	a := model.Action{
		Type: "import",
	}

	a.Options.Filters = filters

	data, err := json.Marshal(a)
	if err != nil {
		return "", err
	}

	body, resp, rerr := m.doRequest("/api/projects/"+project+"/envs/"+name+"/actions/", "POST", data, token, "application/json")
	if resp == nil {
		return "", ErrConnectionRefused
	}
	if rerr != nil {
		return "", rerr
	}

	err = json.Unmarshal([]byte(body), &a)
	if err != nil {
		return "", errors.New(body)
	}

	return a.ResourceID, helper.Monitorize(m.URL, "/events", token, a.ResourceID)
}

// ApplyEnv : Applies a yaml to create / update a new env
func (m *Manager) ApplyEnv(d model.Definition, token string, credentials map[string]interface{}, monit, dry bool) (string, error) {
	payload, err := d.Save()

	if err != nil {
		return "", errors.New("Could not finalize definition yaml")
	}

	if dry {
		return m.dryApply(token, payload, d)
	}

	var response struct {
		ID      string `json:"id,omitempty"`
		Name    string `json:"name,omitempty"`
		Status  string `json:"status,omitempty"`
		Project string `json:"project,omitempty"`
		Message string `json:"message,omitempty"`
	}

	body, resp, rerr := m.doRequest("/api/projects/"+d.Project+"/envs/"+d.Name+"/builds/", "POST", payload, token, "application/yaml")
	if resp == nil {
		return "", ErrConnectionRefused
	}

	err = json.Unmarshal([]byte(body), &response)
	if err != nil {
		return "", errors.New(body)
	}

	if rerr != nil {
		return "", errors.New(response.Message)
	}

	if response.Status == "submitted" {
		color.Green("Build has been succesfully submitted and is awaiting approval.")
		os.Exit(0)
	}

	if monit {
		err = helper.Monitorize(m.URL, "/events", token, response.ID)
		if err != nil {
			return response.ID, err
		}

		fmt.Println("================\nPlatform Details\n================\n ")
		var build model.Build

		build, err = m.BuildStatusByID(token, d.Project, d.Name, response.ID)
		if err != nil {
			return response.ID, err
		}

		view.PrintEnvInfo(&build)
	}

	return response.ID, nil
}

func (m *Manager) dryApply(token string, payload []byte, d model.Definition) (string, error) {
	var body string
	body, resp, err := m.doRequest("/api/projects/"+d.Project+"/envs/"+d.Name+"/builds/?dry=true", "POST", payload, token, "application/yaml")
	if err != nil {
		if resp == nil {
			return "", ErrConnectionRefused
		}
		var internalError struct {
			Message string `json:"message"`
		}
		if err := json.Unmarshal([]byte(body), &internalError); err != nil {
			return "", errors.New(body)
		}
		return "", errors.New(internalError.Message)
	}
	view.EnvDry(body)
	return "", nil
}

// ReviewBuild : Reviews a submitted build
func (m *Manager) ReviewBuild(token, name, project, resolution string) error {
	a := model.Action{
		Type: "review",
	}

	a.Options.Resolution = resolution

	data, err := json.Marshal(a)
	if err != nil {
		return err
	}

	fmt.Println(string(data))

	body, resp, rerr := m.doRequest("/api/projects/"+project+"/envs/"+name+"/actions/", "POST", data, token, "application/json")
	if resp == nil {
		return ErrConnectionRefused
	}
	if rerr != nil {
		return rerr
	}

	err = json.Unmarshal([]byte(body), &a)
	if err != nil {
		return errors.New(body)
	}

	if a.Status == "done" {
		color.Green("Sync successfully resolved!")
		return nil
	}

	return helper.Monitorize(m.URL, "/events", token, a.ResourceID)
}
<commit_msg>removed logging<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package manager

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"

	"github.com/ernestio/ernest-cli/helper"
	"github.com/ernestio/ernest-cli/model"
	"github.com/ernestio/ernest-cli/view"
	"github.com/fatih/color"
)

// ListBuilds ...
func (m *Manager) ListBuilds(project, env, token string) (builds []model.Build, err error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/", "GET", []byte(""), token, "")
	if err != nil {
		if resp == nil {
			return nil, ErrConnectionRefused
		}
		if resp.StatusCode == 403 {
			return builds, errors.New("You don't have permissions to perform this action")
		}
		if resp.StatusCode == 404 {
			return builds, errors.New("Specified environment name does not exist")
		}
		return nil, err
	}
	err = json.Unmarshal([]byte(body), &builds)
	if err != nil {
		return nil, err
	}
	return builds, err
}

// BuildStatus ...
func (m *Manager) BuildStatus(token, project, env, index string) (build model.Build, err error) {
	buildID, err := m.BuildIDFromIndex(token, project, env, index)
	if err != nil {
		return build, err
	}

	return m.BuildStatusByID(token, project, env, buildID)
}

// BuildIDFromIndex ...
func (m *Manager) BuildIDFromIndex(token, project, env, index string) (string, error) {
	builds, _ := m.ListBuilds(project, env, token)
	num, _ := strconv.Atoi(index)
	if num < 1 || num > len(builds) {
		return "", errors.New("Invalid build ID")
	}
	num = len(builds) - num
	return builds[num].ID, nil
}

// BuildStatusByID ...
func (m *Manager) BuildStatusByID(token, project, env, buildID string) (build model.Build, err error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/"+buildID, "GET", []byte(""), token, "")
	if err != nil {
		if resp == nil {
			return build, ErrConnectionRefused
		}
		if resp.StatusCode == 403 {
			return build, errors.New("You don't have permissions to perform this action")
		}
		if resp.StatusCode == 404 {
			return build, errors.New("Specified build not found")
		}
		return build, err
	}
	if body == "null" {
		return build, errors.New("Unexpected endpoint response : " + string(body))
	}
	err = json.Unmarshal([]byte(body), &build)
	return build, err
}

// BuildDefinitionByID ...
func (m *Manager) BuildDefinitionByID(token, project, env, buildID string) ([]byte, error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/"+buildID+"/definition/", "GET", []byte(""), token, "")
	if err != nil {
		if resp == nil {
			return nil, ErrConnectionRefused
		}
		if resp.StatusCode == 403 {
			return nil, errors.New("You don't have permissions to perform this action")
		}
		if resp.StatusCode == 404 {
			return nil, errors.New("Specified build not found")
		}
		return nil, err
	}
	if body == "null" {
		return nil, errors.New("Unexpected endpoint response : " + string(body))
	}

	return []byte(body), err
}

// LatestBuildDefinition ...
func (m *Manager) LatestBuildDefinition(token, project, env string) ([]byte, error) {
	id, err := m.LatestBuildID(token, project, env)
	if err != nil {
		return nil, err
	}

	return m.BuildDefinitionByID(token, project, env, id)
}

// BuildDefinitionFromIndex ...
func (m *Manager) BuildDefinitionFromIndex(token, project, env, index string) ([]byte, error) {
	id, err := m.BuildIDFromIndex(token, project, env, index)
	if err != nil {
		return nil, err
	}

	return m.BuildDefinitionByID(token, project, env, id)
}

// LatestBuildID ...
func (m *Manager) LatestBuildID(token, project, env string) (string, error) {
	builds, err := m.ListBuilds(project, env, token)
	if err != nil {
		return "", err
	}

	if len(builds) < 1 {
		return "", errors.New("Specified build not found")
	}

	return builds[0].ID, nil
}

// BuildMappingChanges ...
func (m *Manager) BuildMappingChanges(token, project, env, build string) (string, error) {
	body, resp, err := m.doRequest("/api/projects/"+project+"/envs/"+env+"/builds/"+build+"/mapping/?changes=true", "GET", nil, token, "application/json")
	if err != nil {
		if resp == nil {
			return "", ErrConnectionRefused
		}
		var internalError struct {
			Message string `json:"message"`
		}
		if err := json.Unmarshal([]byte(body), &internalError); err != nil {
			return "", errors.New(body)
		}
		return "", errors.New(internalError.Message)
	}

	return body, nil
}

// LatestBuildStatus ...
func (m *Manager) LatestBuildStatus(token, project, env string) (build model.Build, err error) {
	id, err := m.LatestBuildID(token, project, env)
	if err != nil {
		return build, err
	}

	return m.BuildStatusByID(token, project, env, id)
}

// Apply : Applies a yaml to create / update a new env
func (m *Manager) Apply(token, path string, credentials map[string]interface{}, monit, dry bool) (string, error) {
	var d model.Definition

	payload, err := ioutil.ReadFile(path)
	if err != nil {
		return "", errors.New("You should specify a valid template path or store an ernest.yml on the current folder")
	}

	err = d.Load(payload)
	if err != nil {
		return "", errors.New("Could not process definition yaml")
	}

	_, err = m.EnvStatus(token, d.Project, d.Name)
	if err != nil {
		err = m.CreateEnv(token, d.Name, d.Project, credentials, nil)
		if err != nil {
			return "", err
		}
	}

	// Load any imported files
	if err := d.LoadFileImports(); err != nil {
		return "", err
	}

	return m.ApplyEnv(d, token, credentials, monit, dry)
}

// Import : Imports an existing env
func (m *Manager) Import(token string, name string, project string, filters []string) (streamID string, err error) {
	_, err = m.EnvStatus(token, project, name)
	if err != nil {
		err = m.CreateEnv(token, name, project, nil, nil)
		if err != nil {
			return "", err
		}
	}

	a := model.Action{
		Type: "import",
	}

	a.Options.Filters = filters

	data, err := json.Marshal(a)
	if err != nil {
		return "", err
	}

	body, resp, rerr := m.doRequest("/api/projects/"+project+"/envs/"+name+"/actions/", "POST", data, token, "application/json")
	if resp == nil {
		return "", ErrConnectionRefused
	}
	if rerr != nil {
		return "", rerr
	}

	err = json.Unmarshal([]byte(body), &a)
	if err != nil {
		return "", errors.New(body)
	}

	return a.ResourceID, helper.Monitorize(m.URL, "/events", token, a.ResourceID)
}

// ApplyEnv : Applies a yaml to create / update a new env
func (m *Manager) ApplyEnv(d model.Definition, token string, credentials map[string]interface{}, monit, dry bool) (string, error) {
	payload, err := d.Save()

	if err != nil {
		return "", errors.New("Could not finalize definition yaml")
	}

	if dry {
		return m.dryApply(token, payload, d)
	}

	var response struct {
		ID      string `json:"id,omitempty"`
		Name    string `json:"name,omitempty"`
		Status  string `json:"status,omitempty"`
		Project string `json:"project,omitempty"`
		Message string `json:"message,omitempty"`
	}

	body, resp, rerr := m.doRequest("/api/projects/"+d.Project+"/envs/"+d.Name+"/builds/", "POST", payload, token, "application/yaml")
	if resp == nil {
		return "", ErrConnectionRefused
	}

	err = json.Unmarshal([]byte(body), &response)
	if err != nil {
		return "", errors.New(body)
	}

	if rerr != nil {
		return "", errors.New(response.Message)
	}

	if response.Status == "submitted" {
		color.Green("Build has been succesfully submitted and is awaiting approval.")
		os.Exit(0)
	}

	if monit {
		err = helper.Monitorize(m.URL, "/events", token, response.ID)
		if err != nil {
			return response.ID, err
		}

		fmt.Println("================\nPlatform Details\n================\n ")
		var build model.Build

		build, err = m.BuildStatusByID(token, d.Project, d.Name, response.ID)
		if err != nil {
			return response.ID, err
		}

		view.PrintEnvInfo(&build)
	}

	return response.ID, nil
}

func (m *Manager) dryApply(token string, payload []byte, d model.Definition) (string, error) {
	var body string
	body, resp, err := m.doRequest("/api/projects/"+d.Project+"/envs/"+d.Name+"/builds/?dry=true", "POST", payload, token, "application/yaml")
	if err != nil {
		if resp == nil {
			return "", ErrConnectionRefused
		}
		var internalError struct {
			Message string `json:"message"`
		}
		if err := json.Unmarshal([]byte(body), &internalError); err != nil {
			return "", errors.New(body)
		}
		return "", errors.New(internalError.Message)
	}
	view.EnvDry(body)
	return "", nil
}

// ReviewBuild : Reviews a submitted build
func (m *Manager) ReviewBuild(token, name, project, resolution string) error {
	a := model.Action{
		Type: "review",
	}

	a.Options.Resolution = resolution

	data, err := json.Marshal(a)
	if err != nil {
		return err
	}

	body, resp, rerr := m.doRequest("/api/projects/"+project+"/envs/"+name+"/actions/", "POST", data, token, "application/json")
	if resp == nil {
		return ErrConnectionRefused
	}
	if rerr != nil {
		return rerr
	}

	err = json.Unmarshal([]byte(body), &a)
	if err != nil {
		return errors.New(body)
	}

	if a.Status == "done" {
		color.Green("Sync successfully resolved!")
		return nil
	}

	return helper.Monitorize(m.URL, "/events", token, a.ResourceID)
}
<|endoftext|>


<commit_before>// Copyright 2015 Openprovider Authors. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

package mapper

// Bundle - set of entries
type Bundle []Entry

type Entry struct {
	// TLDs - list of TLDs, which accepted by specified Entry
	TLDs []string `json: "TLDs"`

	// a list of fields from "01" to last number "nn" in ascending order
	Fields map[string]Field `json: "fields"`
}

// Field - representation of one field
type Field struct {
	// a label in whois output
	Key string `json: "key"`

	// used if a field has constant value
	Value []string `json: "value"`

	// a name of the field in the database, if "value" is not defined
	Name []string `json: "name"`

	// special instructions to indicate how to display the field
	Format string `json: "format"`

	// if this option is set to 'true', each value will be repeated in whois output with the same label
	Multiple bool `json: "multiple"`

	// if this option is set to 'true', a value of the field will not shown in whois output
	Hide bool `json: "hide"`

	// a name of the field in a database through which a request for
	Related string `json: "related"`

	// a name of the field in a database through which related a request for
	RelatedBy string `json: "related_by"`

	// a name of the table/type in a database through which made a relation
	RelatedTo string `json: "related_to"`

	// it contains the number of field that replaces this field.
	// If the field is referenced by "replacedBy" has non-empty value,
	// then this field will not show in whois output, because this field
	// should be replaced by the field specified in 'ReplacedBy'
	ReplacedBy string `json: "replaced_by"`
}

func (bundle Bundle) EntryByTLD(TLD string) *Entry {
	for index := range bundle {
		for _, item := range bundle[index].TLDs {
			if item == TLD {
				return &bundle[index]
			}
		}
	}

	return nil
}
<commit_msg>added default attribute for entry<commit_after>// Copyright 2015 Openprovider Authors. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

package mapper

// Bundle - set of entries
type Bundle []Entry

type Entry struct {
	// TLDs - list of TLDs, which accepted by specified Entry
	TLDs []string `json: "TLDs"`

	// Default - use this entry as default for all undefined TLDs
	Default bool `json: "default"`

	// a list of fields from "01" to last number "nn" in ascending order
	Fields map[string]Field `json: "fields"`
}

// Field - representation of one field
type Field struct {
	// a label in whois output
	Key string `json: "key"`

	// used if a field has constant value
	Value []string `json: "value"`

	// a name of the field in the database, if "value" is not defined
	Name []string `json: "name"`

	// special instructions to indicate how to display the field
	Format string `json: "format"`

	// if this option is set to 'true', each value will be repeated in whois output with the same label
	Multiple bool `json: "multiple"`

	// if this option is set to 'true', a value of the field will not shown in whois output
	Hide bool `json: "hide"`

	// a name of the field in a database through which a request for
	Related string `json: "related"`

	// a name of the field in a database through which related a request for
	RelatedBy string `json: "related_by"`

	// a name of the table/type in a database through which made a relation
	RelatedTo string `json: "related_to"`

	// it contains the number of field that replaces this field.
	// If the field is referenced by "replacedBy" has non-empty value,
	// then this field will not show in whois output, because this field
	// should be replaced by the field specified in 'ReplacedBy'
	ReplacedBy string `json: "replaced_by"`
}

func (bundle Bundle) EntryByTLD(TLD string) *Entry {
	var defaultIndex int
	for index := range bundle {
		for _, item := range bundle[index].TLDs {
			if item == TLD {
				return &bundle[index]
			}
			if bundle[index].Default {
				defaultIndex = index
			}
		}
	}
	if defaultIndex != 0 {
		return &bundle[defaultIndex]
	}
	return nil
}
<|endoftext|>


<commit_before>package bittrex

type MarketSummary struct {
	MarketName     string  `json:"MarketName"`
	High           float64 `json:"High"`
	Low            float64 `json:"Low"`
	Ask            float64 `json:"Ask"`
	Bid            float64 `json:"Bid"`
	OpenBuyOrders  int     `json:"OpenBuyOrders"`
	OpenSellOrders int     `json:"OpenSellOrders"`
	Volume         float64 `json:"Volume"`
	Last           float64 `json:"Last"`
	BaseVolume     float64 `json:"BaseVolume"`
	TimeStamp      string  `json:"TimeStamp"`
}
<commit_msg>Add PrevDay field to MarketSummary<commit_after>package bittrex

type MarketSummary struct {
	MarketName     string  `json:"MarketName"`
	High           float64 `json:"High"`
	Low            float64 `json:"Low"`
	Ask            float64 `json:"Ask"`
	Bid            float64 `json:"Bid"`
	OpenBuyOrders  int     `json:"OpenBuyOrders"`
	OpenSellOrders int     `json:"OpenSellOrders"`
	Volume         float64 `json:"Volume"`
	Last           float64 `json:"Last"`
	BaseVolume     float64 `json:"BaseVolume"`
	PrevDay        float64 `json:"PrevDay"`
	TimeStamp      string  `json:"TimeStamp"`
}
<|endoftext|>


<commit_before>package material

import (
	"fmt"
	"github.com/BenLubar/Rnoadm/world"
	"math/big"
)

func init() {
	world.RegisterSpawnFunc(WrapSpawnFunc(func(material *Material, s string) world.Visible {
		wood, stone, metal := material.Get()
		if len(wood) != 0 {
			return nil
		}
		if s == "stone" && len(stone) != 0 && len(metal) == 0 {
			return &Stone{
				material: material,
			}
		}
		if s == "ore" && len(stone) == 0 && len(metal) != 0 {
			return &Ore{
				material: material,
			}
		}
		if len(stone) == 0 {
			return nil
		}
		if s == "rock" {
			return &Rock{
				material: material,
			}
		}
		if s == "deposit" && len(metal) != 0 {
			return &Rock{
				material: material,
				rich:     true,
			}
		}
		return nil
	}))
}

type Rock struct {
	Node

	material *Material
	rich     bool
}

func init() {
	world.Register("rock", NodeLike((*Rock)(nil)))
}

func (r *Rock) Save() (uint, interface{}, []world.ObjectLike) {
	return 0, r.rich, []world.ObjectLike{&r.Node, r.material}
}

func (r *Rock) Load(version uint, data interface{}, attached []world.ObjectLike) {
	switch version {
	case 0:
		r.Node = *attached[0].(*Node)
		r.material = attached[1].(*Material)
		r.rich = data.(bool)
	default:
		panic(fmt.Sprintf("version %d unknown", version))
	}
}

func (r *Rock) Quality() *big.Int {
	return r.material.Quality()
}

func (r *Rock) Name() string {
	if r.rich {
		return r.material.Name() + "deposit"
	}
	return r.material.Name() + "rock"
}

func (r *Rock) Examine() (string, [][][2]string) {
	_, info := r.Node.Examine()

	info = append(info, r.material.Info()...)

	return "a rock.", info
}

func (r *Rock) Sprite() string {
	return "rock"
}

func (r *Rock) SpriteSize() (uint, uint) {
	return 32, 32
}

func (r *Rock) Colors() []string {
	ore, stone := r.material.StoneColor(), r.material.OreColor()
	if r.rich {
		return []string{stone, ore, ore}
	}
	return []string{stone, ore}
}

func (r *Rock) Actions(player world.PlayerLike) []string {
	actions := r.Node.Actions(player)

	actions = append([]string{"quarry"}, actions...)
	if r.material.MetalColor() != "" {
		actions = append([]string{"mine"}, actions...)
	}

	return actions
}

type Stone struct {
	world.VisibleObject

	material *Material
}

func init() {
	world.Register("stone", world.Visible((*Stone)(nil)))
}

func (s *Stone) Save() (uint, interface{}, []world.ObjectLike) {
	return 1, uint(0), []world.ObjectLike{s.material}
}

func (s *Stone) Load(version uint, data interface{}, attached []world.ObjectLike) {
	switch version {
	case 0:
		material := &Material{components: []*material{&material{}}}
		world.InitObject(material)
		world.InitObject(material.components[0])
		kind := StoneType(data.(uint64))
		material.components[0].stone = &kind
		material.components[0].volume = 100
		material.quality = *big.NewInt(1 << 62)
		attached = append(attached, material)
		fallthrough
	case 1:
		s.material = attached[0].(*Material)
	default:
		panic(fmt.Sprintf("version %d unknown", version))
	}
}

func (s *Stone) Material() *Material {
	return s.material
}

func (s *Stone) Name() string {
	return s.material.Name() + "stone"
}

func (s *Stone) Examine() (string, [][][2]string) {
	_, info := s.VisibleObject.Examine()

	info = append(info, s.material.Info()...)

	return "some stones.", info
}

func (s *Stone) Sprite() string {
	return "item_stone"
}

func (s *Stone) Colors() []string {
	return []string{s.material.StoneColor()}
}

func (s *Stone) Volume() uint64 {
	return s.material.Volume()
}

func (s *Stone) Weight() uint64 {
	return s.material.Weight()
}

func (s *Stone) AdminOnly() bool {
	return false
}

type Ore struct {
	world.VisibleObject

	material *Material
}

func init() {
	world.Register("ore", world.Visible((*Ore)(nil)))
}

func (o *Ore) Save() (uint, interface{}, []world.ObjectLike) {
	return 1, uint(0), []world.ObjectLike{o.material}
}

func (o *Ore) Load(version uint, data interface{}, attached []world.ObjectLike) {
	switch version {
	case 0:
		material := &Material{components: []*material{&material{}}}
		world.InitObject(material)
		world.InitObject(material.components[0])
		kind := MetalType(data.(uint64))
		material.components[0].metal = &kind
		material.components[0].volume = 100
		material.quality = *big.NewInt(1 << 62)
		attached = append(attached, material)
		fallthrough
	case 1:
		o.material = attached[0].(*Material)
	default:
		panic(fmt.Sprintf("version %d unknown", version))
	}
}

func (o *Ore) Material() *Material {
	return o.material
}

func (o *Ore) Name() string {
	return o.material.Name() + "ore"
}

func (o *Ore) Examine() (string, [][][2]string) {
	_, info := o.VisibleObject.Examine()

	info = append(info, o.material.Info()...)

	return "some unrefined ore.", info
}

func (o *Ore) Sprite() string {
	return "item_ore"
}

func (o *Ore) Colors() []string {
	return []string{o.material.OreColor()}
}

func (o *Ore) Volume() uint64 {
	return o.material.Volume()
}

func (o *Ore) Weight() uint64 {
	return o.material.Weight()
}

func (o *Ore) AdminOnly() bool {
	return false
}
<commit_msg>fix brain fart<commit_after>package material

import (
	"fmt"
	"github.com/BenLubar/Rnoadm/world"
	"math/big"
)

func init() {
	world.RegisterSpawnFunc(WrapSpawnFunc(func(material *Material, s string) world.Visible {
		wood, stone, metal := material.Get()
		if len(wood) != 0 {
			return nil
		}
		if s == "stone" && len(stone) != 0 && len(metal) == 0 {
			return &Stone{
				material: material,
			}
		}
		if s == "ore" && len(stone) == 0 && len(metal) != 0 {
			return &Ore{
				material: material,
			}
		}
		if len(stone) == 0 {
			return nil
		}
		if s == "rock" {
			return &Rock{
				material: material,
			}
		}
		if s == "deposit" && len(metal) != 0 {
			return &Rock{
				material: material,
				rich:     true,
			}
		}
		return nil
	}))
}

type Rock struct {
	Node

	material *Material
	rich     bool
}

func init() {
	world.Register("rock", NodeLike((*Rock)(nil)))
}

func (r *Rock) Save() (uint, interface{}, []world.ObjectLike) {
	return 0, r.rich, []world.ObjectLike{&r.Node, r.material}
}

func (r *Rock) Load(version uint, data interface{}, attached []world.ObjectLike) {
	switch version {
	case 0:
		r.Node = *attached[0].(*Node)
		r.material = attached[1].(*Material)
		r.rich = data.(bool)
	default:
		panic(fmt.Sprintf("version %d unknown", version))
	}
}

func (r *Rock) Quality() *big.Int {
	return r.material.Quality()
}

func (r *Rock) Name() string {
	if r.rich {
		return r.material.Name() + "deposit"
	}
	return r.material.Name() + "rock"
}

func (r *Rock) Examine() (string, [][][2]string) {
	_, info := r.Node.Examine()

	info = append(info, r.material.Info()...)

	return "a rock.", info
}

func (r *Rock) Sprite() string {
	return "rock"
}

func (r *Rock) SpriteSize() (uint, uint) {
	return 32, 32
}

func (r *Rock) Colors() []string {
	stone, ore := r.material.StoneColor(), r.material.OreColor()
	if r.rich {
		return []string{stone, ore, ore}
	}
	return []string{stone, ore}
}

func (r *Rock) Actions(player world.PlayerLike) []string {
	actions := r.Node.Actions(player)

	actions = append([]string{"quarry"}, actions...)
	if r.material.MetalColor() != "" {
		actions = append([]string{"mine"}, actions...)
	}

	return actions
}

type Stone struct {
	world.VisibleObject

	material *Material
}

func init() {
	world.Register("stone", world.Visible((*Stone)(nil)))
}

func (s *Stone) Save() (uint, interface{}, []world.ObjectLike) {
	return 1, uint(0), []world.ObjectLike{s.material}
}

func (s *Stone) Load(version uint, data interface{}, attached []world.ObjectLike) {
	switch version {
	case 0:
		material := &Material{components: []*material{&material{}}}
		world.InitObject(material)
		world.InitObject(material.components[0])
		kind := StoneType(data.(uint64))
		material.components[0].stone = &kind
		material.components[0].volume = 100
		material.quality = *big.NewInt(1 << 62)
		attached = append(attached, material)
		fallthrough
	case 1:
		s.material = attached[0].(*Material)
	default:
		panic(fmt.Sprintf("version %d unknown", version))
	}
}

func (s *Stone) Material() *Material {
	return s.material
}

func (s *Stone) Name() string {
	return s.material.Name() + "stone"
}

func (s *Stone) Examine() (string, [][][2]string) {
	_, info := s.VisibleObject.Examine()

	info = append(info, s.material.Info()...)

	return "some stones.", info
}

func (s *Stone) Sprite() string {
	return "item_stone"
}

func (s *Stone) Colors() []string {
	return []string{s.material.StoneColor()}
}

func (s *Stone) Volume() uint64 {
	return s.material.Volume()
}

func (s *Stone) Weight() uint64 {
	return s.material.Weight()
}

func (s *Stone) AdminOnly() bool {
	return false
}

type Ore struct {
	world.VisibleObject

	material *Material
}

func init() {
	world.Register("ore", world.Visible((*Ore)(nil)))
}

func (o *Ore) Save() (uint, interface{}, []world.ObjectLike) {
	return 1, uint(0), []world.ObjectLike{o.material}
}

func (o *Ore) Load(version uint, data interface{}, attached []world.ObjectLike) {
	switch version {
	case 0:
		material := &Material{components: []*material{&material{}}}
		world.InitObject(material)
		world.InitObject(material.components[0])
		kind := MetalType(data.(uint64))
		material.components[0].metal = &kind
		material.components[0].volume = 100
		material.quality = *big.NewInt(1 << 62)
		attached = append(attached, material)
		fallthrough
	case 1:
		o.material = attached[0].(*Material)
	default:
		panic(fmt.Sprintf("version %d unknown", version))
	}
}

func (o *Ore) Material() *Material {
	return o.material
}

func (o *Ore) Name() string {
	return o.material.Name() + "ore"
}

func (o *Ore) Examine() (string, [][][2]string) {
	_, info := o.VisibleObject.Examine()

	info = append(info, o.material.Info()...)

	return "some unrefined ore.", info
}

func (o *Ore) Sprite() string {
	return "item_ore"
}

func (o *Ore) Colors() []string {
	return []string{o.material.OreColor()}
}

func (o *Ore) Volume() uint64 {
	return o.material.Volume()
}

func (o *Ore) Weight() uint64 {
	return o.material.Weight()
}

func (o *Ore) AdminOnly() bool {
	return false
}
<|endoftext|>


<commit_before>// Copyright ©2011-2012 Dan Kortschak <dan.kortschak@adelaide.edu.au>
// Derived from quicksect.py of bx-python ©James Taylor bitbucket.org/james_taylor/bx-python
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Packages providing basic linear algebra operations
package matrix

const (
	Cols = true
	Rows = !Cols
	Inf  = int(^uint(0) >> 1)
	Fro  = -Inf - 1
)

var (
	Precision int          = 8
	Format    byte         = 'e'
	Pad       map[byte]int = map[byte]int{'e': 6, 'f': 2, 'g': 6, 'E': 6, 'F': 2, 'G': 6}
)

type FilterFunc func(r, c int, v float64) bool
type ApplyFunc func(r, c int, v float64) float64

type Matrix interface {
	Copy() Matrix
	Dims() (int, int)
	Set(r, c int, v float64) error
	At(r, c int) (float64, error)
	Norm() float64
	T() Matrix
	Det() float64
	Add(b Matrix) Matrix
	Sub(b Matrix) Matrix
	MulElem(b Matrix) Matrix
	Equals(b Matrix) bool
	EqualsApprox(b Matrix, error float64) bool
	Scalar(f float64) Matrix
	Sum() (s float64)
	Dot(b Matrix) Matrix
	Inner(b Matrix) Matrix
	Stack(b Matrix) Matrix
	Augment(b Matrix) Matrix
	Filter(f FilterFunc) Matrix
}
<commit_msg>Who knows?<commit_after>// Copyright ©2011-2012 Dan Kortschak <dan.kortschak@adelaide.edu.au>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Packages providing basic linear algebra operations
package matrix

const (
	Cols = true
	Rows = !Cols
	Inf  = int(^uint(0) >> 1)
	Fro  = -Inf - 1
)

var (
	Precision int          = 8
	Format    byte         = 'e'
	Pad       map[byte]int = map[byte]int{'e': 6, 'f': 2, 'g': 6, 'E': 6, 'F': 2, 'G': 6}
)

type FilterFunc func(r, c int, v float64) bool
type ApplyFunc func(r, c int, v float64) float64

type Matrix interface {
	Copy() Matrix
	Dims() (int, int)
	Set(r, c int, v float64) error
	At(r, c int) (float64, error)
	Norm() float64
	T() Matrix
	Det() float64
	Add(b Matrix) Matrix
	Sub(b Matrix) Matrix
	MulElem(b Matrix) Matrix
	Equals(b Matrix) bool
	EqualsApprox(b Matrix, error float64) bool
	Scalar(f float64) Matrix
	Sum() (s float64)
	Dot(b Matrix) Matrix
	Inner(b Matrix) Matrix
	Stack(b Matrix) Matrix
	Augment(b Matrix) Matrix
	Filter(f FilterFunc) Matrix
}
<|endoftext|>


<commit_before>package workerpool

import (
	"github.com/gammazero/deque"
	"sync"
	"sync/atomic"
	"time"
)

const (
	// This value is the size of the queue that workers register their
	// availability to the dispatcher.  There may be hundreds of workers, but
	// only a small channel is needed to register some of the workers.
	readyQueueSize = 16

	// If worker pool receives no new work for this period of time, then stop
	// a worker goroutine.
	idleTimeoutSec = 5
)

// New creates and starts a pool of worker goroutines.
//
// The maxWorkers parameter specifies the maximum number of workers that will
// execute tasks concurrently.  After each timeout period, a worker goroutine
// is stopped until there are no remaining workers.
func New(maxWorkers int) *WorkerPool {
	// There must be at least one worker.
	if maxWorkers < 1 {
		maxWorkers = 1
	}

	pool := &WorkerPool{
		taskQueue:    make(chan func(), 1),
		maxWorkers:   maxWorkers,
		readyWorkers: make(chan chan func(), readyQueueSize),
		timeout:      time.Second * idleTimeoutSec,
		stoppedChan:  make(chan struct{}),
	}

	// Start the task dispatcher.
	go pool.dispatch()

	return pool
}

// WorkerPool is a collection of goroutines, where the number of concurrent
// goroutines processing requests does not exceed the specified maximum.
type WorkerPool struct {
	maxWorkers   int
	timeout      time.Duration
	taskQueue    chan func()
	readyWorkers chan chan func()
	stoppedChan  chan struct{}
	waitingQueue deque.Deque
	stopMutex    sync.Mutex
	stopped      bool
	waiting      int32
}

// Stop stops the worker pool and waits for only currently running tasks to
// complete.  Pending tasks that are not currently running are abandoned.
// Tasks must not be submitted to the worker pool after calling stop.
//
// Since creating the worker pool starts at least one goroutine, for the
// dispatcher, Stop() or StopWait() should be called when the worker pool is no
// longer needed.
func (p *WorkerPool) Stop() {
	p.stop(false)
}

// StopWait stops the worker pool and waits for all queued tasks tasks to
// complete.  No additional tasks may be submitted, but all pending tasks are
// executed by workers before this function returns.
func (p *WorkerPool) StopWait() {
	p.stop(true)
}

// Stopped returns true if this worker pool has been stopped.
func (p *WorkerPool) Stopped() bool {
	p.stopMutex.Lock()
	defer p.stopMutex.Unlock()
	return p.stopped
}

// Submit enqueues a function for a worker to execute.
//
// Any external values needed by the task function must be captured in a
// closure.  Any return values should be returned over a channel that is
// captured in the task function closure.
//
// Submit will not block regardless of the number of tasks submitted.  Each
// task is immediately given to an available worker or passed to a goroutine to
// be given to the next available worker.  If there are no available workers,
// the dispatcher adds a worker, until the maximum number of workers are
// running.
//
// After the maximum number of workers are running, and no workers are
// available, incoming tasks are put onto a queue and will be executed as
// workers become available.
//
// When no new tasks have been submitted for a time period and a worker is
// available, the worker is shutdown.  As long as no new tasks arrive, one
// available worker is shutdown each time period until there are no more idle
// workers.  Since the time to start new goroutines is not significant, there
// is no need to retain idle workers.
func (p *WorkerPool) Submit(task func()) {
	if task != nil {
		p.taskQueue <- task
	}
}

// SubmitWait enqueues the given function and waits for it to be executed.
func (p *WorkerPool) SubmitWait(task func()) {
	if task == nil {
		return
	}
	doneChan := make(chan struct{})
	p.taskQueue <- func() {
		task()
		close(doneChan)
	}
	<-doneChan
}

// WaitingQueueSize will return the size of the waiting queue
func (p *WorkerPool) WaitingQueueSize() int {
	return int(atomic.LoadInt32(&p.waiting))
}

// dispatch sends the next queued task to an available worker.
func (p *WorkerPool) dispatch() {
	defer close(p.stoppedChan)
	timeout := time.NewTimer(p.timeout)
	var (
		workerCount    int
		task           func()
		ok, wait       bool
		workerTaskChan chan func()
	)
	startReady := make(chan chan func())
Loop:
	for {
		// As long as tasks are in the waiting queue, remove and execute these
		// tasks as workers become available, and place new incoming tasks on
		// the queue.  Once the queue is empty, then go back to submitting
		// incoming tasks directly to available workers.
		if p.waitingQueue.Len() != 0 {
			select {
			case task, ok = <-p.taskQueue:
				if !ok {
					break Loop
				}
				if task == nil {
					wait = true
					break Loop
				}
				p.waitingQueue.PushBack(task)
			case workerTaskChan = <-p.readyWorkers:
				// A worker is ready, so give task to worker.
				workerTaskChan <- p.waitingQueue.PopFront().(func())
			}
			atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len()))
			continue
		}
		timeout.Reset(p.timeout)
		select {
		case task, ok = <-p.taskQueue:
			if !ok || task == nil {
				break Loop
			}
			// Got a task to do.
			select {
			case workerTaskChan = <-p.readyWorkers:
				// A worker is ready, so give task to worker.
				workerTaskChan <- task
			default:
				// No workers ready.
				// Create a new worker, if not at max.
				if workerCount < p.maxWorkers {
					workerCount++
					go func(t func()) {
						startWorker(startReady, p.readyWorkers)
						// Submit the task when the new worker.
						taskChan := <-startReady
						taskChan <- t
					}(task)
				} else {
					// Enqueue task to be executed by next available worker.
					p.waitingQueue.PushBack(task)
					atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len()))
				}
			}
		case <-timeout.C:
			// Timed out waiting for work to arrive.  Kill a ready worker.
			if workerCount > 0 {
				select {
				case workerTaskChan = <-p.readyWorkers:
					// A worker is ready, so kill.
					close(workerTaskChan)
					workerCount--
				default:
					// No work, but no ready workers.  All workers are busy.
				}
			}
		}
	}

	// If instructed to wait for all queued tasks, then remove from queue and
	// give to workers until queue is empty.
	if wait {
		for p.waitingQueue.Len() != 0 {
			workerTaskChan = <-p.readyWorkers
			// A worker is ready, so give task to worker.
			workerTaskChan <- p.waitingQueue.PopFront().(func())
			atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len()))
		}
	}

	// Stop all remaining workers as they become ready.
	for workerCount > 0 {
		workerTaskChan = <-p.readyWorkers
		close(workerTaskChan)
		workerCount--
	}
}

// startWorker starts a goroutine that executes tasks given by the dispatcher.
//
// When a new worker starts, it registers its availability on the startReady
// channel.  This ensures that the goroutine associated with starting the
// worker gets to use the worker to execute its task.  Otherwise, the main
// dispatcher loop could steal the new worker and not know to start up another
// worker for the waiting goroutine.  The task would then have to wait for
// another existing worker to become available, even though capacity is
// available to start additional workers.
//
// A worker registers that is it available to do work by putting its task
// channel on the readyWorkers channel.  The dispatcher reads a worker's task
// channel from the readyWorkers channel, and writes a task to the worker over
// the worker's task channel.  To stop a worker, the dispatcher closes a
// worker's task channel, instead of writing a task to it.
func startWorker(startReady, readyWorkers chan chan func()) {
	go func() {
		taskChan := make(chan func())
		var task func()
		var ok bool
		// Register availability on starReady channel.
		startReady <- taskChan
		for {
			// Read task from dispatcher.
			task, ok = <-taskChan
			if !ok {
				// Dispatcher has told worker to stop.
				break
			}

			// Execute the task.
			task()

			// Register availability on readyWorkers channel.
			readyWorkers <- taskChan
		}
	}()
}

// stop tells the dispatcher to exit, and whether or not to complete queued
// tasks.
func (p *WorkerPool) stop(wait bool) {
	p.stopMutex.Lock()
	defer p.stopMutex.Unlock()
	if p.stopped {
		return
	}
	p.stopped = true
	if wait {
		p.taskQueue <- nil
	}
	// Close task queue and wait for currently running tasks to finish.
	close(p.taskQueue)
	<-p.stoppedChan
}
<commit_msg>All goroutines calling Stop or StopWait wait for workerpool to stop<commit_after>package workerpool

import (
	"github.com/gammazero/deque"
	"sync"
	"sync/atomic"
	"time"
)

const (
	// This value is the size of the queue that workers register their
	// availability to the dispatcher.  There may be hundreds of workers, but
	// only a small channel is needed to register some of the workers.
	readyQueueSize = 16

	// If worker pool receives no new work for this period of time, then stop
	// a worker goroutine.
	idleTimeoutSec = 5
)

// New creates and starts a pool of worker goroutines.
//
// The maxWorkers parameter specifies the maximum number of workers that will
// execute tasks concurrently.  After each timeout period, a worker goroutine
// is stopped until there are no remaining workers.
func New(maxWorkers int) *WorkerPool {
	// There must be at least one worker.
	if maxWorkers < 1 {
		maxWorkers = 1
	}

	pool := &WorkerPool{
		taskQueue:    make(chan func(), 1),
		maxWorkers:   maxWorkers,
		readyWorkers: make(chan chan func(), readyQueueSize),
		timeout:      time.Second * idleTimeoutSec,
		stoppedChan:  make(chan struct{}),
	}

	// Start the task dispatcher.
	go pool.dispatch()

	return pool
}

// WorkerPool is a collection of goroutines, where the number of concurrent
// goroutines processing requests does not exceed the specified maximum.
type WorkerPool struct {
	maxWorkers   int
	timeout      time.Duration
	taskQueue    chan func()
	readyWorkers chan chan func()
	stoppedChan  chan struct{}
	waitingQueue deque.Deque
	stopOnce     sync.Once
	stopped      int32
	waiting      int32
}

// Stop stops the worker pool and waits for only currently running tasks to
// complete.  Pending tasks that are not currently running are abandoned.
// Tasks must not be submitted to the worker pool after calling stop.
//
// Since creating the worker pool starts at least one goroutine, for the
// dispatcher, Stop() or StopWait() should be called when the worker pool is no
// longer needed.
func (p *WorkerPool) Stop() {
	p.stop(false)
}

// StopWait stops the worker pool and waits for all queued tasks tasks to
// complete.  No additional tasks may be submitted, but all pending tasks are
// executed by workers before this function returns.
func (p *WorkerPool) StopWait() {
	p.stop(true)
}

// Stopped returns true if this worker pool has been stopped.
func (p *WorkerPool) Stopped() bool {
	return atomic.LoadInt32(&p.stopped) != 0
}

// Submit enqueues a function for a worker to execute.
//
// Any external values needed by the task function must be captured in a
// closure.  Any return values should be returned over a channel that is
// captured in the task function closure.
//
// Submit will not block regardless of the number of tasks submitted.  Each
// task is immediately given to an available worker or passed to a goroutine to
// be given to the next available worker.  If there are no available workers,
// the dispatcher adds a worker, until the maximum number of workers are
// running.
//
// After the maximum number of workers are running, and no workers are
// available, incoming tasks are put onto a queue and will be executed as
// workers become available.
//
// When no new tasks have been submitted for a time period and a worker is
// available, the worker is shutdown.  As long as no new tasks arrive, one
// available worker is shutdown each time period until there are no more idle
// workers.  Since the time to start new goroutines is not significant, there
// is no need to retain idle workers.
func (p *WorkerPool) Submit(task func()) {
	if task != nil {
		p.taskQueue <- task
	}
}

// SubmitWait enqueues the given function and waits for it to be executed.
func (p *WorkerPool) SubmitWait(task func()) {
	if task == nil {
		return
	}
	doneChan := make(chan struct{})
	p.taskQueue <- func() {
		task()
		close(doneChan)
	}
	<-doneChan
}

// WaitingQueueSize will return the size of the waiting queue
func (p *WorkerPool) WaitingQueueSize() int {
	return int(atomic.LoadInt32(&p.waiting))
}

// dispatch sends the next queued task to an available worker.
func (p *WorkerPool) dispatch() {
	defer close(p.stoppedChan)
	timeout := time.NewTimer(p.timeout)
	var (
		workerCount    int
		task           func()
		ok, wait       bool
		workerTaskChan chan func()
	)
	startReady := make(chan chan func())
Loop:
	for {
		// As long as tasks are in the waiting queue, remove and execute these
		// tasks as workers become available, and place new incoming tasks on
		// the queue.  Once the queue is empty, then go back to submitting
		// incoming tasks directly to available workers.
		if p.waitingQueue.Len() != 0 {
			select {
			case task, ok = <-p.taskQueue:
				if !ok {
					break Loop
				}
				if task == nil {
					wait = true
					break Loop
				}
				p.waitingQueue.PushBack(task)
			case workerTaskChan = <-p.readyWorkers:
				// A worker is ready, so give task to worker.
				workerTaskChan <- p.waitingQueue.PopFront().(func())
			}
			atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len()))
			continue
		}
		timeout.Reset(p.timeout)
		select {
		case task, ok = <-p.taskQueue:
			if !ok || task == nil {
				break Loop
			}
			// Got a task to do.
			select {
			case workerTaskChan = <-p.readyWorkers:
				// A worker is ready, so give task to worker.
				workerTaskChan <- task
			default:
				// No workers ready.
				// Create a new worker, if not at max.
				if workerCount < p.maxWorkers {
					workerCount++
					go func(t func()) {
						startWorker(startReady, p.readyWorkers)
						// Submit the task when the new worker.
						taskChan := <-startReady
						taskChan <- t
					}(task)
				} else {
					// Enqueue task to be executed by next available worker.
					p.waitingQueue.PushBack(task)
					atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len()))
				}
			}
		case <-timeout.C:
			// Timed out waiting for work to arrive.  Kill a ready worker.
			if workerCount > 0 {
				select {
				case workerTaskChan = <-p.readyWorkers:
					// A worker is ready, so kill.
					close(workerTaskChan)
					workerCount--
				default:
					// No work, but no ready workers.  All workers are busy.
				}
			}
		}
	}

	// If instructed to wait for all queued tasks, then remove from queue and
	// give to workers until queue is empty.
	if wait {
		for p.waitingQueue.Len() != 0 {
			workerTaskChan = <-p.readyWorkers
			// A worker is ready, so give task to worker.
			workerTaskChan <- p.waitingQueue.PopFront().(func())
			atomic.StoreInt32(&p.waiting, int32(p.waitingQueue.Len()))
		}
	}

	// Stop all remaining workers as they become ready.
	for workerCount > 0 {
		workerTaskChan = <-p.readyWorkers
		close(workerTaskChan)
		workerCount--
	}
}

// startWorker starts a goroutine that executes tasks given by the dispatcher.
//
// When a new worker starts, it registers its availability on the startReady
// channel.  This ensures that the goroutine associated with starting the
// worker gets to use the worker to execute its task.  Otherwise, the main
// dispatcher loop could steal the new worker and not know to start up another
// worker for the waiting goroutine.  The task would then have to wait for
// another existing worker to become available, even though capacity is
// available to start additional workers.
//
// A worker registers that is it available to do work by putting its task
// channel on the readyWorkers channel.  The dispatcher reads a worker's task
// channel from the readyWorkers channel, and writes a task to the worker over
// the worker's task channel.  To stop a worker, the dispatcher closes a
// worker's task channel, instead of writing a task to it.
func startWorker(startReady, readyWorkers chan chan func()) {
	go func() {
		taskChan := make(chan func())
		var task func()
		var ok bool
		// Register availability on starReady channel.
		startReady <- taskChan
		for {
			// Read task from dispatcher.
			task, ok = <-taskChan
			if !ok {
				// Dispatcher has told worker to stop.
				break
			}

			// Execute the task.
			task()

			// Register availability on readyWorkers channel.
			readyWorkers <- taskChan
		}
	}()
}

// stop tells the dispatcher to exit, and whether or not to complete queued
// tasks.
func (p *WorkerPool) stop(wait bool) {
	p.stopOnce.Do(func() {
		atomic.StoreInt32(&p.stopped, 1)
		if wait {
			p.taskQueue <- nil
		}
		// Close task queue and wait for currently running tasks to finish.
		close(p.taskQueue)
		<-p.stoppedChan
	})
}
<|endoftext|>


<commit_before>package conn

import "fmt"

func cmdSelect(args commandArgs, c *Conn) {
	var err error
	c.SelectedMailbox, err = c.User.MailboxByName(args.Arg(0))
	if err != nil {
		fmt.Fprintf(c, "%s NO %s\r\n", args.ID(), err)
		return
	}

	writeMailboxInfo(c, c.SelectedMailbox)
	c.writeResponse(args.ID(), "OK [READ-WRITE] SELECT completed")
}
<commit_msg>SELECT should set the state of the mailbox<commit_after>package conn

import "fmt"

func cmdSelect(args commandArgs, c *Conn) {
	var err error
	c.SelectedMailbox, err = c.User.MailboxByName(args.Arg(0))
	if err != nil {
		fmt.Fprintf(c, "%s NO %s\r\n", args.ID(), err)
		return
	}
	c.SetState(StateSelected)

	writeMailboxInfo(c, c.SelectedMailbox)
	c.writeResponse(args.ID(), "OK [READ-WRITE] SELECT completed")
}
<|endoftext|>


<commit_before>// Package grid helps to build grid layouts.
package grid

import (
	"fmt"

	"github.com/mum4k/termdash/container"
	"github.com/mum4k/termdash/widgetapi"
)

// Builder builds grid layouts.
type Builder struct {
	elems []Element
}

// New returns a new grid builder.
func New() *Builder {
	return &Builder{}
}

// Add adds the specified elements.
// The subElements can be either a single Widget or any combination of Rows and
// Columns.
// Rows are created using RowHeightPerc() and Columns are created using
// ColWidthPerc().
// Can be called repeatedly, e.g. to add multiple Rows or Columns.
func (b *Builder) Add(subElements ...Element) {
	b.elems = append(b.elems, subElements...)
}

// Build builds the grid layout and returns the corresponding container
// options.
func (b *Builder) Build() ([]container.Option, error) {
	if err := validate(b.elems); err != nil {
		return nil, err
	}
	return build(b.elems, 100, 100), nil
}

// validate recursively validates the elements that were added to the builder.
// Validates the following per each level of Rows or Columns.:
//   The subElements are either exactly one Widget or any number of Rows and
//   Columns.
//   Each individual width or height is in the range 0 < v < 100.
//   The sum of all widths is <= 100.
//   The sum of all heights is <= 100.
func validate(elems []Element) error {
	heightSum := 0
	widthSum := 0
	for _, elem := range elems {
		switch e := elem.(type) {
		case *row:
			if min, max := 0, 100; e.heightPerc <= min || e.heightPerc >= max {
				return fmt.Errorf("invalid row heightPerc(%d), must be a value in the range %d < v < %d", e.heightPerc, min, max)
			}
			heightSum += e.heightPerc
			if err := validate(e.subElem); err != nil {
				return err
			}

		case *col:
			if min, max := 0, 100; e.widthPerc <= min || e.widthPerc >= max {
				return fmt.Errorf("invalid column widthPerc(%d), must be a value in the range %d < v < %d", e.widthPerc, min, max)
			}
			widthSum += e.widthPerc
			if err := validate(e.subElem); err != nil {
				return err
			}

		case *widget:
			if len(elems) > 1 {
				return fmt.Errorf("when adding a widget, it must be the only added element at that level, got: %v", elems)
			}
		}
	}

	if max := 100; heightSum > max || widthSum > max {
		return fmt.Errorf("the sum of all height percentages(%d) and width percentages(%d) at one element level cannot be larger than %d", heightSum, widthSum, max)
	}
	return nil
}

// build recursively builds the container options according to the elements
// that were added to the builder.
// The parentHeightPerc and parentWidthPerc percent indicate the relative size
// of the element we are building now in the parent element. See innerPerc()
// for more details.
func build(elems []Element, parentHeightPerc, parentWidthPerc int) []container.Option {
	if len(elems) == 0 {
		return nil
	}

	elem := elems[0]
	elems = elems[1:]

	switch e := elem.(type) {
	case *row:
		if len(elems) > 0 {
			perc := innerPerc(e.heightPerc, parentHeightPerc)
			childHeightPerc := parentHeightPerc - e.heightPerc
			return []container.Option{
				container.SplitHorizontal(
					container.Top(build(e.subElem, 100, parentWidthPerc)...),
					container.Bottom(build(elems, childHeightPerc, parentWidthPerc)...),
					container.SplitPercent(perc),
				),
			}
		}
		return build(e.subElem, 100, parentWidthPerc)

	case *col:
		if len(elems) > 0 {
			perc := innerPerc(e.widthPerc, parentWidthPerc)
			childWidthPerc := parentWidthPerc - e.widthPerc
			return []container.Option{
				container.SplitVertical(
					container.Left(build(e.subElem, parentHeightPerc, 100)...),
					container.Right(build(elems, parentHeightPerc, childWidthPerc)...),
					container.SplitPercent(perc),
				),
			}
		}
		return build(e.subElem, parentHeightPerc, 100)

	case *widget:
		opts := e.cOpts
		opts = append(opts, container.PlaceWidget(e.widget))
		return opts
	}
	return nil
}

// innerPerc translates the outer split percentage into the inner one.
// E.g. multiple rows would specify that they want the outer split percentage
// of 25% each, but we are representing them in a tree of containers so the
// inner splits vary:
//     ╭─────────╮
// 25% │   25%   │
//     │╭───────╮│ ---
// 25% ││  33%  ││
//     ││╭─────╮││
// 25% │││ 50% │││
//     ││├─────┤││ 75%
// 25% │││ 50% │││
//     ││╰─────╯││
//     │╰───────╯│
//     ╰─────────╯ ---
//
// Argument outerPerc is the user specified percentage for the split, i.e. the
// 25% in the example above.
// Argument parentPerc is the percentage this container has in the parent, i.e.
// 75% for the first inner container in the example above.
func innerPerc(outerPerc, parentPerc int) int {
	// parentPerc * parentHeightCells = childHeightCells
	// innerPerc * childHeightCells = outerPerc * parentHeightCells
	// innerPerc * parentPerc * parentHeightCells = outerPerc * parentHeightCells
	// innerPerc * parentPerc = outerPerc
	// innerPerc = outerPerc / parentPerc
	return int(float64(outerPerc) / float64(parentPerc) * 100)
}

// Element is an element that can be added to the grid.
type Element interface {
	isElement()
}

// row is a row in the grid.
// row implements Element.
type row struct {
	// heightPerc is the height percentage this row occupies.
	heightPerc int

	// subElem are the sub Rows or Columns or a single widget.
	subElem []Element
}

// isElement implements Element.isElement.
func (row) isElement() {}

// String implements fmt.Stringer.
func (r *row) String() string {
	return fmt.Sprintf("row{height:%d, sub:%v}", r.heightPerc, r.subElem)
}

// col is a column in the grid.
// col implements Element.
type col struct {
	// widthPerc is the width percentage this column occupies.
	widthPerc int

	// subElem are the sub Rows or Columns or a single widget.
	subElem []Element
}

// isElement implements Element.isElement.
func (col) isElement() {}

// String implements fmt.Stringer.
func (c *col) String() string {
	return fmt.Sprintf("col{width:%d, sub:%v}", c.widthPerc, c.subElem)
}

// widget is a widget placed into the grid.
// widget implements Element.
type widget struct {
	// widget is the widget instance.
	widget widgetapi.Widget
	// cOpts are the options for the widget's container.
	cOpts []container.Option
}

// String implements fmt.Stringer.
func (w *widget) String() string {
	return fmt.Sprintf("widget{type:%T}", w.widget)
}

// isElement implements Element.isElement.
func (widget) isElement() {}

// RowHeightPerc creates a row of the specified height.
// The height is supplied as height percentage of the outer container.
// The subElements can be either a single Widget or any combination of Rows and
// Columns.
func RowHeightPerc(heightPerc int, subElements ...Element) Element {
	return &row{
		heightPerc: heightPerc,
		subElem:    subElements,
	}
}

// ColWidthPerc creates a column of the specified width.
// The width is supplied as width percentage of the outer container.
// The subElements can be either a single Widget or any combination of Rows and
// Columns.
func ColWidthPerc(widthPerc int, subElements ...Element) Element {
	return &col{
		widthPerc: widthPerc,
		subElem:   subElements,
	}
}

// Widget adds a widget into the Row or Column.
// The options will be applied to the container that directly holds this
// widget.
func Widget(w widgetapi.Widget, cOpts ...container.Option) Element {
	return &widget{
		widget: w,
		cOpts:  cOpts,
	}
}
<commit_msg>Adding licences.<commit_after>// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package grid helps to build grid layouts.
package grid

import (
	"fmt"

	"github.com/mum4k/termdash/container"
	"github.com/mum4k/termdash/widgetapi"
)

// Builder builds grid layouts.
type Builder struct {
	elems []Element
}

// New returns a new grid builder.
func New() *Builder {
	return &Builder{}
}

// Add adds the specified elements.
// The subElements can be either a single Widget or any combination of Rows and
// Columns.
// Rows are created using RowHeightPerc() and Columns are created using
// ColWidthPerc().
// Can be called repeatedly, e.g. to add multiple Rows or Columns.
func (b *Builder) Add(subElements ...Element) {
	b.elems = append(b.elems, subElements...)
}

// Build builds the grid layout and returns the corresponding container
// options.
func (b *Builder) Build() ([]container.Option, error) {
	if err := validate(b.elems); err != nil {
		return nil, err
	}
	return build(b.elems, 100, 100), nil
}

// validate recursively validates the elements that were added to the builder.
// Validates the following per each level of Rows or Columns.:
//   The subElements are either exactly one Widget or any number of Rows and
//   Columns.
//   Each individual width or height is in the range 0 < v < 100.
//   The sum of all widths is <= 100.
//   The sum of all heights is <= 100.
func validate(elems []Element) error {
	heightSum := 0
	widthSum := 0
	for _, elem := range elems {
		switch e := elem.(type) {
		case *row:
			if min, max := 0, 100; e.heightPerc <= min || e.heightPerc >= max {
				return fmt.Errorf("invalid row heightPerc(%d), must be a value in the range %d < v < %d", e.heightPerc, min, max)
			}
			heightSum += e.heightPerc
			if err := validate(e.subElem); err != nil {
				return err
			}

		case *col:
			if min, max := 0, 100; e.widthPerc <= min || e.widthPerc >= max {
				return fmt.Errorf("invalid column widthPerc(%d), must be a value in the range %d < v < %d", e.widthPerc, min, max)
			}
			widthSum += e.widthPerc
			if err := validate(e.subElem); err != nil {
				return err
			}

		case *widget:
			if len(elems) > 1 {
				return fmt.Errorf("when adding a widget, it must be the only added element at that level, got: %v", elems)
			}
		}
	}

	if max := 100; heightSum > max || widthSum > max {
		return fmt.Errorf("the sum of all height percentages(%d) and width percentages(%d) at one element level cannot be larger than %d", heightSum, widthSum, max)
	}
	return nil
}

// build recursively builds the container options according to the elements
// that were added to the builder.
// The parentHeightPerc and parentWidthPerc percent indicate the relative size
// of the element we are building now in the parent element. See innerPerc()
// for more details.
func build(elems []Element, parentHeightPerc, parentWidthPerc int) []container.Option {
	if len(elems) == 0 {
		return nil
	}

	elem := elems[0]
	elems = elems[1:]

	switch e := elem.(type) {
	case *row:
		if len(elems) > 0 {
			perc := innerPerc(e.heightPerc, parentHeightPerc)
			childHeightPerc := parentHeightPerc - e.heightPerc
			return []container.Option{
				container.SplitHorizontal(
					container.Top(build(e.subElem, 100, parentWidthPerc)...),
					container.Bottom(build(elems, childHeightPerc, parentWidthPerc)...),
					container.SplitPercent(perc),
				),
			}
		}
		return build(e.subElem, 100, parentWidthPerc)

	case *col:
		if len(elems) > 0 {
			perc := innerPerc(e.widthPerc, parentWidthPerc)
			childWidthPerc := parentWidthPerc - e.widthPerc
			return []container.Option{
				container.SplitVertical(
					container.Left(build(e.subElem, parentHeightPerc, 100)...),
					container.Right(build(elems, parentHeightPerc, childWidthPerc)...),
					container.SplitPercent(perc),
				),
			}
		}
		return build(e.subElem, parentHeightPerc, 100)

	case *widget:
		opts := e.cOpts
		opts = append(opts, container.PlaceWidget(e.widget))
		return opts
	}
	return nil
}

// innerPerc translates the outer split percentage into the inner one.
// E.g. multiple rows would specify that they want the outer split percentage
// of 25% each, but we are representing them in a tree of containers so the
// inner splits vary:
//     ╭─────────╮
// 25% │   25%   │
//     │╭───────╮│ ---
// 25% ││  33%  ││
//     ││╭─────╮││
// 25% │││ 50% │││
//     ││├─────┤││ 75%
// 25% │││ 50% │││
//     ││╰─────╯││
//     │╰───────╯│
//     ╰─────────╯ ---
//
// Argument outerPerc is the user specified percentage for the split, i.e. the
// 25% in the example above.
// Argument parentPerc is the percentage this container has in the parent, i.e.
// 75% for the first inner container in the example above.
func innerPerc(outerPerc, parentPerc int) int {
	// parentPerc * parentHeightCells = childHeightCells
	// innerPerc * childHeightCells = outerPerc * parentHeightCells
	// innerPerc * parentPerc * parentHeightCells = outerPerc * parentHeightCells
	// innerPerc * parentPerc = outerPerc
	// innerPerc = outerPerc / parentPerc
	return int(float64(outerPerc) / float64(parentPerc) * 100)
}

// Element is an element that can be added to the grid.
type Element interface {
	isElement()
}

// row is a row in the grid.
// row implements Element.
type row struct {
	// heightPerc is the height percentage this row occupies.
	heightPerc int

	// subElem are the sub Rows or Columns or a single widget.
	subElem []Element
}

// isElement implements Element.isElement.
func (row) isElement() {}

// String implements fmt.Stringer.
func (r *row) String() string {
	return fmt.Sprintf("row{height:%d, sub:%v}", r.heightPerc, r.subElem)
}

// col is a column in the grid.
// col implements Element.
type col struct {
	// widthPerc is the width percentage this column occupies.
	widthPerc int

	// subElem are the sub Rows or Columns or a single widget.
	subElem []Element
}

// isElement implements Element.isElement.
func (col) isElement() {}

// String implements fmt.Stringer.
func (c *col) String() string {
	return fmt.Sprintf("col{width:%d, sub:%v}", c.widthPerc, c.subElem)
}

// widget is a widget placed into the grid.
// widget implements Element.
type widget struct {
	// widget is the widget instance.
	widget widgetapi.Widget
	// cOpts are the options for the widget's container.
	cOpts []container.Option
}

// String implements fmt.Stringer.
func (w *widget) String() string {
	return fmt.Sprintf("widget{type:%T}", w.widget)
}

// isElement implements Element.isElement.
func (widget) isElement() {}

// RowHeightPerc creates a row of the specified height.
// The height is supplied as height percentage of the outer container.
// The subElements can be either a single Widget or any combination of Rows and
// Columns.
func RowHeightPerc(heightPerc int, subElements ...Element) Element {
	return &row{
		heightPerc: heightPerc,
		subElem:    subElements,
	}
}

// ColWidthPerc creates a column of the specified width.
// The width is supplied as width percentage of the outer container.
// The subElements can be either a single Widget or any combination of Rows and
// Columns.
func ColWidthPerc(widthPerc int, subElements ...Element) Element {
	return &col{
		widthPerc: widthPerc,
		subElem:   subElements,
	}
}

// Widget adds a widget into the Row or Column.
// The options will be applied to the container that directly holds this
// widget.
func Widget(w widgetapi.Widget, cOpts ...container.Option) Element {
	return &widget{
		widget: w,
		cOpts:  cOpts,
	}
}
<|endoftext|>


<commit_before>package metadata

import (
	"fmt"
	"os"
	"strings"

	"pkg.re/essentialkaos/ek.v9/fsutil"
	"pkg.re/essentialkaos/ek.v9/jsonutil"
	"pkg.re/essentialkaos/ek.v9/path"

	"github.com/gongled/vgrepo/prefs"
)

// ////////////////////////////////////////////////////////////////////////////////// //

const (
	METADATA_EXTENSION_TYPE = ".json" // extension name of metadata file with a leading dot
)

// ////////////////////////////////////////////////////////////////////////////////// //

type VMetadata struct {
	*prefs.Preferences   // settings
	*VMetadataRepository // metadata of the repository
}

// ////////////////////////////////////////////////////////////////////////////////// //

// metaName returns name of the metadata file with extension
func (m *VMetadata) metaName() string {
	return fmt.Sprintf("%s%s", m.Name, METADATA_EXTENSION_TYPE)
}

// MetaDir returns directory string to metadata file
func (m *VMetadata) MetaDir() string {
	return path.Join(m.MetadataPath(), m.Name)
}

// MetaPath returns full path to metadata file
func (m *VMetadata) MetaPath() string {
	return path.Join(m.MetaDir(), m.metaName())
}

// MetaURL returns direct link to metadata file
func (m *VMetadata) MetaURL() string {
	return fmt.Sprintf("%s/%s/%s/%s",
		strings.Trim(m.StorageURL(), "/"),
		"metadata",
		m.Name,
		m.metaName(),
	)
}

// HasMeta returns true if metadata file is present on disk
func (m *VMetadata) HasMeta() bool {
	return fsutil.IsExist(m.MetaPath())
}

// IsEmptyMeta returns true if versions list is empty
func (m *VMetadata) IsEmptyMeta() bool {
	return m.CountVersions() == 0
}

// ////////////////////////////////////////////////////////////////////////////////// //

// NewMetadata returns new VMetadata struct
func NewMetadata(settings *prefs.Preferences, repository *VMetadataRepository) *VMetadata {
	m := &VMetadata{
		settings,
		repository,
	}

	m.SortVersions()

	return m
}

// ////////////////////////////////////////////////////////////////////////////////// //

// loadFromFile returns new VMetadataRepository struct which was read from the metadata file
func (m *VMetadata) loadFromFile(metaPath string) (*VMetadataRepository, error) {
	if !fsutil.IsExist(metaPath) {
		return nil, fmt.Errorf("metadata %s does not exist", metaPath)
	}

	info := &VMetadataRepository{}

	err := jsonutil.DecodeFile(metaPath, info)

	if err != nil {
		return nil, err
	}

	return info, err
}

// dumpToFile dumps VMetadata struct on the metadata file on the disk
func (m *VMetadata) dumpToFile(metaPath string) error {
	var err error

	m.SortVersions()

	err = os.MkdirAll(path.Dir(metaPath), 0755)

	if err != nil {
		return err
	}

	return jsonutil.EncodeToFile(metaPath, m.VMetadataRepository)
}

// ReadMeta returns new VMetadata struct which was read from the metadata file
func (m *VMetadata) ReadMeta() (*VMetadata, error) {
	md, err := m.loadFromFile(m.MetaPath())

	return NewMetadata(
		m.Preferences,
		md,
	), err
}

// WriteMeta dumps VMetadata struct on the linked metadata file on the disk
func (m *VMetadata) WriteMeta() error {
	return m.dumpToFile(m.MetaPath())
}

// DeleteMeta deletes file with metadata from the disk
func (m *VMetadata) DeleteMeta() error {
	return os.RemoveAll(m.MetaDir())
}

// ////////////////////////////////////////////////////////////////////////////////// //
<commit_msg>#4: added reading metadata from HTTP resource by given URL.<commit_after>package metadata

import (
	"fmt"
	"os"
	"strings"

	"pkg.re/essentialkaos/ek.v9/fsutil"
	"pkg.re/essentialkaos/ek.v9/jsonutil"
	"pkg.re/essentialkaos/ek.v9/path"
	"pkg.re/essentialkaos/ek.v9/req"

	"github.com/gongled/vgrepo/prefs"
)

// ////////////////////////////////////////////////////////////////////////////////// //

const (
	MD_EXT_TYPE = ".json" // extension name of metadata file with a leading dot
)

// ////////////////////////////////////////////////////////////////////////////////// //

type VMetadata struct {
	*prefs.Preferences   // settings
	*VMetadataRepository // metadata of the repository
}

// ////////////////////////////////////////////////////////////////////////////////// //

// metaName returns name of the metadata file with extension
func (m *VMetadata) metaName() string {
	return fmt.Sprintf("%s%s", m.Name, MD_EXT_TYPE)
}

// MetaDir returns directory string to metadata file
func (m *VMetadata) MetaDir() string {
	return path.Join(m.MetadataPath(), m.Name)
}

// MetaPath returns full path to metadata file
func (m *VMetadata) MetaPath() string {
	return path.Join(m.MetaDir(), m.metaName())
}

// MetaURL returns direct link to metadata file
func (m *VMetadata) MetaURL() string {
	return fmt.Sprintf("%s/%s/%s/%s",
		strings.Trim(m.StorageURL(), "/"),
		"metadata",
		m.Name,
		m.metaName(),
	)
}

// HasMeta returns true if metadata file is present on disk
func (m *VMetadata) HasMeta() bool {
	return fsutil.IsExist(m.MetaPath())
}

// IsEmptyMeta returns true if versions list is empty
func (m *VMetadata) IsEmptyMeta() bool {
	return m.CountVersions() == 0
}

// ////////////////////////////////////////////////////////////////////////////////// //

// NewMetadata returns new VMetadata struct
func NewMetadata(settings *prefs.Preferences, repository *VMetadataRepository) *VMetadata {
	m := &VMetadata{
		settings,
		repository,
	}

	m.SortVersions()

	return m
}

// ////////////////////////////////////////////////////////////////////////////////// //

// loadFromFile returns new VMetadataRepository struct which was read from the metadata file
func (m *VMetadata) loadFromFile(metaPath string) (*VMetadataRepository, error) {
	if !fsutil.IsExist(metaPath) {
		return nil, fmt.Errorf("metadata %s does not exist", metaPath)
	}

	info := &VMetadataRepository{}

	err := jsonutil.DecodeFile(metaPath, info)

	if err != nil {
		return nil, err
	}

	return info, err
}

// loadFromURL returns new VMetadataRepository struct which was fetched from the HTTP/HTTPS resource
func (m *VMetadata) loadFromURL(metaURL string) (*VMetadataRepository, error) {
	resp, err := req.Request{URL: metaURL}.Get()

	if err != nil {
		return nil, fmt.Errorf("cannot fetch metadata from URL %s", metaURL)
	}

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf(
			"cannot fetch metadata: server return status %s",
			resp.StatusCode,
		)
	}

	info := &VMetadataRepository{}

	err = resp.JSON(info)

	if err != nil {
		return nil, fmt.Errorf("cannot decode server response: %v", err)
	}

	return info, nil
}

// dumpToFile dumps VMetadata struct on the metadata file on the disk
func (m *VMetadata) dumpToFile(metaPath string) error {
	var err error

	m.SortVersions()

	err = os.MkdirAll(path.Dir(metaPath), 0755)

	if err != nil {
		return err
	}

	return jsonutil.EncodeToFile(metaPath, m.VMetadataRepository)
}

// ReadMeta returns new VMetadata struct which was read from the metadata file or URL
func (m *VMetadata) ReadMeta() (*VMetadata, error) {
	md, err := m.loadFromFile(m.MetaPath())

	if err != nil {
		md, err = m.loadFromURL(m.MetaURL())
	}

	return NewMetadata(
		m.Preferences,
		md,
	), err
}

// WriteMeta dumps VMetadata struct on the linked metadata file on the disk
func (m *VMetadata) WriteMeta() error {
	return m.dumpToFile(m.MetaPath())
}

// DeleteMeta deletes file with metadata from the disk
func (m *VMetadata) DeleteMeta() error {
	return os.RemoveAll(m.MetaDir())
}

// ////////////////////////////////////////////////////////////////////////////////// //
<|endoftext|>