<commit_before>package library

import (
	"os"
	"path/filepath"

	"github.com/anaminus/rbxmk"
	reflect "github.com/anaminus/rbxmk/library/rbxmk"
	"github.com/anaminus/rbxmk/rtypes"
	"github.com/robloxapi/rbxdump"
	"github.com/robloxapi/rbxdump/diff"
	"github.com/robloxapi/types"
	lua "github.com/yuin/gopher-lua"
)

func init() { register(RBXMK, 0) }

var RBXMK = rbxmk.Library{
	Name: "rbxmk",
	Open: func(s rbxmk.State) *lua.LTable {
		lib := s.L.CreateTable(0, 7)
		lib.RawSetString("load", s.WrapFunc(rbxmkLoad))
		lib.RawSetString("meta", s.WrapFunc(rbxmkMeta))
		lib.RawSetString("newDesc", s.WrapFunc(rbxmkNewDesc))
		lib.RawSetString("diffDesc", s.WrapFunc(rbxmkDiffDesc))
		lib.RawSetString("patchDesc", s.WrapFunc(rbxmkPatchDesc))
		lib.RawSetString("encodeFormat", s.WrapFunc(rbxmkEncodeFormat))
		lib.RawSetString("decodeFormat", s.WrapFunc(rbxmkDecodeFormat))
		lib.RawSetString("readSource", s.WrapFunc(rbxmkReadSource))
		lib.RawSetString("writeSource", s.WrapFunc(rbxmkWriteSource))

		for _, f := range reflect.All() {
			r := f()
			s.RegisterReflector(r)
			s.ApplyReflector(r, lib)
		}

		mt := s.L.CreateTable(0, 2)
		mt.RawSetString("__index", s.WrapFunc(func(s rbxmk.State) int {
			if field := s.Pull(2, "string").(types.String); field != "desc" {
				return s.RaiseError("unknown field %q", field)
			}
			desc := s.Desc(nil)
			if desc == nil {
				return s.Push(rtypes.Nil)
			}
			return s.Push(desc)
		}))
		mt.RawSetString("__newindex", s.WrapFunc(func(s rbxmk.State) int {
			if field := s.Pull(2, "string").(types.String); field != "desc" {
				return s.RaiseError("unknown field %q", field)
			}
			desc, _ := s.PullOpt(3, "RootDesc", nil).(*rtypes.RootDesc)
			if desc == nil {
				s.SetDesc(nil)
			}
			s.SetDesc(desc)
			return 0
		}))
		s.L.SetMetatable(lib, mt)

		return lib
	},
}

func rbxmkLoad(s rbxmk.State) int {
	fileName := filepath.Clean(s.L.CheckString(1))
	fi, err := os.Stat(fileName)
	if err != nil {
		return s.RaiseError(err.Error())
	}
	if err = s.PushFile(rbxmk.FileInfo{Path: fileName, FileInfo: fi}); err != nil {
		return s.RaiseError(err.Error())
	}

	nt := s.L.GetTop()

	// Load file as function.
	fn, err := s.L.LoadFile(fileName)
	if err != nil {
		s.PopFile()
		return s.RaiseError(err.Error())
	}
	s.L.Push(fn) // +function

	// Push extra arguments as arguments to loaded function.
	for i := 2; i <= nt; i++ {
		s.L.Push(s.L.Get(i)) // function, ..., +arg
	}
	// function, +args...

	// Call loaded function.
	err = s.L.PCall(nt-1, lua.MultRet, nil) // -function, -args..., +returns...
	s.PopFile()
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.L.GetTop() - nt
}

func metaGet(s rbxmk.State, inst *rtypes.Instance, name string) int {
	switch name {
	case "Reference":
		return s.Push(types.String(inst.Reference))
	case "IsService":
		return s.Push(types.Bool(inst.IsService))
	}
	return s.RaiseError("unknown metadata %q", name)
}

func metaSet(s rbxmk.State, inst *rtypes.Instance, name string) int {
	switch name {
	case "Reference":
		inst.Reference = string(s.Pull(3, "string").(types.String))
		return 0
	case "IsService":
		inst.IsService = bool(s.Pull(3, "bool").(types.Bool))
		return 0
	}
	return s.RaiseError("unknown metadata %q", name)
}

func rbxmkMeta(s rbxmk.State) int {
	inst := s.Pull(1, "Instance").(*rtypes.Instance)
	name := string(s.Pull(2, "string").(types.String))
	if s.Count() == 3 {
		return metaSet(s, inst, name)
	}
	return metaGet(s, inst, name)
}

func rbxmkNewDesc(s rbxmk.State) int {
	switch name := string(s.Pull(1, "string").(types.String)); name {
	case "Root":
		return s.Push(&rtypes.RootDesc{Root: &rbxdump.Root{
			Classes: make(map[string]*rbxdump.Class),
			Enums:   make(map[string]*rbxdump.Enum),
		}})
	case "Class":
		return s.Push(rtypes.ClassDesc{Class: &rbxdump.Class{
			Members: make(map[string]rbxdump.Member),
		}})
	case "Property":
		return s.Push(rtypes.PropertyDesc{Property: &rbxdump.Property{}})
	case "Function":
		return s.Push(rtypes.FunctionDesc{Function: &rbxdump.Function{}})
	case "Event":
		return s.Push(rtypes.EventDesc{Event: &rbxdump.Event{}})
	case "Callback":
		return s.Push(rtypes.CallbackDesc{Callback: &rbxdump.Callback{}})
	case "Parameter":
		return s.Push(rtypes.ParameterDesc{Parameter: &rbxdump.Parameter{}})
	case "Type":
		return s.Push(rtypes.TypeDesc{Embedded: &rbxdump.Type{}})
	case "Enum":
		return s.Push(rtypes.EnumDesc{Enum: &rbxdump.Enum{
			Items: make(map[string]*rbxdump.EnumItem),
		}})
	case "EnumItem":
		return s.Push(rtypes.EnumItemDesc{EnumItem: &rbxdump.EnumItem{}})
	default:
		return s.RaiseError("unable to create descriptor of type %q", name)
	}
}

func rbxmkDiffDesc(s rbxmk.State) int {
	var prev *rbxdump.Root
	var next *rbxdump.Root
	switch v := s.PullAnyOf(1, "RootDesc", "nil").(type) {
	case rtypes.NilType:
	case *rtypes.RootDesc:
		prev = v.Root
	}
	switch v := s.PullAnyOf(2, "RootDesc", "nil").(type) {
	case rtypes.NilType:
	case *rtypes.RootDesc:
		next = v.Root
	}
	actions := diff.Diff{Prev: prev, Next: next}.Diff()
	descActions := make(rtypes.DescActions, len(actions))
	for i, action := range actions {
		descActions[i] = &rtypes.DescAction{Action: action}
	}
	return s.Push(descActions)
}

func rbxmkPatchDesc(s rbxmk.State) int {
	desc := s.Pull(1, "RootDesc").(*rtypes.RootDesc).Root
	descActions := s.Pull(2, "DescActions").(rtypes.DescActions)
	actions := make([]diff.Action, len(descActions))
	for i, action := range descActions {
		actions[i] = action.Action
	}
	diff.Patch{Root: desc}.Patch(actions)
	return 0
}

func rbxmkEncodeFormat(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	format := s.Format(name)
	if format.Name == "" {
		return s.RaiseError("unknown format %q", name)
	}
	if format.Encode == nil {
		return s.RaiseError("cannot encode with format %s", name)
	}
	b, err := format.Encode(s.Pull(2, "Variant"))
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.Push(types.BinaryString(b))
}

func rbxmkDecodeFormat(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	format := s.Format(name)
	if format.Name == "" {
		return s.RaiseError("unknown format %q", name)
	}
	if format.Decode == nil {
		return s.RaiseError("cannot decode with format %s", name)
	}
	v, err := format.Decode([]byte(s.Pull(2, "BinaryString").(types.BinaryString)))
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.Push(v)
}

func rbxmkReadSource(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	source := s.Source(name)
	if source.Name == "" {
		return s.RaiseError("unknown source %q", name)
	}
	if source.Read == nil {
		return s.RaiseError("cannot read with format %s", name)
	}
	options := make([]interface{}, s.L.GetTop()-1)
	for i := 2; i <= s.L.GetTop(); i++ {
		options[i-2] = s.Pull(i, "Variant")
	}
	b, err := source.Read(options...)
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.Push(types.BinaryString(b))
}

func rbxmkWriteSource(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	source := s.Source(name)
	if source.Name == "" {
		return s.RaiseError("unknown source %q", name)
	}
	if source.Write == nil {
		return s.RaiseError("cannot write with format %s", name)
	}
	b := []byte(s.Pull(2, "BinaryString").(types.BinaryString))
	options := make([]interface{}, s.L.GetTop()-2)
	for i := 3; i <= s.L.GetTop(); i++ {
		options[i-3] = s.Pull(i, "Variant")
	}
	if err := source.Write(b, options...); err != nil {
		return s.RaiseError(err.Error())
	}
	return 0
}
<commit_msg>Swap condition for rbxmk.meta.<commit_after>package library

import (
	"os"
	"path/filepath"

	"github.com/anaminus/rbxmk"
	reflect "github.com/anaminus/rbxmk/library/rbxmk"
	"github.com/anaminus/rbxmk/rtypes"
	"github.com/robloxapi/rbxdump"
	"github.com/robloxapi/rbxdump/diff"
	"github.com/robloxapi/types"
	lua "github.com/yuin/gopher-lua"
)

func init() { register(RBXMK, 0) }

var RBXMK = rbxmk.Library{
	Name: "rbxmk",
	Open: func(s rbxmk.State) *lua.LTable {
		lib := s.L.CreateTable(0, 7)
		lib.RawSetString("load", s.WrapFunc(rbxmkLoad))
		lib.RawSetString("meta", s.WrapFunc(rbxmkMeta))
		lib.RawSetString("newDesc", s.WrapFunc(rbxmkNewDesc))
		lib.RawSetString("diffDesc", s.WrapFunc(rbxmkDiffDesc))
		lib.RawSetString("patchDesc", s.WrapFunc(rbxmkPatchDesc))
		lib.RawSetString("encodeFormat", s.WrapFunc(rbxmkEncodeFormat))
		lib.RawSetString("decodeFormat", s.WrapFunc(rbxmkDecodeFormat))
		lib.RawSetString("readSource", s.WrapFunc(rbxmkReadSource))
		lib.RawSetString("writeSource", s.WrapFunc(rbxmkWriteSource))

		for _, f := range reflect.All() {
			r := f()
			s.RegisterReflector(r)
			s.ApplyReflector(r, lib)
		}

		mt := s.L.CreateTable(0, 2)
		mt.RawSetString("__index", s.WrapFunc(func(s rbxmk.State) int {
			if field := s.Pull(2, "string").(types.String); field != "desc" {
				return s.RaiseError("unknown field %q", field)
			}
			desc := s.Desc(nil)
			if desc == nil {
				return s.Push(rtypes.Nil)
			}
			return s.Push(desc)
		}))
		mt.RawSetString("__newindex", s.WrapFunc(func(s rbxmk.State) int {
			if field := s.Pull(2, "string").(types.String); field != "desc" {
				return s.RaiseError("unknown field %q", field)
			}
			desc, _ := s.PullOpt(3, "RootDesc", nil).(*rtypes.RootDesc)
			if desc == nil {
				s.SetDesc(nil)
			}
			s.SetDesc(desc)
			return 0
		}))
		s.L.SetMetatable(lib, mt)

		return lib
	},
}

func rbxmkLoad(s rbxmk.State) int {
	fileName := filepath.Clean(s.L.CheckString(1))
	fi, err := os.Stat(fileName)
	if err != nil {
		return s.RaiseError(err.Error())
	}
	if err = s.PushFile(rbxmk.FileInfo{Path: fileName, FileInfo: fi}); err != nil {
		return s.RaiseError(err.Error())
	}

	nt := s.L.GetTop()

	// Load file as function.
	fn, err := s.L.LoadFile(fileName)
	if err != nil {
		s.PopFile()
		return s.RaiseError(err.Error())
	}
	s.L.Push(fn) // +function

	// Push extra arguments as arguments to loaded function.
	for i := 2; i <= nt; i++ {
		s.L.Push(s.L.Get(i)) // function, ..., +arg
	}
	// function, +args...

	// Call loaded function.
	err = s.L.PCall(nt-1, lua.MultRet, nil) // -function, -args..., +returns...
	s.PopFile()
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.L.GetTop() - nt
}

func metaGet(s rbxmk.State, inst *rtypes.Instance, name string) int {
	switch name {
	case "Reference":
		return s.Push(types.String(inst.Reference))
	case "IsService":
		return s.Push(types.Bool(inst.IsService))
	}
	return s.RaiseError("unknown metadata %q", name)
}

func metaSet(s rbxmk.State, inst *rtypes.Instance, name string) int {
	switch name {
	case "Reference":
		inst.Reference = string(s.Pull(3, "string").(types.String))
		return 0
	case "IsService":
		inst.IsService = bool(s.Pull(3, "bool").(types.Bool))
		return 0
	}
	return s.RaiseError("unknown metadata %q", name)
}

func rbxmkMeta(s rbxmk.State) int {
	inst := s.Pull(1, "Instance").(*rtypes.Instance)
	name := string(s.Pull(2, "string").(types.String))
	if s.Count() <= 2 {
		return metaGet(s, inst, name)
	}
	return metaSet(s, inst, name)
}

func rbxmkNewDesc(s rbxmk.State) int {
	switch name := string(s.Pull(1, "string").(types.String)); name {
	case "Root":
		return s.Push(&rtypes.RootDesc{Root: &rbxdump.Root{
			Classes: make(map[string]*rbxdump.Class),
			Enums:   make(map[string]*rbxdump.Enum),
		}})
	case "Class":
		return s.Push(rtypes.ClassDesc{Class: &rbxdump.Class{
			Members: make(map[string]rbxdump.Member),
		}})
	case "Property":
		return s.Push(rtypes.PropertyDesc{Property: &rbxdump.Property{}})
	case "Function":
		return s.Push(rtypes.FunctionDesc{Function: &rbxdump.Function{}})
	case "Event":
		return s.Push(rtypes.EventDesc{Event: &rbxdump.Event{}})
	case "Callback":
		return s.Push(rtypes.CallbackDesc{Callback: &rbxdump.Callback{}})
	case "Parameter":
		return s.Push(rtypes.ParameterDesc{Parameter: &rbxdump.Parameter{}})
	case "Type":
		return s.Push(rtypes.TypeDesc{Embedded: &rbxdump.Type{}})
	case "Enum":
		return s.Push(rtypes.EnumDesc{Enum: &rbxdump.Enum{
			Items: make(map[string]*rbxdump.EnumItem),
		}})
	case "EnumItem":
		return s.Push(rtypes.EnumItemDesc{EnumItem: &rbxdump.EnumItem{}})
	default:
		return s.RaiseError("unable to create descriptor of type %q", name)
	}
}

func rbxmkDiffDesc(s rbxmk.State) int {
	var prev *rbxdump.Root
	var next *rbxdump.Root
	switch v := s.PullAnyOf(1, "RootDesc", "nil").(type) {
	case rtypes.NilType:
	case *rtypes.RootDesc:
		prev = v.Root
	}
	switch v := s.PullAnyOf(2, "RootDesc", "nil").(type) {
	case rtypes.NilType:
	case *rtypes.RootDesc:
		next = v.Root
	}
	actions := diff.Diff{Prev: prev, Next: next}.Diff()
	descActions := make(rtypes.DescActions, len(actions))
	for i, action := range actions {
		descActions[i] = &rtypes.DescAction{Action: action}
	}
	return s.Push(descActions)
}

func rbxmkPatchDesc(s rbxmk.State) int {
	desc := s.Pull(1, "RootDesc").(*rtypes.RootDesc).Root
	descActions := s.Pull(2, "DescActions").(rtypes.DescActions)
	actions := make([]diff.Action, len(descActions))
	for i, action := range descActions {
		actions[i] = action.Action
	}
	diff.Patch{Root: desc}.Patch(actions)
	return 0
}

func rbxmkEncodeFormat(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	format := s.Format(name)
	if format.Name == "" {
		return s.RaiseError("unknown format %q", name)
	}
	if format.Encode == nil {
		return s.RaiseError("cannot encode with format %s", name)
	}
	b, err := format.Encode(s.Pull(2, "Variant"))
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.Push(types.BinaryString(b))
}

func rbxmkDecodeFormat(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	format := s.Format(name)
	if format.Name == "" {
		return s.RaiseError("unknown format %q", name)
	}
	if format.Decode == nil {
		return s.RaiseError("cannot decode with format %s", name)
	}
	v, err := format.Decode([]byte(s.Pull(2, "BinaryString").(types.BinaryString)))
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.Push(v)
}

func rbxmkReadSource(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	source := s.Source(name)
	if source.Name == "" {
		return s.RaiseError("unknown source %q", name)
	}
	if source.Read == nil {
		return s.RaiseError("cannot read with format %s", name)
	}
	options := make([]interface{}, s.L.GetTop()-1)
	for i := 2; i <= s.L.GetTop(); i++ {
		options[i-2] = s.Pull(i, "Variant")
	}
	b, err := source.Read(options...)
	if err != nil {
		return s.RaiseError(err.Error())
	}
	return s.Push(types.BinaryString(b))
}

func rbxmkWriteSource(s rbxmk.State) int {
	name := string(s.Pull(1, "string").(types.String))
	source := s.Source(name)
	if source.Name == "" {
		return s.RaiseError("unknown source %q", name)
	}
	if source.Write == nil {
		return s.RaiseError("cannot write with format %s", name)
	}
	b := []byte(s.Pull(2, "BinaryString").(types.BinaryString))
	options := make([]interface{}, s.L.GetTop()-2)
	for i := 3; i <= s.L.GetTop(); i++ {
		options[i-3] = s.Pull(i, "Variant")
	}
	if err := source.Write(b, options...); err != nil {
		return s.RaiseError(err.Error())
	}
	return 0
}
<|endoftext|>


<commit_before>package gapi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"
)

// LibraryPanelMetaUser represents the Grafana library panel createdBy and updatedBy fields
type LibraryPanelMetaUser struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	AvatarURL string `json:"folderId"`
}

// LibraryPanelMeta represents Grafana library panel metadata.
type LibraryPanelMeta struct {
	FolderName          string               `json:"folderName,,omitempty"`
	FolderUID           string               `json:"folderUid,omitempty"`
	ConnectedDashboards int64                `json:"connectedDashboards,omitempty"`
	Created             time.Time            `json:"created,omitempty"`
	Updated             time.Time            `json:"updated,omitempty"`
	CreatedBy           LibraryPanelMetaUser `json:"createdBy,omitempty"`
	UpdatedBy           LibraryPanelMetaUser `json:"updatedBy,omitempty"`
}

// LibraryPanel represents a Grafana library panel.
type LibraryPanel struct {
	Folder      int64                  `json:"folderId,omitempty"`
	Name        string                 `json:"name"`
	Model       map[string]interface{} `json:"model"`
	Type        string                 `json:"type,omitempty"`
	Description string                 `json:"description,omitempty"`
	ID          int64                  `json:"id,omitempty"`
	Kind        int64                  `json:"kind,omitempty"`
	OrgID       int64                  `json:"orgId,omitempty"`
	UID         string                 `json:"uid,omitempty"`
	Version     int64                  `json:"version,omitempty"`
	Meta        LibraryPanelMeta       `json:"meta,omitempty"`
}

// LibraryPanelCreateResponse represents the Grafana API response to creating or saving a library panel.
type LibraryPanelCreateResponse struct {
	Result LibraryPanel `json:"result"`
}

// LibraryPanelGetAllResponse represents the Grafana API response to getting all library panels.
type LibraryPanelGetAllResponse struct {
	TotalCount int64          `json:"totalCount"`
	Page       int64          `json:"page"`
	PerPage    int64          `json:"perPage"`
	Elements   []LibraryPanel `json:"elements"`
}

// LibraryPanelDeleteResponse represents the Grafana API response to deleting a library panel.
type LibraryPanelDeleteResponse struct {
	Message string `json:"message"`
	ID      int64  `json:"id,omitempty"`
}

// LibraryPanelConnection represents a Grafana connection between a library panel and a dashboard.
type LibraryPanelConnection struct {
	ID          int64                `json:"id"`
	Kind        int64                `json:"kind"`
	PanelID     int64                `json:"elementId"`
	DashboardID int64                `json:"connectionId"`
	Created     time.Time            `json:"created"`
	CreatedBy   LibraryPanelMetaUser `json:"createdBy"`
}

// NewLibraryPanel creates a new Grafana library panel.
func (c *Client) NewLibraryPanel(panel LibraryPanel) (*LibraryPanel, error) {
	panel.Kind = int64(1)
	data, err := json.Marshal(panel)
	if err != nil {
		return nil, err
	}

	resp := &LibraryPanelCreateResponse{}
	err = c.request("POST", "/api/library-elements", nil, bytes.NewBuffer(data), &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// Dashboards fetches and returns all dashboards.
func (c *Client) LibraryPanels() ([]LibraryPanel, error) {
	resp := &struct {
		Result LibraryPanelGetAllResponse `json:"result"`
	}{}
	err := c.request("GET", "/api/library-elements", nil, nil, &resp)
	if err != nil {
		return nil, err
	}

	return resp.Result.Elements, err
}

// LibraryPanelByUID gets a library panel by UID.
func (c *Client) LibraryPanelByUID(uid string) (*LibraryPanel, error) {
	resp := &LibraryPanelCreateResponse{}
	path := fmt.Sprintf("/api/library-elements/%s", uid)

	err := c.request("GET", path, nil, nil, &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// LibraryPanelByName gets a library panel by name.
func (c *Client) LibraryPanelByName(name string) (*LibraryPanel, error) {
	var resp struct {
		Result []LibraryPanel `json:"result"`
	}
	path := fmt.Sprintf("/api/library-elements/name/%s", name)

	err := c.request("GET", path, nil, nil, &resp)
	if err != nil {
		return nil, err
	}

	if len(resp.Result) != 1 {
		return nil, fmt.Errorf("error: expected 1 panel from GET library panel by name, got: %v", resp.Result)
	}

	return &resp.Result[0], err
}

// PatchLibraryPanel updates one or more properties of an existing panel that matches the specified UID.
func (c *Client) PatchLibraryPanel(uid string, panel LibraryPanel) (*LibraryPanel, error) {
	path := fmt.Sprintf("/api/library-elements/%s", uid)
	panel.Kind = int64(1)

	// if Version not specified, get current version from API
	if panel.Version == int64(0) {
		remotePanel, err := c.LibraryPanelByUID(panel.UID)
		if err != nil {
			return nil, err
		}
		panel.Version = remotePanel.Version
	}

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

	resp := &LibraryPanelCreateResponse{}
	err = c.request("PATCH", path, nil, bytes.NewBuffer(data), &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// DeleteLibraryPanel deletes a panel by UID.
func (c *Client) DeleteLibraryPanel(uid string) (*LibraryPanelDeleteResponse, error) {
	path := fmt.Sprintf("/api/library-elements/%s", uid)

	resp := &LibraryPanelDeleteResponse{}
	err := c.request("DELETE", path, nil, bytes.NewBuffer(nil), &resp)
	if err != nil {
		return nil, err
	}

	return resp, err
}

// LibraryPanelConnections gets library panel connections by UID.
func (c *Client) LibraryPanelConnections(uid string) (*[]LibraryPanelConnection, error) {
	path := fmt.Sprintf("/api/library-elements/%s/connections", uid)

	resp := struct {
		Result []LibraryPanelConnection `json:"result"`
	}{}

	err := c.request("GET", path, nil, bytes.NewBuffer(nil), &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// LibraryPanelConnectedDashboards gets Dashboards using this Library Panel.
func (c *Client) LibraryPanelConnectedDashboards(uid string) ([]FolderDashboardSearchResponse, error) {
	connections, err := c.LibraryPanelConnections(uid)
	if err != nil {
		return nil, err
	}

	var dashboardIds []int64
	for _, connection := range *connections {
		dashboardIds = append(dashboardIds, connection.DashboardID)
	}

	return c.DashboardsByIDs(dashboardIds)
}
<commit_msg>Return nil in an explicit way<commit_after>package gapi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"
)

// LibraryPanelMetaUser represents the Grafana library panel createdBy and updatedBy fields
type LibraryPanelMetaUser struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	AvatarURL string `json:"folderId"`
}

// LibraryPanelMeta represents Grafana library panel metadata.
type LibraryPanelMeta struct {
	FolderName          string               `json:"folderName,,omitempty"`
	FolderUID           string               `json:"folderUid,omitempty"`
	ConnectedDashboards int64                `json:"connectedDashboards,omitempty"`
	Created             time.Time            `json:"created,omitempty"`
	Updated             time.Time            `json:"updated,omitempty"`
	CreatedBy           LibraryPanelMetaUser `json:"createdBy,omitempty"`
	UpdatedBy           LibraryPanelMetaUser `json:"updatedBy,omitempty"`
}

// LibraryPanel represents a Grafana library panel.
type LibraryPanel struct {
	Folder      int64                  `json:"folderId,omitempty"`
	Name        string                 `json:"name"`
	Model       map[string]interface{} `json:"model"`
	Type        string                 `json:"type,omitempty"`
	Description string                 `json:"description,omitempty"`
	ID          int64                  `json:"id,omitempty"`
	Kind        int64                  `json:"kind,omitempty"`
	OrgID       int64                  `json:"orgId,omitempty"`
	UID         string                 `json:"uid,omitempty"`
	Version     int64                  `json:"version,omitempty"`
	Meta        LibraryPanelMeta       `json:"meta,omitempty"`
}

// LibraryPanelCreateResponse represents the Grafana API response to creating or saving a library panel.
type LibraryPanelCreateResponse struct {
	Result LibraryPanel `json:"result"`
}

// LibraryPanelGetAllResponse represents the Grafana API response to getting all library panels.
type LibraryPanelGetAllResponse struct {
	TotalCount int64          `json:"totalCount"`
	Page       int64          `json:"page"`
	PerPage    int64          `json:"perPage"`
	Elements   []LibraryPanel `json:"elements"`
}

// LibraryPanelDeleteResponse represents the Grafana API response to deleting a library panel.
type LibraryPanelDeleteResponse struct {
	Message string `json:"message"`
	ID      int64  `json:"id,omitempty"`
}

// LibraryPanelConnection represents a Grafana connection between a library panel and a dashboard.
type LibraryPanelConnection struct {
	ID          int64                `json:"id"`
	Kind        int64                `json:"kind"`
	PanelID     int64                `json:"elementId"`
	DashboardID int64                `json:"connectionId"`
	Created     time.Time            `json:"created"`
	CreatedBy   LibraryPanelMetaUser `json:"createdBy"`
}

// NewLibraryPanel creates a new Grafana library panel.
func (c *Client) NewLibraryPanel(panel LibraryPanel) (*LibraryPanel, error) {
	panel.Kind = int64(1)
	data, err := json.Marshal(panel)
	if err != nil {
		return nil, err
	}

	resp := &LibraryPanelCreateResponse{}
	err = c.request("POST", "/api/library-elements", nil, bytes.NewBuffer(data), &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// Dashboards fetches and returns all dashboards.
func (c *Client) LibraryPanels() ([]LibraryPanel, error) {
	resp := &struct {
		Result LibraryPanelGetAllResponse `json:"result"`
	}{}
	err := c.request("GET", "/api/library-elements", nil, nil, &resp)
	if err != nil {
		return nil, err
	}

	return resp.Result.Elements, err
}

// LibraryPanelByUID gets a library panel by UID.
func (c *Client) LibraryPanelByUID(uid string) (*LibraryPanel, error) {
	resp := &LibraryPanelCreateResponse{}
	path := fmt.Sprintf("/api/library-elements/%s", uid)

	err := c.request("GET", path, nil, nil, &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, nil
}

// LibraryPanelByName gets a library panel by name.
func (c *Client) LibraryPanelByName(name string) (*LibraryPanel, error) {
	var resp struct {
		Result []LibraryPanel `json:"result"`
	}
	path := fmt.Sprintf("/api/library-elements/name/%s", name)

	err := c.request("GET", path, nil, nil, &resp)
	if err != nil {
		return nil, err
	}

	if len(resp.Result) != 1 {
		return nil, fmt.Errorf("error: expected 1 panel from GET library panel by name, got: %v", resp.Result)
	}

	return &resp.Result[0], err
}

// PatchLibraryPanel updates one or more properties of an existing panel that matches the specified UID.
func (c *Client) PatchLibraryPanel(uid string, panel LibraryPanel) (*LibraryPanel, error) {
	path := fmt.Sprintf("/api/library-elements/%s", uid)
	panel.Kind = int64(1)

	// if Version not specified, get current version from API
	if panel.Version == int64(0) {
		remotePanel, err := c.LibraryPanelByUID(panel.UID)
		if err != nil {
			return nil, err
		}
		panel.Version = remotePanel.Version
	}

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

	resp := &LibraryPanelCreateResponse{}
	err = c.request("PATCH", path, nil, bytes.NewBuffer(data), &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// DeleteLibraryPanel deletes a panel by UID.
func (c *Client) DeleteLibraryPanel(uid string) (*LibraryPanelDeleteResponse, error) {
	path := fmt.Sprintf("/api/library-elements/%s", uid)

	resp := &LibraryPanelDeleteResponse{}
	err := c.request("DELETE", path, nil, bytes.NewBuffer(nil), &resp)
	if err != nil {
		return nil, err
	}

	return resp, err
}

// LibraryPanelConnections gets library panel connections by UID.
func (c *Client) LibraryPanelConnections(uid string) (*[]LibraryPanelConnection, error) {
	path := fmt.Sprintf("/api/library-elements/%s/connections", uid)

	resp := struct {
		Result []LibraryPanelConnection `json:"result"`
	}{}

	err := c.request("GET", path, nil, bytes.NewBuffer(nil), &resp)
	if err != nil {
		return nil, err
	}

	return &resp.Result, err
}

// LibraryPanelConnectedDashboards gets Dashboards using this Library Panel.
func (c *Client) LibraryPanelConnectedDashboards(uid string) ([]FolderDashboardSearchResponse, error) {
	connections, err := c.LibraryPanelConnections(uid)
	if err != nil {
		return nil, err
	}

	var dashboardIds []int64
	for _, connection := range *connections {
		dashboardIds = append(dashboardIds, connection.DashboardID)
	}

	return c.DashboardsByIDs(dashboardIds)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"path"
	"strings"

	"github.com/spf13/cobra"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/fs"
	"github.com/restic/restic/internal/restic"
	"github.com/restic/restic/internal/walker"
)

var cmdLs = &cobra.Command{
	Use:   "ls [flags] [snapshotID] [dir...]",
	Short: "List files in a snapshot",
	Long: `
The "ls" command lists files and directories in a snapshot.

The special snapshot ID "latest" can be used to list files and
directories of the latest snapshot in the repository. The
--host flag can be used in conjunction to select the latest
snapshot originating from a certain host only.

File listings can optionally be filtered by directories. Any
positional arguments after the snapshot ID are interpreted as
absolute directory paths, and only files inside those directories
will be listed. If the --recursive flag is used, then the filter
will allow traversing into matching directories' subfolders.
Any directory paths specified must be absolute (starting with
a path separator); paths use the forward slash '/' as separator.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runLs(lsOptions, globalOptions, args)
	},
}

// LsOptions collects all options for the ls command.
type LsOptions struct {
	ListLong  bool
	Host      string
	Tags      restic.TagLists
	Paths     []string
	Recursive bool
}

var lsOptions LsOptions

func init() {
	cmdRoot.AddCommand(cmdLs)

	flags := cmdLs.Flags()
	flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
	flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
	flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
	flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
	flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
}

func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
	if len(args) == 0 && opts.Host == "" && len(opts.Tags) == 0 && len(opts.Paths) == 0 {
		return errors.Fatal("Invalid arguments, either give one or more snapshot IDs or set filters.")
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if err = repo.LoadIndex(gopts.ctx); err != nil {
		return err
	}

	// extract any specific directories to walk
	var dirs []string
	if len(args) > 1 {
		dirs = args[1:]
		for _, dir := range dirs {
			if !strings.HasPrefix(dir, "/") {
				return errors.Fatal("All path filters must be absolute, starting with a forward slash '/'")
			}
		}
	}

	ctx, cancel := context.WithCancel(gopts.ctx)
	defer cancel()
	for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args[:1]) {
		Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)

		err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) {
			if err != nil {
				return false, err
			}
			if node == nil {
				return false, nil
			}

			// apply any directory filters
			if len(dirs) > 0 {
				nodeDir := path.Dir(nodepath)

				// this first iteration ensures we do not traverse branches that
				// are not in matching trees or will not lead us to matching trees
				var walk bool
				for _, dir := range dirs {
					approachingMatchingTree := fs.HasPathPrefix(nodeDir, dir)
					inMatchingTree := fs.HasPathPrefix(dir, nodepath)

					// this condition is complex, but it basically requires that we
					// are either approaching a matching tree (not yet deep enough)
					// or: if recursive, we have entered a matching tree; if non-
					// recursive, then that we are at exactly the right depth
					// (we can do the walk correctly by just using the condition of
					// "approachingMatchingTree || inMatchingTree", but it will be
					// much slower for non-recursive queries since it will continue
					// to traverse subtrees that are too deep and won't match -- this
					// extra check allows us to return SkipNode if we've gone TOO deep,
					// which skips all its subfolders)
					if approachingMatchingTree || opts.Recursive || (inMatchingTree && dir == nodeDir) {
						walk = true
						break
					}
				}
				if !walk {
					return false, walker.SkipNode
				}

				// this second iteration ensures that we get an exact match
				// according to the filter and whether we should match subfolders
				var match bool
				for _, dir := range dirs {
					if nodepath == dir {
						// special case: match the directory filter exactly,
						// which may or may not be desirable depending on your
						// use case (for example, this is unnecessary when
						// wanting to simply list the contents of a folder,
						// rather than all files matching a directory prefix)
						match = true
						break
					}
					if opts.Recursive && fs.HasPathPrefix(dir, nodepath) {
						match = true
						break
					}
					if !opts.Recursive && nodeDir == dir {
						match = true
						break
					}
				}
				if !match {
					return false, nil
				}
			}

			Printf("%s\n", formatNode(nodepath, node, lsOptions.ListLong))

			return false, nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>ls: Check dirs before opening the repository<commit_after>package main

import (
	"context"
	"path"
	"strings"

	"github.com/spf13/cobra"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/fs"
	"github.com/restic/restic/internal/restic"
	"github.com/restic/restic/internal/walker"
)

var cmdLs = &cobra.Command{
	Use:   "ls [flags] [snapshotID] [dir...]",
	Short: "List files in a snapshot",
	Long: `
The "ls" command lists files and directories in a snapshot.

The special snapshot ID "latest" can be used to list files and
directories of the latest snapshot in the repository. The
--host flag can be used in conjunction to select the latest
snapshot originating from a certain host only.

File listings can optionally be filtered by directories. Any
positional arguments after the snapshot ID are interpreted as
absolute directory paths, and only files inside those directories
will be listed. If the --recursive flag is used, then the filter
will allow traversing into matching directories' subfolders.
Any directory paths specified must be absolute (starting with
a path separator); paths use the forward slash '/' as separator.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runLs(lsOptions, globalOptions, args)
	},
}

// LsOptions collects all options for the ls command.
type LsOptions struct {
	ListLong  bool
	Host      string
	Tags      restic.TagLists
	Paths     []string
	Recursive bool
}

var lsOptions LsOptions

func init() {
	cmdRoot.AddCommand(cmdLs)

	flags := cmdLs.Flags()
	flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
	flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
	flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
	flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
	flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
}

func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
	if len(args) == 0 && opts.Host == "" && len(opts.Tags) == 0 && len(opts.Paths) == 0 {
		return errors.Fatal("Invalid arguments, either give one or more snapshot IDs or set filters.")
	}

	// extract any specific directories to walk
	var dirs []string
	if len(args) > 1 {
		dirs = args[1:]
		for _, dir := range dirs {
			if !strings.HasPrefix(dir, "/") {
				return errors.Fatal("All path filters must be absolute, starting with a forward slash '/'")
			}
		}
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if err = repo.LoadIndex(gopts.ctx); err != nil {
		return err
	}

	ctx, cancel := context.WithCancel(gopts.ctx)
	defer cancel()
	for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args[:1]) {
		Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)

		err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) {
			if err != nil {
				return false, err
			}
			if node == nil {
				return false, nil
			}

			// apply any directory filters
			if len(dirs) > 0 {
				nodeDir := path.Dir(nodepath)

				// this first iteration ensures we do not traverse branches that
				// are not in matching trees or will not lead us to matching trees
				var walk bool
				for _, dir := range dirs {
					approachingMatchingTree := fs.HasPathPrefix(nodeDir, dir)
					inMatchingTree := fs.HasPathPrefix(dir, nodepath)

					// this condition is complex, but it basically requires that we
					// are either approaching a matching tree (not yet deep enough)
					// or: if recursive, we have entered a matching tree; if non-
					// recursive, then that we are at exactly the right depth
					// (we can do the walk correctly by just using the condition of
					// "approachingMatchingTree || inMatchingTree", but it will be
					// much slower for non-recursive queries since it will continue
					// to traverse subtrees that are too deep and won't match -- this
					// extra check allows us to return SkipNode if we've gone TOO deep,
					// which skips all its subfolders)
					if approachingMatchingTree || opts.Recursive || (inMatchingTree && dir == nodeDir) {
						walk = true
						break
					}
				}
				if !walk {
					return false, walker.SkipNode
				}

				// this second iteration ensures that we get an exact match
				// according to the filter and whether we should match subfolders
				var match bool
				for _, dir := range dirs {
					if nodepath == dir {
						// special case: match the directory filter exactly,
						// which may or may not be desirable depending on your
						// use case (for example, this is unnecessary when
						// wanting to simply list the contents of a folder,
						// rather than all files matching a directory prefix)
						match = true
						break
					}
					if opts.Recursive && fs.HasPathPrefix(dir, nodepath) {
						match = true
						break
					}
					if !opts.Recursive && nodeDir == dir {
						match = true
						break
					}
				}
				if !match {
					return false, nil
				}
			}

			Printf("%s\n", formatNode(nodepath, node, lsOptions.ListLong))

			return false, nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import "golang.org/x/net/websocket"

// Player is an interface which defines methods for controlling a player.
type Player interface {
	// Play the current track.
	Play() error
	// Pause the current track.
	Pause() error
	// NextTrack jumps to the next track.
	NextTrack() error
	// PreviousTrack jumps to the previous track.
	PreviousTrack() error

	// Toggle play/pause.
	TogglePlayPause() error
	// Toggle mute on/off.
	ToggleMute() error

	// SetMute enabled/disables mute.
	SetMute(bool) error
	// SetVolume sets the volume (value should be between 0.0 and 1.0).
	SetVolume(float64) error
	// SetTime sets the current play position
	SetTime(float64) error
}

// Validated wraps a player with validation checks for value-setting methods.
func ValidatedPlayer(p Player) Player {
	return validator{
		Player: p,
	}
}

type validator struct {
	Player
}

// InvalidValueError is an error returned by value-setting methods.
type InvalidValueError string

// Error implements error.
func (v InvalidValueError) Error() string { return string(v) }

// SetVolume implements Player.
func (v validator) SetVolume(f float64) error {
	if f < 0.0 || f > 1.0 {
		return InvalidValueError("invalid volume value: must be between 0.0 and 1.0")
	}
	return v.Player.SetVolume(f)
}

// SetTime implements Player.
func (v validator) SetTime(f float64) error {
	if f < 0.0 {
		return InvalidValueError("invalid time value: must be greater than 0.0")
	}
	return v.Player.SetTime(f)
}

type websocketPlayer struct {
	*websocket.Conn
}

func (w *websocketPlayer) sendCtrlAction(data interface{}) error {
	return websocket.JSON.Send(w.Conn, struct {
		Action string
		Data   interface{}
	}{
		Action: CtrlAction,
		Data:   data,
	})
}

func (w *websocketPlayer) sendCtrlValue(key string, value interface{}) error {
	return w.sendCtrlAction(struct {
		Key   string
		Value interface{}
	}{
		Key:   key,
		Value: value,
	})
}

func (w websocketPlayer) Play() error            { return w.sendCtrlAction("PLAY") }
func (w websocketPlayer) Pause() error           { return w.sendCtrlAction("PAUSE") }
func (w websocketPlayer) NextTrack() error       { return w.sendCtrlAction("NEXT") }
func (w websocketPlayer) PreviousTrack() error   { return w.sendCtrlAction("PREV") }
func (w websocketPlayer) TogglePlayPause() error { return w.sendCtrlAction("TOGGLE_PLAY_PAUSE") }
func (w websocketPlayer) ToggleMute() error      { return w.sendCtrlAction("TOGGLE_MUTE") }

func (w websocketPlayer) SetMute(b bool) error      { return w.sendCtrlValue("mute", b) }
func (w websocketPlayer) SetVolume(f float64) error { return w.sendCtrlValue("volume", f) }
func (w websocketPlayer) SetTime(f float64) error   { return w.sendCtrlValue("time", f) }
<commit_msg>Implement MultiPlayer to dispatch calls to multiple Players<commit_after>package main

import "golang.org/x/net/websocket"

// Player is an interface which defines methods for controlling a player.
type Player interface {
	// Play the current track.
	Play() error
	// Pause the current track.
	Pause() error
	// NextTrack jumps to the next track.
	NextTrack() error
	// PreviousTrack jumps to the previous track.
	PreviousTrack() error

	// Toggle play/pause.
	TogglePlayPause() error
	// Toggle mute on/off.
	ToggleMute() error

	// SetMute enabled/disables mute.
	SetMute(bool) error
	// SetVolume sets the volume (value should be between 0.0 and 1.0).
	SetVolume(float64) error
	// SetTime sets the current play position
	SetTime(float64) error
}

type multiPlayer struct {
	players []Player
}

// MultiPlayer returns a player that will apply calls to all provided Players
// in sequence.  If an error is returning by a Player then it is returned
// immediately.
func MultiPlayer(players ...Player) Player {
	return multiPlayer{
		players: players,
	}
}

type cmdFn func(Player) error

func (m multiPlayer) applyCmdFn(fn cmdFn) error {
	for _, p := range m.players {
		err := fn(p)
		if err != nil {
			return err
		}
	}
	return nil
}

type setFloatFn func(Player, float64) error

func (m multiPlayer) applySetFloatFn(fn setFloatFn, f float64) error {
	for _, p := range m.players {
		err := fn(p, f)
		if err != nil {
			return err
		}
	}
	return nil
}

func (m multiPlayer) Play() error            { return m.applyCmdFn(Player.Play) }
func (m multiPlayer) Pause() error           { return m.applyCmdFn(Player.Pause) }
func (m multiPlayer) NextTrack() error       { return m.applyCmdFn(Player.NextTrack) }
func (m multiPlayer) PreviousTrack() error   { return m.applyCmdFn(Player.PreviousTrack) }
func (m multiPlayer) TogglePlayPause() error { return m.applyCmdFn(Player.TogglePlayPause) }
func (m multiPlayer) ToggleMute() error      { return m.applyCmdFn(Player.ToggleMute) }

func (m multiPlayer) SetVolume(f float64) error { return m.applySetFloatFn(Player.SetVolume, f) }
func (m multiPlayer) SetTime(f float64) error   { return m.applySetFloatFn(Player.SetTime, f) }

func (m multiPlayer) SetMute(b bool) error {
	for _, p := range m.players {
		err := p.SetMute(b)
		if err != nil {
			return err
		}
	}
	return nil
}

// Validated wraps a player with validation checks for value-setting methods.
func ValidatedPlayer(p Player) Player {
	return validator{
		Player: p,
	}
}

type validator struct {
	Player
}

// InvalidValueError is an error returned by value-setting methods.
type InvalidValueError string

// Error implements error.
func (v InvalidValueError) Error() string { return string(v) }

// SetVolume implements Player.
func (v validator) SetVolume(f float64) error {
	if f < 0.0 || f > 1.0 {
		return InvalidValueError("invalid volume value: must be between 0.0 and 1.0")
	}
	return v.Player.SetVolume(f)
}

// SetTime implements Player.
func (v validator) SetTime(f float64) error {
	if f < 0.0 {
		return InvalidValueError("invalid time value: must be greater than 0.0")
	}
	return v.Player.SetTime(f)
}

type websocketPlayer struct {
	*websocket.Conn
}

func (w *websocketPlayer) sendCtrlAction(data interface{}) error {
	return websocket.JSON.Send(w.Conn, struct {
		Action string
		Data   interface{}
	}{
		Action: CtrlAction,
		Data:   data,
	})
}

func (w *websocketPlayer) sendCtrlValue(key string, value interface{}) error {
	return w.sendCtrlAction(struct {
		Key   string
		Value interface{}
	}{
		Key:   key,
		Value: value,
	})
}

func (w websocketPlayer) Play() error            { return w.sendCtrlAction("PLAY") }
func (w websocketPlayer) Pause() error           { return w.sendCtrlAction("PAUSE") }
func (w websocketPlayer) NextTrack() error       { return w.sendCtrlAction("NEXT") }
func (w websocketPlayer) PreviousTrack() error   { return w.sendCtrlAction("PREV") }
func (w websocketPlayer) TogglePlayPause() error { return w.sendCtrlAction("TOGGLE_PLAY_PAUSE") }
func (w websocketPlayer) ToggleMute() error      { return w.sendCtrlAction("TOGGLE_MUTE") }

func (w websocketPlayer) SetMute(b bool) error      { return w.sendCtrlValue("mute", b) }
func (w websocketPlayer) SetVolume(f float64) error { return w.sendCtrlValue("volume", f) }
func (w websocketPlayer) SetTime(f float64) error   { return w.sendCtrlValue("time", f) }
<|endoftext|>


<commit_before>package linux

import (
	"io/ioutil"
	"regexp"
	"strconv"
	"strings"
)

type CPUInfo struct {
	Processors []Processor `json:"processors"`
}

func (self *CPUInfo) NumCPU() int {
	return len(self.Processors)
}

type Processor struct {
	Id        int64    `json:"id"`
	VendorId  string   `json:"vendor_id"`
	Model     int64    `json:"model"`
	ModelName string   `json:"model_name"`
	Flags     []string `json:"flags"`
	Cores     int64    `json:"cores"`
	MHz       float64  `json:"mhz"`
}

var cpuinfoRegExp = regexp.MustCompile("([^:]*?)\\s*:\\s*(.*)$")

func ReadCPUInfo(path string) (*CPUInfo, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	content := string(b)
	lines := strings.Split(content, "\n")

	var cpuinfo = CPUInfo{}
	var processor = &Processor{}

	for _, line := range lines {
		var key string
		var value string

		if len(line) == 0 {
			// end of processor
			cpuinfo.Processors = append(cpuinfo.Processors, *processor)
			processor = &Processor{}
			continue
		}

		submatches := cpuinfoRegExp.FindStringSubmatch(line)
		key = submatches[1]
		value = submatches[2]

		switch key {
		case "processor":
			processor.Id, _ = strconv.ParseInt(value, 10, 32)
		case "vendor_id":
			processor.VendorId = value
		case "model":
			processor.Model, _ = strconv.ParseInt(value, 10, 32)
		case "model name":
			processor.ModelName = value
		case "flags":
			processor.Flags = strings.Fields(value)
		case "cpu cores":
			processor.Cores, _ = strconv.ParseInt(value, 10, 32)
		case "cpu MHz":
			processor.MHz, _ = strconv.ParseFloat(value, 64)
		}
		/*
			processor	: 0
			vendor_id	: GenuineIntel
			cpu family	: 6
			model		: 26
			model name	: Intel(R) Xeon(R) CPU           L5520  @ 2.27GHz
		*/
	}
	return &cpuinfo, nil
}
<commit_msg>Prevent "Ghost" CPUs<commit_after>package linux

import (
	"io/ioutil"
	"regexp"
	"strconv"
	"strings"
)

type CPUInfo struct {
	Processors []Processor `json:"processors"`
}

func (self *CPUInfo) NumCPU() int {
	return len(self.Processors)
}

type Processor struct {
	Id        int64    `json:"id"`
	VendorId  string   `json:"vendor_id"`
	Model     int64    `json:"model"`
	ModelName string   `json:"model_name"`
	Flags     []string `json:"flags"`
	Cores     int64    `json:"cores"`
	MHz       float64  `json:"mhz"`
}

var cpuinfoRegExp = regexp.MustCompile("([^:]*?)\\s*:\\s*(.*)$")

func ReadCPUInfo(path string) (*CPUInfo, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	content := string(b)
	lines := strings.Split(content, "\n")

	var cpuinfo = CPUInfo{}
	var processor = &Processor{}

	for i, line := range lines {
		var key string
		var value string

		if len(line) == 0 && i != len(lines)-1 {
			// end of processor
			cpuinfo.Processors = append(cpuinfo.Processors, *processor)
			processor = &Processor{}
			continue
		} else if i == len(lines)-1 {
			continue
		}

		submatches := cpuinfoRegExp.FindStringSubmatch(line)
		key = submatches[1]
		value = submatches[2]

		switch key {
		case "processor":
			processor.Id, _ = strconv.ParseInt(value, 10, 32)
		case "vendor_id":
			processor.VendorId = value
		case "model":
			processor.Model, _ = strconv.ParseInt(value, 10, 32)
		case "model name":
			processor.ModelName = value
		case "flags":
			processor.Flags = strings.Fields(value)
		case "cpu cores":
			processor.Cores, _ = strconv.ParseInt(value, 10, 32)
		case "cpu MHz":
			processor.MHz, _ = strconv.ParseFloat(value, 64)
		}
		/*
			processor	: 0
			vendor_id	: GenuineIntel
			cpu family	: 6
			model		: 26
			model name	: Intel(R) Xeon(R) CPU           L5520  @ 2.27GHz
		*/
	}
	return &cpuinfo, nil
}
<|endoftext|>


<commit_before>package stamp

import (
	"net"
	"strconv"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/dedis/prifi/coco"
	"github.com/dedis/prifi/coco/coconet"
	"github.com/dedis/prifi/coco/hashid"
	"github.com/dedis/prifi/coco/proof"
	"github.com/dedis/prifi/coco/sign"
	"github.com/dedis/prifi/coco/test/logutils"
)

type Server struct {
	coco.Signer
	name    string
	Clients map[string]coconet.Conn

	// for aggregating messages from clients
	mux        sync.Mutex
	Queue      [][]MustReplyMessage
	READING    int
	PROCESSING int

	// Leaves, Root and Proof for a round
	Leaves []hashid.HashId // can be removed after we verify protocol
	Root   hashid.HashId
	Proofs []proof.Proof

	rLock     sync.Mutex
	maxRounds int
	closeChan chan bool

	Logger   string
	Hostname string
	App      string
}

func NewServer(signer coco.Signer) *Server {
	s := &Server{}

	s.Clients = make(map[string]coconet.Conn)
	s.Queue = make([][]MustReplyMessage, 2)
	s.READING = 0
	s.PROCESSING = 1

	s.Signer = signer
	s.Signer.RegisterAnnounceFunc(s.OnAnnounce())
	s.Signer.RegisterDoneFunc(s.OnDone())
	s.rLock = sync.Mutex{}

	// listen for client requests at one port higher
	// than the signing node
	h, p, err := net.SplitHostPort(s.Signer.Name())
	if err == nil {
		i, err := strconv.Atoi(p)
		if err != nil {
			log.Fatal(err)
		}
		s.name = net.JoinHostPort(h, strconv.Itoa(i+1))
	}
	s.Queue[s.READING] = make([]MustReplyMessage, 0)
	s.Queue[s.PROCESSING] = make([]MustReplyMessage, 0)
	s.closeChan = make(chan bool, 5)
	return s
}

var clientNumber int = 0

func (s *Server) Close() {
	log.Printf("closing stampserver: %p", s)
	s.closeChan <- true
	s.Signer.Close()
}

// listen for clients connections
// this server needs to be running on a different port
// than the Signer that is beneath it
func (s *Server) Listen() error {
	// log.Println("Listening @ ", s.name)
	ln, err := net.Listen("tcp4", s.name)
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			// log.Printf("LISTENING TO CLIENTS: %p", s)
			conn, err := ln.Accept()
			if err != nil {
				// handle error
				log.Errorln("failed to accept connection")
				continue
			}

			c := coconet.NewTCPConnFromNet(conn)
			// log.Println("CLIENT TCP CONNECTION SUCCESSFULLY ESTABLISHED:", c)

			if _, ok := s.Clients[c.Name()]; !ok {
				s.Clients[c.Name()] = c

				go func(c coconet.Conn) {
					for {
						tsm := TimeStampMessage{}
						err := c.Get(&tsm)
						if err != nil {
							log.Errorf("%p Failed to get from child:", s, err)
							s.Close()
							return
						}
						switch tsm.Type {
						default:
							log.Errorf("Message of unknown type: %v\n", tsm.Type)
						case StampRequestType:
							// log.Println("RECEIVED STAMP REQUEST")
							s.mux.Lock()
							READING := s.READING
							s.Queue[READING] = append(s.Queue[READING],
								MustReplyMessage{Tsm: tsm, To: c.Name()})
							s.mux.Unlock()
						}
					}
				}(c)
			}

		}
	}()

	return nil
}

// Used for goconns
// should only be used if clients are created in batch
func (s *Server) ListenToClients() {
	// log.Printf("LISTENING TO CLIENTS: %p", s, s.Clients)
	for _, c := range s.Clients {
		go func(c coconet.Conn) {
			for {
				tsm := TimeStampMessage{}
				err := c.Get(&tsm)
				if err == coconet.ErrClosed {
					log.Errorf("%p Failed to get from client:", s, err)
					s.Close()
					return
				}
				if err != nil {
					log.WithFields(log.Fields{
						"file": logutils.File(),
					}).Errorf("%p failed To get message:", s, err)
				}
				switch tsm.Type {
				default:
					log.Errorln("Message of unknown type")
				case StampRequestType:
					// log.Println("STAMP REQUEST")
					s.mux.Lock()
					READING := s.READING
					s.Queue[READING] = append(s.Queue[READING],
						MustReplyMessage{Tsm: tsm, To: c.Name()})
					s.mux.Unlock()
				}
			}
		}(c)
	}
}

func (s *Server) ConnectToLogger() {
	return
	if s.Logger == "" || s.Hostname == "" || s.App == "" {
		log.Println("skipping connect to logger")
		return
	}
	log.Println("Connecting to Logger")
	lh, _ := logutils.NewLoggerHook(s.Logger, s.Hostname, s.App)
	log.Println("Connected to Logger")
	log.AddHook(lh)
}

func (s *Server) LogReRun(nextRole string, curRole string) {
	if nextRole == "root" {
		var messg = s.Name() + " became root"
		if curRole == "root" {
			messg = s.Name() + " remained root"
		}

		go s.ConnectToLogger()

		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "role_change",
		}).Infoln(messg)
		// log.Printf("role change: %p", s)

	} else {
		var messg = s.Name() + " remained regular"
		if curRole == "root" {
			messg = s.Name() + " became regular"
		}

		if curRole == "root" {
			log.WithFields(log.Fields{
				"file": logutils.File(),
				"type": "role_change",
			}).Infoln(messg)
			log.Printf("role change: %p", s)
		}

	}

}

func (s *Server) runAsRoot(nRounds int) string {
	// every 5 seconds start a new round
	ticker := time.Tick(ROUND_TIME)
	if s.LastRound()+1 > int64(nRounds) {
		log.Errorln(s.Name(), "runAsRoot called with too large round number")
		return "close"
	}

	log.Infoln(s.Name(), "running as root", s.LastRound(), int64(nRounds))
	for {
		select {
		case nextRole := <-s.ViewChangeCh():
			return nextRole
			// s.reRunWith(nextRole, nRounds, true)
		case <-ticker:

			start := time.Now()
			log.Println(s.Name(), "is STAMP SERVER STARTING SIGNING ROUND FOR:", s.LastRound()+1, "of", nRounds)

			err := s.StartSigningRound()
			if err == sign.ChangingViewError {
				// report change in view, and continue with the select
				log.WithFields(log.Fields{
					"file": logutils.File(),
					"type": "view_change",
				}).Info("Tried to stary signing round on " + s.Name() + " but it reports view change in progress")
				// skip # of failed round
				// s.SetLastSeenRound(s.LastRound() + 1)
				break
			}

			if err != nil {
				log.Errorln(err)
				break
			}

			if s.LastRound()+1 >= int64(nRounds) {
				log.Errorln(s.Name(), "reports exceeded the max round: terminating", s.LastRound()+1, ">=", nRounds)
				return "close"
			}

			elapsed := time.Since(start)
			log.WithFields(log.Fields{
				"file":  logutils.File(),
				"type":  "root_round",
				"round": s.LastRound(),
				"time":  elapsed,
			}).Info("root round")

		}
	}
}

func (s *Server) runAsRegular() string {
	select {
	case <-s.closeChan:
		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "close",
		}).Infoln("server" + s.Name() + "has closed")
		return ""

	case nextRole := <-s.ViewChangeCh():
		return nextRole
	}
}

// Listen on client connections. If role is root also send annoucement
// for all of the nRounds
func (s *Server) Run(role string, nRounds int) {
	// defer func() {
	// 	log.Infoln(s.Name(), "CLOSE AFTER RUN")
	// 	s.Close()
	// }()

	closed := make(chan bool, 1)

	go func() { err := s.Signer.Listen(); closed <- true; s.Close(); log.Error(err) }()
	if role == "test_connect" {
		role = "regular"
		go func() {
			time.Sleep(90 * time.Second)
			hostlist := s.Hostlist()
			ticker := time.Tick(30 * time.Second)
			i := 1
			for _ = range ticker {
				select {
				case <-closed:
					log.Println("server.Run: received closed")
					return
				default:
				}
				if i%2 == 0 {
					log.Println("removing self")
					s.Signer.RemoveSelf()
				} else {
					log.Println("adding self")
					s.Signer.AddSelf(hostlist[(i/2)%len(hostlist)])
				}
			}
		}()
	}
	s.rLock.Lock()
	s.maxRounds = nRounds
	s.rLock.Unlock()

	var nextRole string // next role when view changes
	for {
		switch role {

		case "root":
			log.Println("running as root")
			nextRole = s.runAsRoot(nRounds)
		case "regular":
			log.Println("running as regular")
			nextRole = s.runAsRegular()
		case "test":
			log.Println("running as test")
			ticker := time.Tick(2000 * time.Millisecond)
			for _ = range ticker {
				s.AggregateCommits(0)
			}
		default:
			log.Println("UNABLE TO RUN AS ANYTHING")
			return
		}

		// log.Println(s.Name(), "nextRole: ", nextRole)
		if nextRole == "close" {
			s.Close()
			return
		}
		if nextRole == "" {
			return
		}
		s.LogReRun(nextRole, role)
		role = nextRole
	}

}

func (s *Server) OnAnnounce() coco.CommitFunc {
	return func(view int) []byte {
		//log.Println("Aggregating Commits")
		return s.AggregateCommits(view)
	}
}

func (s *Server) OnDone() coco.DoneFunc {
	return func(view int, SNRoot hashid.HashId, LogHash hashid.HashId, p proof.Proof) {
		s.mux.Lock()
		for i, msg := range s.Queue[s.PROCESSING] {
			// proof to get from s.Root to big root
			combProof := make(proof.Proof, len(p))
			copy(combProof, p)

			// add my proof to get from a leaf message to my root s.Root
			combProof = append(combProof, s.Proofs[i]...)

			// proof that i can get from a leaf message to the big root
			if coco.DEBUG == true {
				proof.CheckProof(s.Signer.(*sign.Node).Suite().Hash, SNRoot, s.Leaves[i], combProof)
			}

			respMessg := TimeStampMessage{
				Type:  StampReplyType,
				ReqNo: msg.Tsm.ReqNo,
				Srep:  &StampReply{Sig: SNRoot, Prf: combProof}}

			s.PutToClient(msg.To, respMessg)
		}
		s.mux.Unlock()
	}

}

func (s *Server) AggregateCommits(view int) []byte {
	//log.Println(s.Name(), "calling AggregateCommits")
	s.mux.Lock()
	// get data from s once to avoid refetching from structure
	Queue := s.Queue
	READING := s.READING
	PROCESSING := s.PROCESSING
	// messages read will now be processed
	READING, PROCESSING = PROCESSING, READING
	s.READING, s.PROCESSING = s.PROCESSING, s.READING
	s.Queue[READING] = s.Queue[READING][:0]

	// give up if nothing to process
	if len(Queue[PROCESSING]) == 0 {
		s.mux.Unlock()
		s.Root = make([]byte, hashid.Size)
		s.Proofs = make([]proof.Proof, 1)
		return s.Root
	}

	// pull out to be Merkle Tree leaves
	s.Leaves = make([]hashid.HashId, 0)
	for _, msg := range Queue[PROCESSING] {
		s.Leaves = append(s.Leaves, hashid.HashId(msg.Tsm.Sreq.Val))
	}
	s.mux.Unlock()

	// non root servers keep track of rounds here
	if !s.IsRoot(view) {
		s.rLock.Lock()
		lsr := s.LastRound()
		mr := s.maxRounds
		s.rLock.Unlock()
		// if this is our last round then close the connections
		if lsr >= int64(mr) && mr >= 0 {
			s.closeChan <- true
		}
	}

	// create Merkle tree for this round's messages and check corectness
	s.Root, s.Proofs = proof.ProofTree(s.Suite().Hash, s.Leaves)
	if coco.DEBUG == true {
		if proof.CheckLocalProofs(s.Suite().Hash, s.Root, s.Leaves, s.Proofs) == true {
			log.Println("Local Proofs of", s.Name(), "successful for round "+strconv.Itoa(int(s.LastRound())))
		} else {
			panic("Local Proofs" + s.Name() + " unsuccessful for round " + strconv.Itoa(int(s.LastRound())))
		}
	}

	return s.Root
}

// Send message to client given by name
func (s *Server) PutToClient(name string, data coconet.BinaryMarshaler) {
	err := s.Clients[name].Put(data)
	if err == coconet.ErrClosed {
		s.Close()
		return
	}
	if err != nil && err != coconet.ErrNotEstablished {
		log.Warnf("%p error putting to client: %v", s, err)
	}
}
<commit_msg>fixed bug in test_connect<commit_after>package stamp

import (
	"net"
	"strconv"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/dedis/prifi/coco"
	"github.com/dedis/prifi/coco/coconet"
	"github.com/dedis/prifi/coco/hashid"
	"github.com/dedis/prifi/coco/proof"
	"github.com/dedis/prifi/coco/sign"
	"github.com/dedis/prifi/coco/test/logutils"
)

type Server struct {
	coco.Signer
	name    string
	Clients map[string]coconet.Conn

	// for aggregating messages from clients
	mux        sync.Mutex
	Queue      [][]MustReplyMessage
	READING    int
	PROCESSING int

	// Leaves, Root and Proof for a round
	Leaves []hashid.HashId // can be removed after we verify protocol
	Root   hashid.HashId
	Proofs []proof.Proof

	rLock     sync.Mutex
	maxRounds int
	closeChan chan bool

	Logger   string
	Hostname string
	App      string
}

func NewServer(signer coco.Signer) *Server {
	s := &Server{}

	s.Clients = make(map[string]coconet.Conn)
	s.Queue = make([][]MustReplyMessage, 2)
	s.READING = 0
	s.PROCESSING = 1

	s.Signer = signer
	s.Signer.RegisterAnnounceFunc(s.OnAnnounce())
	s.Signer.RegisterDoneFunc(s.OnDone())
	s.rLock = sync.Mutex{}

	// listen for client requests at one port higher
	// than the signing node
	h, p, err := net.SplitHostPort(s.Signer.Name())
	if err == nil {
		i, err := strconv.Atoi(p)
		if err != nil {
			log.Fatal(err)
		}
		s.name = net.JoinHostPort(h, strconv.Itoa(i+1))
	}
	s.Queue[s.READING] = make([]MustReplyMessage, 0)
	s.Queue[s.PROCESSING] = make([]MustReplyMessage, 0)
	s.closeChan = make(chan bool, 5)
	return s
}

var clientNumber int = 0

func (s *Server) Close() {
	log.Printf("closing stampserver: %p", s)
	s.closeChan <- true
	s.Signer.Close()
}

// listen for clients connections
// this server needs to be running on a different port
// than the Signer that is beneath it
func (s *Server) Listen() error {
	// log.Println("Listening @ ", s.name)
	ln, err := net.Listen("tcp4", s.name)
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			// log.Printf("LISTENING TO CLIENTS: %p", s)
			conn, err := ln.Accept()
			if err != nil {
				// handle error
				log.Errorln("failed to accept connection")
				continue
			}

			c := coconet.NewTCPConnFromNet(conn)
			// log.Println("CLIENT TCP CONNECTION SUCCESSFULLY ESTABLISHED:", c)

			if _, ok := s.Clients[c.Name()]; !ok {
				s.Clients[c.Name()] = c

				go func(c coconet.Conn) {
					for {
						tsm := TimeStampMessage{}
						err := c.Get(&tsm)
						if err != nil {
							log.Errorf("%p Failed to get from child:", s, err)
							s.Close()
							return
						}
						switch tsm.Type {
						default:
							log.Errorf("Message of unknown type: %v\n", tsm.Type)
						case StampRequestType:
							// log.Println("RECEIVED STAMP REQUEST")
							s.mux.Lock()
							READING := s.READING
							s.Queue[READING] = append(s.Queue[READING],
								MustReplyMessage{Tsm: tsm, To: c.Name()})
							s.mux.Unlock()
						}
					}
				}(c)
			}

		}
	}()

	return nil
}

// Used for goconns
// should only be used if clients are created in batch
func (s *Server) ListenToClients() {
	// log.Printf("LISTENING TO CLIENTS: %p", s, s.Clients)
	for _, c := range s.Clients {
		go func(c coconet.Conn) {
			for {
				tsm := TimeStampMessage{}
				err := c.Get(&tsm)
				if err == coconet.ErrClosed {
					log.Errorf("%p Failed to get from client:", s, err)
					s.Close()
					return
				}
				if err != nil {
					log.WithFields(log.Fields{
						"file": logutils.File(),
					}).Errorf("%p failed To get message:", s, err)
				}
				switch tsm.Type {
				default:
					log.Errorln("Message of unknown type")
				case StampRequestType:
					// log.Println("STAMP REQUEST")
					s.mux.Lock()
					READING := s.READING
					s.Queue[READING] = append(s.Queue[READING],
						MustReplyMessage{Tsm: tsm, To: c.Name()})
					s.mux.Unlock()
				}
			}
		}(c)
	}
}

func (s *Server) ConnectToLogger() {
	return
	if s.Logger == "" || s.Hostname == "" || s.App == "" {
		log.Println("skipping connect to logger")
		return
	}
	log.Println("Connecting to Logger")
	lh, _ := logutils.NewLoggerHook(s.Logger, s.Hostname, s.App)
	log.Println("Connected to Logger")
	log.AddHook(lh)
}

func (s *Server) LogReRun(nextRole string, curRole string) {
	if nextRole == "root" {
		var messg = s.Name() + " became root"
		if curRole == "root" {
			messg = s.Name() + " remained root"
		}

		go s.ConnectToLogger()

		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "role_change",
		}).Infoln(messg)
		// log.Printf("role change: %p", s)

	} else {
		var messg = s.Name() + " remained regular"
		if curRole == "root" {
			messg = s.Name() + " became regular"
		}

		if curRole == "root" {
			log.WithFields(log.Fields{
				"file": logutils.File(),
				"type": "role_change",
			}).Infoln(messg)
			log.Printf("role change: %p", s)
		}

	}

}

func (s *Server) runAsRoot(nRounds int) string {
	// every 5 seconds start a new round
	ticker := time.Tick(ROUND_TIME)
	if s.LastRound()+1 > int64(nRounds) {
		log.Errorln(s.Name(), "runAsRoot called with too large round number")
		return "close"
	}

	log.Infoln(s.Name(), "running as root", s.LastRound(), int64(nRounds))
	for {
		select {
		case nextRole := <-s.ViewChangeCh():
			return nextRole
			// s.reRunWith(nextRole, nRounds, true)
		case <-ticker:

			start := time.Now()
			log.Println(s.Name(), "is STAMP SERVER STARTING SIGNING ROUND FOR:", s.LastRound()+1, "of", nRounds)

			err := s.StartSigningRound()
			if err == sign.ChangingViewError {
				// report change in view, and continue with the select
				log.WithFields(log.Fields{
					"file": logutils.File(),
					"type": "view_change",
				}).Info("Tried to stary signing round on " + s.Name() + " but it reports view change in progress")
				// skip # of failed round
				// s.SetLastSeenRound(s.LastRound() + 1)
				break
			}

			if err != nil {
				log.Errorln(err)
				break
			}

			if s.LastRound()+1 >= int64(nRounds) {
				log.Errorln(s.Name(), "reports exceeded the max round: terminating", s.LastRound()+1, ">=", nRounds)
				return "close"
			}

			elapsed := time.Since(start)
			log.WithFields(log.Fields{
				"file":  logutils.File(),
				"type":  "root_round",
				"round": s.LastRound(),
				"time":  elapsed,
			}).Info("root round")

		}
	}
}

func (s *Server) runAsRegular() string {
	select {
	case <-s.closeChan:
		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "close",
		}).Infoln("server" + s.Name() + "has closed")
		return ""

	case nextRole := <-s.ViewChangeCh():
		return nextRole
	}
}

// Listen on client connections. If role is root also send annoucement
// for all of the nRounds
func (s *Server) Run(role string, nRounds int) {
	// defer func() {
	// 	log.Infoln(s.Name(), "CLOSE AFTER RUN")
	// 	s.Close()
	// }()

	closed := make(chan bool, 1)

	go func() { err := s.Signer.Listen(); closed <- true; s.Close(); log.Error(err) }()
	if role == "test_connect" {
		role = "regular"
		go func() {
			time.Sleep(90 * time.Second)
			hostlist := s.Hostlist()
			ticker := time.Tick(30 * time.Second)
			i := 0
			for _ = range ticker {
				select {
				case <-closed:
					log.Println("server.Run: received closed")
					return
				default:
				}
				if i%2 == 0 {
					log.Println("removing self")
					s.Signer.RemoveSelf()
				} else {
					log.Println("adding self")
					s.Signer.AddSelf(hostlist[(i/2)%len(hostlist)])
				}
				i++
			}
		}()
	}
	s.rLock.Lock()
	s.maxRounds = nRounds
	s.rLock.Unlock()

	var nextRole string // next role when view changes
	for {
		switch role {

		case "root":
			log.Println("running as root")
			nextRole = s.runAsRoot(nRounds)
		case "regular":
			log.Println("running as regular")
			nextRole = s.runAsRegular()
		case "test":
			log.Println("running as test")
			ticker := time.Tick(2000 * time.Millisecond)
			for _ = range ticker {
				s.AggregateCommits(0)
			}
		default:
			log.Println("UNABLE TO RUN AS ANYTHING")
			return
		}

		// log.Println(s.Name(), "nextRole: ", nextRole)
		if nextRole == "close" {
			s.Close()
			return
		}
		if nextRole == "" {
			return
		}
		s.LogReRun(nextRole, role)
		role = nextRole
	}

}

func (s *Server) OnAnnounce() coco.CommitFunc {
	return func(view int) []byte {
		//log.Println("Aggregating Commits")
		return s.AggregateCommits(view)
	}
}

func (s *Server) OnDone() coco.DoneFunc {
	return func(view int, SNRoot hashid.HashId, LogHash hashid.HashId, p proof.Proof) {
		s.mux.Lock()
		for i, msg := range s.Queue[s.PROCESSING] {
			// proof to get from s.Root to big root
			combProof := make(proof.Proof, len(p))
			copy(combProof, p)

			// add my proof to get from a leaf message to my root s.Root
			combProof = append(combProof, s.Proofs[i]...)

			// proof that i can get from a leaf message to the big root
			if coco.DEBUG == true {
				proof.CheckProof(s.Signer.(*sign.Node).Suite().Hash, SNRoot, s.Leaves[i], combProof)
			}

			respMessg := TimeStampMessage{
				Type:  StampReplyType,
				ReqNo: msg.Tsm.ReqNo,
				Srep:  &StampReply{Sig: SNRoot, Prf: combProof}}

			s.PutToClient(msg.To, respMessg)
		}
		s.mux.Unlock()
	}

}

func (s *Server) AggregateCommits(view int) []byte {
	//log.Println(s.Name(), "calling AggregateCommits")
	s.mux.Lock()
	// get data from s once to avoid refetching from structure
	Queue := s.Queue
	READING := s.READING
	PROCESSING := s.PROCESSING
	// messages read will now be processed
	READING, PROCESSING = PROCESSING, READING
	s.READING, s.PROCESSING = s.PROCESSING, s.READING
	s.Queue[READING] = s.Queue[READING][:0]

	// give up if nothing to process
	if len(Queue[PROCESSING]) == 0 {
		s.mux.Unlock()
		s.Root = make([]byte, hashid.Size)
		s.Proofs = make([]proof.Proof, 1)
		return s.Root
	}

	// pull out to be Merkle Tree leaves
	s.Leaves = make([]hashid.HashId, 0)
	for _, msg := range Queue[PROCESSING] {
		s.Leaves = append(s.Leaves, hashid.HashId(msg.Tsm.Sreq.Val))
	}
	s.mux.Unlock()

	// non root servers keep track of rounds here
	if !s.IsRoot(view) {
		s.rLock.Lock()
		lsr := s.LastRound()
		mr := s.maxRounds
		s.rLock.Unlock()
		// if this is our last round then close the connections
		if lsr >= int64(mr) && mr >= 0 {
			s.closeChan <- true
		}
	}

	// create Merkle tree for this round's messages and check corectness
	s.Root, s.Proofs = proof.ProofTree(s.Suite().Hash, s.Leaves)
	if coco.DEBUG == true {
		if proof.CheckLocalProofs(s.Suite().Hash, s.Root, s.Leaves, s.Proofs) == true {
			log.Println("Local Proofs of", s.Name(), "successful for round "+strconv.Itoa(int(s.LastRound())))
		} else {
			panic("Local Proofs" + s.Name() + " unsuccessful for round " + strconv.Itoa(int(s.LastRound())))
		}
	}

	return s.Root
}

// Send message to client given by name
func (s *Server) PutToClient(name string, data coconet.BinaryMarshaler) {
	err := s.Clients[name].Put(data)
	if err == coconet.ErrClosed {
		s.Close()
		return
	}
	if err != nil && err != coconet.ErrNotEstablished {
		log.Warnf("%p error putting to client: %v", s, err)
	}
}
<|endoftext|>


<commit_before>// Package event provides the functions for creating, editing, and showing events
package event

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"text/template"

	helpers "github.com/devopsdays/devopsdays-cli/helpers"
	paths "github.com/devopsdays/devopsdays-cli/helpers/paths"
	"github.com/devopsdays/devopsdays-cli/images"
	"github.com/devopsdays/devopsdays-cli/model"
	"github.com/fatih/color"
	survey "gopkg.in/AlecAivazis/survey.v1"
)

// LogoPath is the fully qualified path to the event's logo
var LogoPath string

// SquareLogoPath is the fully qualified path to the event's square logo
var SquareLogoPath string

// the questions to ask
var qsCreateEvent = []*survey.Question{
	{
		Name: "twitter",
		Prompt: &survey.Input{
			Message: "Enter your devopsdays event twitter handle (defaults to @devopsdays):",
			Help:    "Twitter username can include the @ symbol or not. Examples: '@devopsdays' or 'devopsdays",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && !helpers.ValidateField(str, "twitter") {
				return errors.New("Please enter a valid Twitter handle. Spaces are not allowed.")
			}
			return nil
		},
	},
	{
		Name: "description",
		Prompt: &survey.Input{
			Message: "Enter a description of your event [optional]:",
			Help:    "One or two sentences to descript the event. Defaults to Devopsdays is coming to CITY!",
		},
	},
	{
		Name: "googleanalytics",
		Prompt: &survey.Input{
			Message: "Enter your Google Analytics ID [optional]:",
			Help:    "This will allow your page to be tracked. Example: UA-74738648-1.",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && (helpers.ValidateField(str, "googleanalytics") == false) {
				return errors.New("Please enter a valid Google Analytics ID. Example: UA-74738648-1")
			}
			return nil
		},
	},
	{
		Name: "startdate",
		Prompt: &survey.Input{
			Message: "Enter the event's start date in the format of YYYY-MM-YY [optional]:",
			Help:    "You can only provide a date for your event if you have a signed contract with your venue.",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && (helpers.ValidateField(str, "date") == false) {
				return errors.New("Please enter a valid date. Example: 2018-01-30.")
			}
			return nil
		},
	},
	{
		Name: "enddate",
		Prompt: &survey.Input{
			Message: "Enter the event's end date in the format of YYYY-MM-YY [optional]:",
			Help:    "For single-day events make the end date the same as the start date. You can only provide a date for your event if you have a signed contract with your venue.",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && (helpers.ValidateField(str, "date") == false) {
				return errors.New("Please enter a valid date. Example: 2018-01-30.")
			}
			return nil
		},
	},
	{
		Name: "coordinates",
		Prompt: &survey.Input{
			Message: "Enter the coordinates of your venue [optional]:",
			Help:    "Get Latitude and Longitude of a Point: http://itouchmap.com/latlong.html. Example: 41.882219, -87.640530",
		},
	},
	{
		Name: "location",
		Prompt: &survey.Input{
			Message: "Enter the name of your venue [optional]:",
			Help:    "If you do not enter a value here, it will default to your city name.",
		},
	},
	{
		Name: "locationaddress",
		Prompt: &survey.Input{
			Message: "Enter the address of your venue [optional]:",
			Help:    "Use the street address of your venue. This will show up on the welcome page if set.",
		},
	},
	{
		Name: "logopath",
		Prompt: &survey.Input{
			Message: "Enter the path to your event's logo, for use on your event's home page. [optional]",
			Help:    "Path to logo image. Must be a PNG file. Example: /Users/mattstratton/Pictures/chicago.png.",
		},
		Validate: func(val interface{}) error {
			str, _ := val.(string)
			if str != "" {
				if _, err := os.Stat(str); err != nil {
					return errors.New("File not found.")
				}
				ret, _ := regexp.MatchString(`[0-9a-z]+\.(png|PNG)`, str)
				if ret != true {
					return errors.New("Logo image must be a PNG file")
				}
			}

			return nil
		},
	},
	{
		Name: "squarelogopath",
		Prompt: &survey.Input{
			Message: "Enter the path to your event's square logo, for use on the main home page. [optional]",
			Help:    "Path to logo image. Must be a PNG file, 600 x 600 px. Example: /Users/mattstratton/Pictures/chicago-square.png.",
		},
		Validate: func(val interface{}) error {
			str, _ := val.(string)
			if str != "" {
				if _, err := os.Stat(str); err != nil {
					return errors.New("File not found.")
				}
				ret, _ := regexp.MatchString(`[0-9a-z]+\.(png|PNG)`, str)
				if ret != true {
					return errors.New("Logo image must be a PNG file")
				}
			}

			return nil
		},
	},
}

// CreateEvent takes input from the user to create a new event
func CreateEvent(city, year string) (err error) {

	answers := struct {
		Twitter         string
		Description     string
		GoogleAnalytics string
		StartDate       string
		EndDate         string
		Coordinates     string
		Location        string
		LocationAddress string
		LogoPath        string
		SquareLogoPath  string
	}{}

	if city == "" {
		prompt := &survey.Input{
			Message: "Enter the city name:",
		}
		cityErr := survey.AskOne(prompt, &city, survey.Required)
		if cityErr != nil {
			return
		}
	}

	if year == "" {
		prompt := &survey.Input{
			Message: "Enter the year:",
		}
		yearErr := survey.AskOne(prompt, &year, survey.Required)
		if yearErr != nil {
			return
		}
	}

	if CheckEvent(city, year) {
		fmt.Println("This event already exists. If you would like to edit it, please run `devopsdays-cli edit event`")
		return
	}

	surveyErr := survey.Ask(qsCreateEvent, &answers)
	if surveyErr != nil {
		fmt.Println(surveyErr.Error())
		return
	}

	orgEmail := []string{"organizers-", strings.Replace(strings.TrimSpace(strings.ToLower(CityClean(city))), " ", "-", 10), "-", strings.TrimSpace(year), "@devopsdays.org"}
	proposalEmail := []string{"proposals-", CityClean(city), "-", strings.TrimSpace(year), "@devopsdays.org"}
	myEvent := model.Event{
		Name:            strings.Join([]string{strings.TrimSpace(year), "-", CityClean(city)}, ""),
		Year:            year,
		City:            city,
		EventTwitter:    helpers.TwitterClean(answers.Twitter),
		Description:     answers.Description,
		GoogleAnalytics: answers.GoogleAnalytics,
		StartDate:       answers.StartDate,
		EndDate:         answers.EndDate,
		Coordinates:     answers.Coordinates,
		Location:        answers.Location,
		LocationAddress: answers.LocationAddress,
		OrganizerEmail:  strings.Join(orgEmail, ""),
		ProposalEmail:   strings.Join(proposalEmail, ""),
	}

	NewEvent(myEvent, CityClean(city), year)

	// create the event content files
	contentfiles := []string{"index", "conduct", "contact", "location", "program", "propose", "registration", "sponsor"}
	for _, contentFile := range contentfiles {

		if result, err := createEventContentFile(city, year, contentFile); err != nil {
			fmt.Printf("Error: %s\n", err)
		} else {
			fmt.Printf("Event content file created for %s!!!\n", result)
		}

	}
	if answers.LogoPath != "" {
		err = Logo(answers.LogoPath, CityClean(city), year)
	}

	if answers.SquareLogoPath != "" {
		err = LogoSquare(answers.SquareLogoPath, CityClean(city), year)
	}

	return
}

// NewEvent takes in a constructed Event type and generates the stuff
func NewEvent(event model.Event, city string, year string) (err error) {
	t := template.New("Event template")

	t, err = t.Parse(eventTmpl)
	if err != nil {
		log.Fatal("Parse: ", err)
		return
	}
	f, err := os.Create(paths.EventDataPath(paths.GetWebdir(), city, year))
	defer f.Close()
	t.Execute(f, event)

	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Fprintf(color.Output, "\n\n\nCreated event file for %s\n", color.GreenString(event.City))
		fmt.Fprintf(color.Output, "at %s\n\n\n", color.BlueString(paths.EventDataPath(paths.GetWebdir(), city, year)))
	}
	return
}

// Logo takes in a path to an event's main logo and copies/renames it to the proper destination
func Logo(srcPath, city, year string) (err error) {

	eventStaticPath, err := paths.EventStaticPath(city, year)
	if err != nil {
		log.Fatal(err)
	}
	err = helpers.CopyFile(srcPath, filepath.Join(eventStaticPath, "logo.png"))

	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil

}

// LogoSquare takes in a path the event's square logo, and crops/resizes it and copies it to the proper destination
func LogoSquare(srcPath, city, year string) (err error) {
	eventStaticPath, err := paths.EventStaticPath(city, year)
	if err != nil {
		log.Fatal(err)
	}
	destPath := filepath.Join(eventStaticPath, "logo-square.png")
	images.ResizeImage(srcPath, destPath, "png", 600, 600)

	// @todo update helpers.ResizeImage to return error code and do something with it here

	return nil
}
<commit_msg>Fix duplicate code issue in event.go<commit_after>// Package event provides the functions for creating, editing, and showing events
package event

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"text/template"

	helpers "github.com/devopsdays/devopsdays-cli/helpers"
	paths "github.com/devopsdays/devopsdays-cli/helpers/paths"
	"github.com/devopsdays/devopsdays-cli/images"
	"github.com/devopsdays/devopsdays-cli/model"
	"github.com/fatih/color"
	survey "gopkg.in/AlecAivazis/survey.v1"
)

// LogoPath is the fully qualified path to the event's logo
var LogoPath string

// SquareLogoPath is the fully qualified path to the event's square logo
var SquareLogoPath string

// the questions to ask
var qsCreateEvent = []*survey.Question{
	{
		Name: "twitter",
		Prompt: &survey.Input{
			Message: "Enter your devopsdays event twitter handle (defaults to @devopsdays):",
			Help:    "Twitter username can include the @ symbol or not. Examples: '@devopsdays' or 'devopsdays",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && !helpers.ValidateField(str, "twitter") {
				return errors.New("Please enter a valid Twitter handle. Spaces are not allowed.")
			}
			return nil
		},
	},
	{
		Name: "description",
		Prompt: &survey.Input{
			Message: "Enter a description of your event [optional]:",
			Help:    "One or two sentences to descript the event. Defaults to Devopsdays is coming to CITY!",
		},
	},
	{
		Name: "googleanalytics",
		Prompt: &survey.Input{
			Message: "Enter your Google Analytics ID [optional]:",
			Help:    "This will allow your page to be tracked. Example: UA-74738648-1.",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && (helpers.ValidateField(str, "googleanalytics") == false) {
				return errors.New("Please enter a valid Google Analytics ID. Example: UA-74738648-1")
			}
			return nil
		},
	},
	{
		Name: "startdate",
		Prompt: &survey.Input{
			Message: "Enter the event's start date in the format of YYYY-MM-YY [optional]:",
			Help:    "You can only provide a date for your event if you have a signed contract with your venue.",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && (helpers.ValidateField(str, "date") == false) {
				return errors.New("Please enter a valid date. Example: 2018-01-30.")
			}
			return nil
		},
	},
	{
		Name: "enddate",
		Prompt: &survey.Input{
			Message: "Enter the event's end date in the format of YYYY-MM-YY [optional]:",
			Help:    "For single-day events make the end date the same as the start date. You can only provide a date for your event if you have a signed contract with your venue.",
		},
		Validate: func(val interface{}) error {
			if str, _ := val.(string); (str != "") && (helpers.ValidateField(str, "date") == false) {
				return errors.New("Please enter a valid date. Example: 2018-01-30.")
			}
			return nil
		},
	},
	{
		Name: "coordinates",
		Prompt: &survey.Input{
			Message: "Enter the coordinates of your venue [optional]:",
			Help:    "Get Latitude and Longitude of a Point: http://itouchmap.com/latlong.html. Example: 41.882219, -87.640530",
		},
	},
	{
		Name: "location",
		Prompt: &survey.Input{
			Message: "Enter the name of your venue [optional]:",
			Help:    "If you do not enter a value here, it will default to your city name.",
		},
	},
	{
		Name: "locationaddress",
		Prompt: &survey.Input{
			Message: "Enter the address of your venue [optional]:",
			Help:    "Use the street address of your venue. This will show up on the welcome page if set.",
		},
	},
	{
		Name: "logopath",
		Prompt: &survey.Input{
			Message: "Enter the path to your event's logo, for use on your event's home page. [optional]",
			Help:    "Path to logo image. Must be a PNG file. Example: /Users/mattstratton/Pictures/chicago.png.",
		},
		Validate: func(val interface{}) error {
			str, _ := val.(string)
			if str != "" {
				err := checkPNG(str)
				if err != nil {
					return err
				}
			}

			return nil
		},
	},
	{
		Name: "squarelogopath",
		Prompt: &survey.Input{
			Message: "Enter the path to your event's square logo, for use on the main home page. [optional]",
			Help:    "Path to logo image. Must be a PNG file, 600 x 600 px. Example: /Users/mattstratton/Pictures/chicago-square.png.",
		},
		Validate: func(val interface{}) error {
			str, _ := val.(string)
			if str != "" {
				err := checkPNG(str)
				if err != nil {
					return err
				}
			}

			return nil
		},
	},
}

// checkPNG ensures that an image file exists and is a PNG
func checkPNG(imagePath string) error {
	if _, err := os.Stat(imagePath); err != nil {
		return errors.New("File not found.")
	}
	ret, _ := regexp.MatchString(`[0-9a-z]+\.(png|PNG)`, imagePath)
	if ret != true {
		return errors.New("Logo image must be a PNG file")
	}

	return nil
}

// CreateEvent takes input from the user to create a new event
func CreateEvent(city, year string) (err error) {

	answers := struct {
		Twitter         string
		Description     string
		GoogleAnalytics string
		StartDate       string
		EndDate         string
		Coordinates     string
		Location        string
		LocationAddress string
		LogoPath        string
		SquareLogoPath  string
	}{}

	if city == "" {
		prompt := &survey.Input{
			Message: "Enter the city name:",
		}
		cityErr := survey.AskOne(prompt, &city, survey.Required)
		if cityErr != nil {
			return
		}
	}

	if year == "" {
		prompt := &survey.Input{
			Message: "Enter the year:",
		}
		yearErr := survey.AskOne(prompt, &year, survey.Required)
		if yearErr != nil {
			return
		}
	}

	if CheckEvent(city, year) {
		fmt.Println("This event already exists. If you would like to edit it, please run `devopsdays-cli edit event`")
		return
	}

	surveyErr := survey.Ask(qsCreateEvent, &answers)
	if surveyErr != nil {
		fmt.Println(surveyErr.Error())
		return
	}

	orgEmail := []string{"organizers-", strings.Replace(strings.TrimSpace(strings.ToLower(CityClean(city))), " ", "-", 10), "-", strings.TrimSpace(year), "@devopsdays.org"}
	proposalEmail := []string{"proposals-", CityClean(city), "-", strings.TrimSpace(year), "@devopsdays.org"}
	myEvent := model.Event{
		Name:            strings.Join([]string{strings.TrimSpace(year), "-", CityClean(city)}, ""),
		Year:            year,
		City:            city,
		EventTwitter:    helpers.TwitterClean(answers.Twitter),
		Description:     answers.Description,
		GoogleAnalytics: answers.GoogleAnalytics,
		StartDate:       answers.StartDate,
		EndDate:         answers.EndDate,
		Coordinates:     answers.Coordinates,
		Location:        answers.Location,
		LocationAddress: answers.LocationAddress,
		OrganizerEmail:  strings.Join(orgEmail, ""),
		ProposalEmail:   strings.Join(proposalEmail, ""),
	}

	NewEvent(myEvent, CityClean(city), year)

	// create the event content files
	contentfiles := []string{"index", "conduct", "contact", "location", "program", "propose", "registration", "sponsor"}
	for _, contentFile := range contentfiles {

		if result, err := createEventContentFile(city, year, contentFile); err != nil {
			fmt.Printf("Error: %s\n", err)
		} else {
			fmt.Printf("Event content file created for %s!!!\n", result)
		}

	}
	if answers.LogoPath != "" {
		err = Logo(answers.LogoPath, CityClean(city), year)
	}

	if answers.SquareLogoPath != "" {
		err = LogoSquare(answers.SquareLogoPath, CityClean(city), year)
	}

	return
}

// NewEvent takes in a constructed Event type and generates the stuff
func NewEvent(event model.Event, city string, year string) (err error) {
	t := template.New("Event template")

	t, err = t.Parse(eventTmpl)
	if err != nil {
		log.Fatal("Parse: ", err)
		return
	}
	f, err := os.Create(paths.EventDataPath(paths.GetWebdir(), city, year))
	defer f.Close()
	t.Execute(f, event)

	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Fprintf(color.Output, "\n\n\nCreated event file for %s\n", color.GreenString(event.City))
		fmt.Fprintf(color.Output, "at %s\n\n\n", color.BlueString(paths.EventDataPath(paths.GetWebdir(), city, year)))
	}
	return
}

// Logo takes in a path to an event's main logo and copies/renames it to the proper destination
func Logo(srcPath, city, year string) (err error) {

	eventStaticPath, err := paths.EventStaticPath(city, year)
	if err != nil {
		log.Fatal(err)
	}
	err = helpers.CopyFile(srcPath, filepath.Join(eventStaticPath, "logo.png"))

	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil

}

// LogoSquare takes in a path the event's square logo, and crops/resizes it and copies it to the proper destination
func LogoSquare(srcPath, city, year string) (err error) {
	eventStaticPath, err := paths.EventStaticPath(city, year)
	if err != nil {
		log.Fatal(err)
	}
	destPath := filepath.Join(eventStaticPath, "logo-square.png")
	images.ResizeImage(srcPath, destPath, "png", 600, 600)

	// @todo update helpers.ResizeImage to return error code and do something with it here

	return nil
}
<|endoftext|>


<commit_before>package bites

import "bytes"

// Bites' purpose is to give byte slices some useful methods.
// The Put methods append things, and return the updated slice.
// The Get methods snip things off the front, and return the remainder of the slice.
type Bites []byte

const extendShortLen = 512

var extendShort [extendShortLen]byte

// Returns an empty slice.
func Empty() Bites {
	return []byte{}
}

// Make a string out of the slice.
// This unavoidably allocates.
func (b Bites) String() string {
	return string(b)
}

// Returns b with at least s capacity left.
func (b Bites) Capacity(s int) Bites {
	if len(b)+s <= cap(b) {
		return b
	}
	orig := len(b)
	b = b.Extend(s)
	return b[:orig]
}

// Extend b by s, return the complete, extended, slice. The extension may contain garbage.
// An allocation (new backing array) will occur if s is larger than cap-len.
func (b Bites) Extend(s int) Bites {
	l := len(b)
	if l+s <= cap(b) {
		b = b[:l+s]
	} else {
		ob := b
		b = make([]byte, len(b)+s)
		copy(b, ob)
	}
	return b
}

// Set length to 0.
func (b Bites) Reuse() Bites {
	return b[:0]
}

// Make an exact copy of b and return it.
// This will allocate.
func (b Bites) Clone() Bites {
	clone := make(Bites, len(b), len(b))
	copy(clone, b)
	return clone
}

// Return a slice containing the last s bytes.
func (b Bites) Last(s int) Bites {
	return b[len(b)-s:]
}

// Return a slice without the first s bytes.
func (b Bites) Skip(s int) Bites {
	return b[s:]
}

// Return a slice with the last s bytes snipped off.
func (b Bites) Snip(s int) Bites {
	return b[:len(b)-s]
}

// Split the slice into the first s bytes and the rest.
func (b Bites) Split(s int) (Bites, Bites) {
	return b[:s], b[s:]
}

// Set all bytes to s.
func (b Bites) Set(s byte) Bites {
	for i := range b {
		b[i] = s
	}
	return b
}

// Set all bytes to 0.
// This is much faster than Set(0).
func (b Bites) Zero() Bites {
	x := b
	for len(x) > 0 {
		x = x[copy(x, extendShort[:]):]
	}
	return b
}

// True if both slices are exactly equal
func (b Bites) Equal(c Bites) bool {
	return bytes.Compare(b, c) == 0
}

// True if the slice is equal to the given string
// TODO: Make sure this doesn't allocate.
func (b Bites) Sequal(str string) bool {
	return b.Equal(Bites(str))
}
<commit_msg>Minor doc update<commit_after>package bites

import "bytes"

// Bites' purpose is to give byte slices some useful methods.
// The Put methods append things, and return the updated slice.
// The Get methods snip things off the front, and return the remainder of the slice.
// The int methods are big-endian by default, but they have Little-Endian versions too.
// The float and complex methods put them in the form of IEE754 binary representation.
type Bites []byte

const extendShortLen = 512

var extendShort [extendShortLen]byte

// Returns an empty slice.
func Empty() Bites {
	return []byte{}
}

// Make a string out of the slice.
// This unavoidably allocates.
func (b Bites) String() string {
	return string(b)
}

// Returns b with at least s capacity left.
func (b Bites) Capacity(s int) Bites {
	if len(b)+s <= cap(b) {
		return b
	}
	orig := len(b)
	b = b.Extend(s)
	return b[:orig]
}

// Extend b by s, return the complete, extended, slice. The extension may contain garbage.
// An allocation (new backing array) will occur if s is larger than cap-len.
func (b Bites) Extend(s int) Bites {
	l := len(b)
	if l+s <= cap(b) {
		b = b[:l+s]
	} else {
		ob := b
		b = make([]byte, len(b)+s)
		copy(b, ob)
	}
	return b
}

// Set length to 0.
func (b Bites) Reuse() Bites {
	return b[:0]
}

// Make an exact copy of b and return it.
// This will allocate.
func (b Bites) Clone() Bites {
	clone := make(Bites, len(b), len(b))
	copy(clone, b)
	return clone
}

// Return a slice containing the last s bytes.
func (b Bites) Last(s int) Bites {
	return b[len(b)-s:]
}

// Return a slice without the first s bytes.
func (b Bites) Skip(s int) Bites {
	return b[s:]
}

// Return a slice with the last s bytes snipped off.
func (b Bites) Snip(s int) Bites {
	return b[:len(b)-s]
}

// Split the slice into the first s bytes and the rest.
func (b Bites) Split(s int) (Bites, Bites) {
	return b[:s], b[s:]
}

// Set all bytes to s.
func (b Bites) Set(s byte) Bites {
	for i := range b {
		b[i] = s
	}
	return b
}

// Set all bytes to 0.
// This is much faster than Set(0).
func (b Bites) Zero() Bites {
	x := b
	for len(x) > 0 {
		x = x[copy(x, extendShort[:]):]
	}
	return b
}

// True if both slices are exactly equal
func (b Bites) Equal(c Bites) bool {
	return bytes.Compare(b, c) == 0
}

// True if the slice is equal to the given string
// TODO: Make sure this doesn't allocate.
func (b Bites) Sequal(str string) bool {
	return b.Equal(Bites(str))
}
<|endoftext|>