<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to state tracking.  If enabled, state
// tracking will capture the initial READY packet and many other websocket
// events and maintain an in-memory state of of guilds, channels, users, and
// so forth.  This information can be accessed through the Session.State struct.

package discordgo

import "errors"

// ErrNilState is returned when the state is nil.
var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.")

// NewState creates an empty state.
func NewState() *State {
	return &State{
		Ready: Ready{
			PrivateChannels: []*Channel{},
			Guilds:          []*Guild{},
		},
		guildMap:   make(map[string]*Guild),
		channelMap: make(map[string]*Channel),
	}
}

// OnReady takes a Ready event and updates all internal state.
func (s *State) OnReady(r *Ready) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	s.Ready = *r

	for _, g := range s.Guilds {
		for _, c := range g.Channels {
			c.GuildID = g.ID
		}

		s.guildMap[g.ID] = g
	}

	return nil
}

// GuildAdd adds a guild to the current world state, or
// updates it if it already exists.
func (s *State) GuildAdd(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	// Otherwise, update the channels to point to the right guild
	for _, c := range guild.Channels {
		c.GuildID = guild.ID
	}

	if g, err := s.Guild(guild.ID); err == nil {
		s.Lock()
		defer s.Unlock()

		if !g.Unavailable {
			guild.Members = g.Members
			guild.Presences = g.Presences
			guild.Channels = g.Channels
			guild.VoiceStates = g.VoiceStates
		}
		*g = *guild
		return nil
	}

	s.Lock()
	defer s.Unlock()

	s.Guilds = append(s.Guilds, guild)
	s.guildMap[guild.ID] = guild

	return nil
}

// GuildRemove removes a guild from current world state.
func (s *State) GuildRemove(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	_, err := s.Guild(guild.ID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...)
			return nil
		}
	}

	delete(s.guildMap, guild.ID)

	return nil
}

// Guild gets a guild by ID.
// Useful for querying if @me is in a guild:
//     _, err := discordgo.Session.State.Guild(guildID)
//     isInGuild := err == nil
func (s *State) Guild(guildID string) (*Guild, error) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	if g, ok := s.guildMap[guildID]; ok {
		return g, nil
	}

	return nil, errors.New("Guild not found.")
}

// TODO: Consider moving Guild state update methods onto *Guild.

// MemberAdd adds a member to the current world state, or
// updates it if it already exists.
func (s *State) MemberAdd(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members[i] = member
			return nil
		}
	}

	guild.Members = append(guild.Members, member)
	return nil
}

// MemberRemove removes a member from current world state.
func (s *State) MemberRemove(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
			return nil
		}
	}

	return errors.New("Member not found.")
}

// Member gets a member by ID from a guild.
func (s *State) Member(guildID, userID string) (*Member, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range guild.Members {
		if m.User.ID == userID {
			return m, nil
		}
	}

	return nil, errors.New("Member not found.")
}

// ChannelAdd adds a guild to the current world state, or
// updates it if it already exists.
// Channels may exist either as PrivateChannels or inside
// a guild.
func (s *State) ChannelAdd(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	if c, err := s.Channel(channel.ID); err == nil {
		s.Lock()
		defer s.Unlock()

		channel.Messages = c.Messages
		channel.PermissionOverwrites = c.PermissionOverwrites

		*c = *channel

		return nil
	}

	s.Lock()
	defer s.Unlock()

	if channel.IsPrivate {
		s.PrivateChannels = append(s.PrivateChannels, channel)
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		guild.Channels = append(guild.Channels, channel)
	}

	s.channelMap[channel.ID] = channel

	return nil
}

// ChannelRemove removes a channel from current world state.
func (s *State) ChannelRemove(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	_, err := s.Channel(channel.ID)
	if err != nil {
		return err
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()

		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...)
				return nil
			}
		}
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		s.Lock()
		defer s.Unlock()

		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
				return nil
			}
		}
	}

	delete(s.channelMap, channel.ID)

	return nil
}

// GuildChannel gets a channel by ID from a guild.
// This method is Deprecated, use Channel(channelID)
func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
	return s.Channel(channelID)
}

// PrivateChannel gets a private channel by ID.
// This method is Deprecated, use Channel(channelID)
func (s *State) PrivateChannel(channelID string) (*Channel, error) {
	return s.Channel(channelID)
}

// Channel gets a channel by ID, it will look in all guilds an private channels.
func (s *State) Channel(channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	if c, ok := s.channelMap[channelID]; ok {
		return c, nil
	}

	return nil, errors.New("Channel not found.")
}

// Emoji returns an emoji for a guild and emoji id.
func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, e := range guild.Emojis {
		if e.ID == emojiID {
			return e, nil
		}
	}

	return nil, errors.New("Emoji not found.")
}

// EmojiAdd adds an emoji to the current world state.
func (s *State) EmojiAdd(guildID string, emoji *Emoji) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, e := range guild.Emojis {
		if e.ID == emoji.ID {
			guild.Emojis[i] = emoji
			return nil
		}
	}

	guild.Emojis = append(guild.Emojis, emoji)
	return nil
}

// EmojisAdd adds multiple emojis to the world state.
func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error {
	for _, e := range emojis {
		if err := s.EmojiAdd(guildID, e); err != nil {
			return err
		}
	}
	return nil
}

// MessageAdd adds a message to the current world state, or updates it if it exists.
// If the channel cannot be found, the message is discarded.
// Messages are kept in state up to s.MaxMessageCount
func (s *State) MessageAdd(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	if s.MaxMessageCount == 0 {
		return nil
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// If the message exists, replace it.
	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages[i] = message
			return nil
		}
	}

	c.Messages = append(c.Messages, message)

	if len(c.Messages) > s.MaxMessageCount {
		c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
	}
	return nil
}

// MessageRemove removes a message from the world state.
func (s *State) MessageRemove(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	if s.MaxMessageCount == 0 {
		return nil
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
			return nil
		}
	}

	return errors.New("Message not found.")
}

func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error {
	guild, err := s.Guild(update.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// Handle Leaving Channel
	if update.ChannelID == "" {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...)
				return nil
			}
		}
	} else {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates[i] = update.VoiceState
				return nil
			}
		}

		guild.VoiceStates = append(guild.VoiceStates, update.VoiceState)
	}

	return nil
}

// Message gets a message by channel and message ID.
func (s *State) Message(channelID, messageID string) (*Message, error) {
	if s == nil {
		return nil, ErrNilState
	}

	c, err := s.Channel(channelID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range c.Messages {
		if m.ID == messageID {
			return m, nil
		}
	}

	return nil, errors.New("Message not found.")
}

// onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) (err error) {
	if s == nil {
		return ErrNilState
	}
	if !se.StateEnabled {
		return nil
	}

	switch t := i.(type) {
	case *Ready:
		err = s.OnReady(t)
	case *GuildCreate:
		err = s.GuildAdd(t.Guild)
	case *GuildUpdate:
		err = s.GuildAdd(t.Guild)
	case *GuildDelete:
		err = s.GuildRemove(t.Guild)
	case *GuildMemberAdd:
		err = s.MemberAdd(t.Member)
	case *GuildMemberUpdate:
		err = s.MemberAdd(t.Member)
	case *GuildMemberRemove:
		err = s.MemberRemove(t.Member)
	case *GuildEmojisUpdate:
		err = s.EmojisAdd(t.GuildID, t.Emojis)
	case *ChannelCreate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelUpdate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelDelete:
		err = s.ChannelRemove(t.Channel)
	case *MessageCreate:
		err = s.MessageAdd(t.Message)
	case *MessageUpdate:
		err = s.MessageAdd(t.Message)
	case *MessageDelete:
		err = s.MessageRemove(t.Message)
	case *VoiceStateUpdate:
		err = s.voiceStateUpdate(t)
	}

	return
}
<commit_msg>Update some comments.<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to state tracking.  If enabled, state
// tracking will capture the initial READY packet and many other websocket
// events and maintain an in-memory state of of guilds, channels, users, and
// so forth.  This information can be accessed through the Session.State struct.

package discordgo

import "errors"

// ErrNilState is returned when the state is nil.
var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.")

// NewState creates an empty state.
func NewState() *State {
	return &State{
		Ready: Ready{
			PrivateChannels: []*Channel{},
			Guilds:          []*Guild{},
		},
		guildMap:   make(map[string]*Guild),
		channelMap: make(map[string]*Channel),
	}
}

// OnReady takes a Ready event and updates all internal state.
func (s *State) OnReady(r *Ready) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	s.Ready = *r

	for _, g := range s.Guilds {
		for _, c := range g.Channels {
			c.GuildID = g.ID
		}

		s.guildMap[g.ID] = g
	}

	return nil
}

// GuildAdd adds a guild to the current world state, or
// updates it if it already exists.
func (s *State) GuildAdd(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	// Update the channels to point to the right guild
	for _, c := range guild.Channels {
		c.GuildID = guild.ID
	}

	// If the guild exists, replace it.
	if g, err := s.Guild(guild.ID); err == nil {
		s.Lock()
		defer s.Unlock()

		if !g.Unavailable {
			guild.Members = g.Members
			guild.Presences = g.Presences
			guild.Channels = g.Channels
			guild.VoiceStates = g.VoiceStates
		}
		*g = *guild
		return nil
	}

	s.Lock()
	defer s.Unlock()

	s.Guilds = append(s.Guilds, guild)
	s.guildMap[guild.ID] = guild

	return nil
}

// GuildRemove removes a guild from current world state.
func (s *State) GuildRemove(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	_, err := s.Guild(guild.ID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...)
			return nil
		}
	}

	delete(s.guildMap, guild.ID)

	return nil
}

// Guild gets a guild by ID.
// Useful for querying if @me is in a guild:
//     _, err := discordgo.Session.State.Guild(guildID)
//     isInGuild := err == nil
func (s *State) Guild(guildID string) (*Guild, error) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	if g, ok := s.guildMap[guildID]; ok {
		return g, nil
	}

	return nil, errors.New("Guild not found.")
}

// TODO: Consider moving Guild state update methods onto *Guild.

// MemberAdd adds a member to the current world state, or
// updates it if it already exists.
func (s *State) MemberAdd(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members[i] = member
			return nil
		}
	}

	guild.Members = append(guild.Members, member)
	return nil
}

// MemberRemove removes a member from current world state.
func (s *State) MemberRemove(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
			return nil
		}
	}

	return errors.New("Member not found.")
}

// Member gets a member by ID from a guild.
func (s *State) Member(guildID, userID string) (*Member, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range guild.Members {
		if m.User.ID == userID {
			return m, nil
		}
	}

	return nil, errors.New("Member not found.")
}

// ChannelAdd adds a guild to the current world state, or
// updates it if it already exists.
// Channels may exist either as PrivateChannels or inside
// a guild.
func (s *State) ChannelAdd(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	// If the channel exists, replace it.
	if c, err := s.Channel(channel.ID); err == nil {
		s.Lock()
		defer s.Unlock()

		channel.Messages = c.Messages
		channel.PermissionOverwrites = c.PermissionOverwrites

		*c = *channel

		return nil
	}

	s.Lock()
	defer s.Unlock()

	if channel.IsPrivate {
		s.PrivateChannels = append(s.PrivateChannels, channel)
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		guild.Channels = append(guild.Channels, channel)
	}

	s.channelMap[channel.ID] = channel

	return nil
}

// ChannelRemove removes a channel from current world state.
func (s *State) ChannelRemove(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	_, err := s.Channel(channel.ID)
	if err != nil {
		return err
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()

		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...)
				return nil
			}
		}
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		s.Lock()
		defer s.Unlock()

		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
				return nil
			}
		}
	}

	delete(s.channelMap, channel.ID)

	return nil
}

// GuildChannel gets a channel by ID from a guild.
// This method is Deprecated, use Channel(channelID)
func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
	return s.Channel(channelID)
}

// PrivateChannel gets a private channel by ID.
// This method is Deprecated, use Channel(channelID)
func (s *State) PrivateChannel(channelID string) (*Channel, error) {
	return s.Channel(channelID)
}

// Channel gets a channel by ID, it will look in all guilds an private channels.
func (s *State) Channel(channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	if c, ok := s.channelMap[channelID]; ok {
		return c, nil
	}

	return nil, errors.New("Channel not found.")
}

// Emoji returns an emoji for a guild and emoji id.
func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, e := range guild.Emojis {
		if e.ID == emojiID {
			return e, nil
		}
	}

	return nil, errors.New("Emoji not found.")
}

// EmojiAdd adds an emoji to the current world state.
func (s *State) EmojiAdd(guildID string, emoji *Emoji) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, e := range guild.Emojis {
		if e.ID == emoji.ID {
			guild.Emojis[i] = emoji
			return nil
		}
	}

	guild.Emojis = append(guild.Emojis, emoji)
	return nil
}

// EmojisAdd adds multiple emojis to the world state.
func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error {
	for _, e := range emojis {
		if err := s.EmojiAdd(guildID, e); err != nil {
			return err
		}
	}
	return nil
}

// MessageAdd adds a message to the current world state, or updates it if it exists.
// If the channel cannot be found, the message is discarded.
// Messages are kept in state up to s.MaxMessageCount
func (s *State) MessageAdd(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	if s.MaxMessageCount == 0 {
		return nil
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// If the message exists, replace it.
	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages[i] = message
			return nil
		}
	}

	c.Messages = append(c.Messages, message)

	if len(c.Messages) > s.MaxMessageCount {
		c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
	}
	return nil
}

// MessageRemove removes a message from the world state.
func (s *State) MessageRemove(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	if s.MaxMessageCount == 0 {
		return nil
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
			return nil
		}
	}

	return errors.New("Message not found.")
}

func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error {
	guild, err := s.Guild(update.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// Handle Leaving Channel
	if update.ChannelID == "" {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...)
				return nil
			}
		}
	} else {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates[i] = update.VoiceState
				return nil
			}
		}

		guild.VoiceStates = append(guild.VoiceStates, update.VoiceState)
	}

	return nil
}

// Message gets a message by channel and message ID.
func (s *State) Message(channelID, messageID string) (*Message, error) {
	if s == nil {
		return nil, ErrNilState
	}

	c, err := s.Channel(channelID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range c.Messages {
		if m.ID == messageID {
			return m, nil
		}
	}

	return nil, errors.New("Message not found.")
}

// onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) (err error) {
	if s == nil {
		return ErrNilState
	}
	if !se.StateEnabled {
		return nil
	}

	switch t := i.(type) {
	case *Ready:
		err = s.OnReady(t)
	case *GuildCreate:
		err = s.GuildAdd(t.Guild)
	case *GuildUpdate:
		err = s.GuildAdd(t.Guild)
	case *GuildDelete:
		err = s.GuildRemove(t.Guild)
	case *GuildMemberAdd:
		err = s.MemberAdd(t.Member)
	case *GuildMemberUpdate:
		err = s.MemberAdd(t.Member)
	case *GuildMemberRemove:
		err = s.MemberRemove(t.Member)
	case *GuildEmojisUpdate:
		err = s.EmojisAdd(t.GuildID, t.Emojis)
	case *ChannelCreate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelUpdate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelDelete:
		err = s.ChannelRemove(t.Channel)
	case *MessageCreate:
		err = s.MessageAdd(t.Message)
	case *MessageUpdate:
		err = s.MessageAdd(t.Message)
	case *MessageDelete:
		err = s.MessageRemove(t.Message)
	case *VoiceStateUpdate:
		err = s.voiceStateUpdate(t)
	}

	return
}
<|endoftext|>


<commit_before>package boardgame

import (
	"encoding/json"
)

//State represents the entire semantic state of a game at a given version. For
//your specific game, Game and Players will actually be concrete structs to
//your particular game. Games often define a top-level concreteStates()
//*myGameState, []*myPlayerState so at the top of methods that accept a State
//they can quickly get concrete, type-checked types with only a single
//conversion leap of faith at the top. States are intended to be read-only;
//methods where you are allowed to mutate the state (e.g. Move.Apply()) will
//take a MutableState instead as a signal that it is permissable to modify the
//state. That is why the states only return non-mutable states
//(PropertyReaders, not PropertyReadSetters, although realistically it is
//possible to cast them and modify directly. The MarshalJSON output of a State
//is appropriate for sending to a client or serializing a state to be put in
//storage. Given a blob serialized in that fashion, GameManager.StateFromBlob
//will return a state.
type State interface {
	//Game returns the GameState for this State
	Game() GameState
	//Players returns a slice of all PlayerStates for this State
	Players() []PlayerState
	//DynamicComponentValues returns a map of deck name to array of component
	//values, one per component in that deck.
	DynamicComponentValues() map[string][]DynamicComponentValues
	//Copy returns a deep copy of the State, including copied version of the Game
	//and Player States.
	Copy(sanitized bool) State
	//Diagram returns a basic, ascii rendering of the state for debug rendering.
	//It thunks out to Delegate.Diagram.
	Diagram() string
	//Santizied will return false if this is a full-fidelity State object, or
	//true if it has been sanitized, which means that some properties might be
	//hidden or otherwise altered. This should return true if the object was
	//created with Copy(true)
	Sanitized() bool
	//Computed returns the computed properties for this state.
	Computed() ComputedProperties
	//SanitizedForPlayer produces a copy state object that has been sanitized for
	//the player at the given index. The state object returned will have
	//Sanitized() return true. Will call GameDelegate.StateSanitizationPolicy to
	//retrieve the policy in place. See the package level comment for an overview
	//of how state sanitization works.
	SanitizedForPlayer(player PlayerIndex) State

	//StorageRecord returns a StateStorageRecord representing the state.
	StorageRecord() StateStorageRecord
}

//PlayerIndex is an int that represents the index of a given player in a game.
//Normal values are [0, game.NumPlayers). Special values are AdminPlayerIndex
//and ObserverPlayerIndex.
type PlayerIndex int

//ObserverPlayerIndex is a special PlayerIndex that denotes that the player in
//question is not one of the normal players, but someone generically watching.
//All hidden state should be hidden to them, and GroupSelf will never trigger
//for them.
const ObserverPlayerIndex PlayerIndex = -1

//AdminPlayerINdex is a special PlayerIndex that denotes the omniscient admin
//who can see all state and make moves whenever they want. This PlayerIndex
//should only be used in rare or debug circumstances.
const AdminPlayerIndex PlayerIndex = -2

//A MutableState is a state that is designed to be modified in place. These
//are passed to methods (instead of normal States) as a signal that
//modifications are intended to be done on the state.
type MutableState interface {
	//MutableState contains all of the methods of a read-only state.
	State
	//MutableGame is a reference to the MutableGameState for this MutableState.
	MutableGame() MutableGameState
	//MutablePlayers returns a slice of MutablePlayerStates for this MutableState.
	MutablePlayers() []MutablePlayerState

	MutableDynamicComponentValues() map[string][]MutableDynamicComponentValues
}

//state implements both State and MutableState, so it can always be passed for
//either, and what it's interpreted as is primarily a function of what the
//method signature is that it's passed to
type state struct {
	gameState              MutableGameState
	playerStates           []MutablePlayerState
	computed               *computedPropertiesImpl
	dynamicComponentValues map[string][]MutableDynamicComponentValues
	sanitized              bool
	game                   *Game
	//Set to true while computed is being calculating computed. Primarily so
	//if you marshal JSON in that time we know to just elide computed.
	calculatingComputed bool
	//If TimerProp.Start() is called, it prepares a timer, but doesn't
	//actually start ticking it until this state is committed. This is where
	//we accumulate the timers that still need to be fully started at that
	//point.
	timersToStart []int
}

func (s *state) MutableGame() MutableGameState {
	return s.gameState
}

func (s *state) MutablePlayers() []MutablePlayerState {
	return s.playerStates
}

func (s *state) MutableDynamicComponentValues() map[string][]MutableDynamicComponentValues {
	return s.dynamicComponentValues
}

func (s *state) Game() GameState {
	return s.gameState
}

func (s *state) Players() []PlayerState {
	result := make([]PlayerState, len(s.playerStates))
	for i := 0; i < len(s.playerStates); i++ {
		result[i] = s.playerStates[i]
	}
	return result
}

func (s *state) Copy(sanitized bool) State {
	return s.copy(sanitized)
}

func (s *state) copy(sanitized bool) *state {
	players := make([]MutablePlayerState, len(s.playerStates))

	for i, player := range s.playerStates {
		players[i] = player.MutableCopy()
	}

	result := &state{
		gameState:              s.gameState.MutableCopy(),
		playerStates:           players,
		dynamicComponentValues: make(map[string][]MutableDynamicComponentValues),
		sanitized:              sanitized,
		game:                   s.game,
		//We copy this over, because this should only be set when computed is
		//being calculated, and during that time we'll be creating sanitized
		//copies of ourselves. However, if there are other copies created when
		//this flag is set that outlive the original flag being unset, that
		//state would be in a bad state long term...
		calculatingComputed: s.calculatingComputed,
	}

	for deckName, values := range s.dynamicComponentValues {
		arr := make([]MutableDynamicComponentValues, len(values))
		for i := 0; i < len(values); i++ {
			arr[i] = values[i].Copy()
			if err := verifyReaderObjects(arr[i].Reader(), result); err != nil {
				return nil
			}
		}
		result.dynamicComponentValues[deckName] = arr
	}

	//FixUp stacks to make sure they point to this new state.
	if err := verifyReaderObjects(result.gameState.Reader(), result); err != nil {
		return nil
	}
	for _, player := range result.playerStates {
		if err := verifyReaderObjects(player.Reader(), result); err != nil {
			return nil
		}
	}

	return result
}

//committed is called right after the state has been committed to the database
//and we're sure it will stick. This is the time to do any actions that were
//triggered during the state manipulation. currently that is only timers.
func (s *state) committed() {
	for _, id := range s.timersToStart {
		s.game.manager.timers.StartTimer(id)
	}
}

func (s *state) StorageRecord() StateStorageRecord {
	record, _ := DefaultMarshalJSON(s)
	return record
}

func (s *state) MarshalJSON() ([]byte, error) {

	obj := map[string]interface{}{
		"Game":     s.gameState,
		"Players":  s.playerStates,
		"Computed": s.Computed(),
	}

	dynamic := s.DynamicComponentValues()

	if dynamic != nil && len(dynamic) != 0 {
		obj["Components"] = dynamic
	} else {
		obj["Components"] = map[string]interface{}{}
	}

	return json.Marshal(obj)
}

func (s *state) Diagram() string {
	return s.game.manager.delegate.Diagram(s)
}

func (s *state) Sanitized() bool {
	return s.sanitized
}

func (s *state) DynamicComponentValues() map[string][]DynamicComponentValues {

	result := make(map[string][]DynamicComponentValues)

	for key, val := range s.dynamicComponentValues {
		slice := make([]DynamicComponentValues, len(val))
		for i := 0; i < len(slice); i++ {
			slice[i] = val[i]
		}
		result[key] = slice
	}

	return result
}

func (s *state) Computed() ComputedProperties {

	if s.calculatingComputed {
		//This might be called in a Compute() callback either directly, or
		//implicitly via MarshalJSON.
		return nil
	}

	if s.computed == nil {

		s.calculatingComputed = true
		s.computed = newComputedPropertiesImpl(s.game.manager.delegate.ComputedPropertiesConfig(), s)
		s.calculatingComputed = false
	}
	return s.computed
}

//SanitizedForPlayer is in sanitized.go

//BaseState is the interface that all state objects--PlayerStates and GameStates
//--implement.
type BaseState interface {
	Reader() PropertyReader
}

//MutableBaseState is the interface that Mutable{Game,Player}State's
//implement.
type MutableBaseState interface {
	ReadSetter() PropertyReadSetter
}

//PlayerState represents the state of a game associated with a specific user.
type PlayerState interface {
	//PlayerIndex encodes the index this user's state is in the containing
	//state object.
	PlayerIndex() int
	//Copy produces a copy of our current state. Be sure it's a deep copy that
	//makes a copy of any pointer arguments.
	Copy() PlayerState
	BaseState
}

//A MutablePlayerState is a PlayerState that is allowed to be mutated.
type MutablePlayerState interface {
	PlayerState
	MutableCopy() MutablePlayerState
	MutableBaseState
}

//GameState represents the state of a game that is not associated with a
//particular user. For example, the draw stack of cards, who the current
//player is, and other properites.
type GameState interface {
	//Copy returns a copy of our current state. Be sure it's a deep copy that
	//makes a copy of any pointer arguments.
	Copy() GameState
	BaseState
}

//A MutableGameState is a GameState that is allowed to be mutated.
type MutableGameState interface {
	GameState
	MutableCopy() MutableGameState
	MutableBaseState
}

//DefaultMarshalJSON is a simple wrapper around json.MarshalIndent, with the
//right defaults set. If your structs need to implement MarshaLJSON to output
//JSON, use this to encode it.
func DefaultMarshalJSON(obj interface{}) ([]byte, error) {
	return json.MarshalIndent(obj, "", "  ")
}
<commit_msg>Define PlayerIndex.Valid(state State) bool. part of #236.<commit_after>package boardgame

import (
	"encoding/json"
)

//State represents the entire semantic state of a game at a given version. For
//your specific game, Game and Players will actually be concrete structs to
//your particular game. Games often define a top-level concreteStates()
//*myGameState, []*myPlayerState so at the top of methods that accept a State
//they can quickly get concrete, type-checked types with only a single
//conversion leap of faith at the top. States are intended to be read-only;
//methods where you are allowed to mutate the state (e.g. Move.Apply()) will
//take a MutableState instead as a signal that it is permissable to modify the
//state. That is why the states only return non-mutable states
//(PropertyReaders, not PropertyReadSetters, although realistically it is
//possible to cast them and modify directly. The MarshalJSON output of a State
//is appropriate for sending to a client or serializing a state to be put in
//storage. Given a blob serialized in that fashion, GameManager.StateFromBlob
//will return a state.
type State interface {
	//Game returns the GameState for this State
	Game() GameState
	//Players returns a slice of all PlayerStates for this State
	Players() []PlayerState
	//DynamicComponentValues returns a map of deck name to array of component
	//values, one per component in that deck.
	DynamicComponentValues() map[string][]DynamicComponentValues
	//Copy returns a deep copy of the State, including copied version of the Game
	//and Player States.
	Copy(sanitized bool) State
	//Diagram returns a basic, ascii rendering of the state for debug rendering.
	//It thunks out to Delegate.Diagram.
	Diagram() string
	//Santizied will return false if this is a full-fidelity State object, or
	//true if it has been sanitized, which means that some properties might be
	//hidden or otherwise altered. This should return true if the object was
	//created with Copy(true)
	Sanitized() bool
	//Computed returns the computed properties for this state.
	Computed() ComputedProperties
	//SanitizedForPlayer produces a copy state object that has been sanitized for
	//the player at the given index. The state object returned will have
	//Sanitized() return true. Will call GameDelegate.StateSanitizationPolicy to
	//retrieve the policy in place. See the package level comment for an overview
	//of how state sanitization works.
	SanitizedForPlayer(player PlayerIndex) State

	//StorageRecord returns a StateStorageRecord representing the state.
	StorageRecord() StateStorageRecord
}

//PlayerIndex is an int that represents the index of a given player in a game.
//Normal values are [0, game.NumPlayers). Special values are AdminPlayerIndex
//and ObserverPlayerIndex.
type PlayerIndex int

//ObserverPlayerIndex is a special PlayerIndex that denotes that the player in
//question is not one of the normal players, but someone generically watching.
//All hidden state should be hidden to them, and GroupSelf will never trigger
//for them.
const ObserverPlayerIndex PlayerIndex = -1

//AdminPlayerINdex is a special PlayerIndex that denotes the omniscient admin
//who can see all state and make moves whenever they want. This PlayerIndex
//should only be used in rare or debug circumstances.
const AdminPlayerIndex PlayerIndex = -2

//A MutableState is a state that is designed to be modified in place. These
//are passed to methods (instead of normal States) as a signal that
//modifications are intended to be done on the state.
type MutableState interface {
	//MutableState contains all of the methods of a read-only state.
	State
	//MutableGame is a reference to the MutableGameState for this MutableState.
	MutableGame() MutableGameState
	//MutablePlayers returns a slice of MutablePlayerStates for this MutableState.
	MutablePlayers() []MutablePlayerState

	MutableDynamicComponentValues() map[string][]MutableDynamicComponentValues
}

//Valid returns true if the PlayerIndex's value is legal in the context of the
//current State.
func (p PlayerIndex) Valid(state State) bool {
	if p == AdminPlayerIndex || p == ObserverPlayerIndex {
		return true
	}
	if state == nil {
		return false
	}
	if p < 0 || int(p) >= len(state.Players()) {
		return false
	}
	return true
}

//state implements both State and MutableState, so it can always be passed for
//either, and what it's interpreted as is primarily a function of what the
//method signature is that it's passed to
type state struct {
	gameState              MutableGameState
	playerStates           []MutablePlayerState
	computed               *computedPropertiesImpl
	dynamicComponentValues map[string][]MutableDynamicComponentValues
	sanitized              bool
	game                   *Game
	//Set to true while computed is being calculating computed. Primarily so
	//if you marshal JSON in that time we know to just elide computed.
	calculatingComputed bool
	//If TimerProp.Start() is called, it prepares a timer, but doesn't
	//actually start ticking it until this state is committed. This is where
	//we accumulate the timers that still need to be fully started at that
	//point.
	timersToStart []int
}

func (s *state) MutableGame() MutableGameState {
	return s.gameState
}

func (s *state) MutablePlayers() []MutablePlayerState {
	return s.playerStates
}

func (s *state) MutableDynamicComponentValues() map[string][]MutableDynamicComponentValues {
	return s.dynamicComponentValues
}

func (s *state) Game() GameState {
	return s.gameState
}

func (s *state) Players() []PlayerState {
	result := make([]PlayerState, len(s.playerStates))
	for i := 0; i < len(s.playerStates); i++ {
		result[i] = s.playerStates[i]
	}
	return result
}

func (s *state) Copy(sanitized bool) State {
	return s.copy(sanitized)
}

func (s *state) copy(sanitized bool) *state {
	players := make([]MutablePlayerState, len(s.playerStates))

	for i, player := range s.playerStates {
		players[i] = player.MutableCopy()
	}

	result := &state{
		gameState:              s.gameState.MutableCopy(),
		playerStates:           players,
		dynamicComponentValues: make(map[string][]MutableDynamicComponentValues),
		sanitized:              sanitized,
		game:                   s.game,
		//We copy this over, because this should only be set when computed is
		//being calculated, and during that time we'll be creating sanitized
		//copies of ourselves. However, if there are other copies created when
		//this flag is set that outlive the original flag being unset, that
		//state would be in a bad state long term...
		calculatingComputed: s.calculatingComputed,
	}

	for deckName, values := range s.dynamicComponentValues {
		arr := make([]MutableDynamicComponentValues, len(values))
		for i := 0; i < len(values); i++ {
			arr[i] = values[i].Copy()
			if err := verifyReaderObjects(arr[i].Reader(), result); err != nil {
				return nil
			}
		}
		result.dynamicComponentValues[deckName] = arr
	}

	//FixUp stacks to make sure they point to this new state.
	if err := verifyReaderObjects(result.gameState.Reader(), result); err != nil {
		return nil
	}
	for _, player := range result.playerStates {
		if err := verifyReaderObjects(player.Reader(), result); err != nil {
			return nil
		}
	}

	return result
}

//committed is called right after the state has been committed to the database
//and we're sure it will stick. This is the time to do any actions that were
//triggered during the state manipulation. currently that is only timers.
func (s *state) committed() {
	for _, id := range s.timersToStart {
		s.game.manager.timers.StartTimer(id)
	}
}

func (s *state) StorageRecord() StateStorageRecord {
	record, _ := DefaultMarshalJSON(s)
	return record
}

func (s *state) MarshalJSON() ([]byte, error) {

	obj := map[string]interface{}{
		"Game":     s.gameState,
		"Players":  s.playerStates,
		"Computed": s.Computed(),
	}

	dynamic := s.DynamicComponentValues()

	if dynamic != nil && len(dynamic) != 0 {
		obj["Components"] = dynamic
	} else {
		obj["Components"] = map[string]interface{}{}
	}

	return json.Marshal(obj)
}

func (s *state) Diagram() string {
	return s.game.manager.delegate.Diagram(s)
}

func (s *state) Sanitized() bool {
	return s.sanitized
}

func (s *state) DynamicComponentValues() map[string][]DynamicComponentValues {

	result := make(map[string][]DynamicComponentValues)

	for key, val := range s.dynamicComponentValues {
		slice := make([]DynamicComponentValues, len(val))
		for i := 0; i < len(slice); i++ {
			slice[i] = val[i]
		}
		result[key] = slice
	}

	return result
}

func (s *state) Computed() ComputedProperties {

	if s.calculatingComputed {
		//This might be called in a Compute() callback either directly, or
		//implicitly via MarshalJSON.
		return nil
	}

	if s.computed == nil {

		s.calculatingComputed = true
		s.computed = newComputedPropertiesImpl(s.game.manager.delegate.ComputedPropertiesConfig(), s)
		s.calculatingComputed = false
	}
	return s.computed
}

//SanitizedForPlayer is in sanitized.go

//BaseState is the interface that all state objects--PlayerStates and GameStates
//--implement.
type BaseState interface {
	Reader() PropertyReader
}

//MutableBaseState is the interface that Mutable{Game,Player}State's
//implement.
type MutableBaseState interface {
	ReadSetter() PropertyReadSetter
}

//PlayerState represents the state of a game associated with a specific user.
type PlayerState interface {
	//PlayerIndex encodes the index this user's state is in the containing
	//state object.
	PlayerIndex() int
	//Copy produces a copy of our current state. Be sure it's a deep copy that
	//makes a copy of any pointer arguments.
	Copy() PlayerState
	BaseState
}

//A MutablePlayerState is a PlayerState that is allowed to be mutated.
type MutablePlayerState interface {
	PlayerState
	MutableCopy() MutablePlayerState
	MutableBaseState
}

//GameState represents the state of a game that is not associated with a
//particular user. For example, the draw stack of cards, who the current
//player is, and other properites.
type GameState interface {
	//Copy returns a copy of our current state. Be sure it's a deep copy that
	//makes a copy of any pointer arguments.
	Copy() GameState
	BaseState
}

//A MutableGameState is a GameState that is allowed to be mutated.
type MutableGameState interface {
	GameState
	MutableCopy() MutableGameState
	MutableBaseState
}

//DefaultMarshalJSON is a simple wrapper around json.MarshalIndent, with the
//right defaults set. If your structs need to implement MarshaLJSON to output
//JSON, use this to encode it.
func DefaultMarshalJSON(obj interface{}) ([]byte, error) {
	return json.MarshalIndent(obj, "", "  ")
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to state tracking.  If enabled, state
// tracking will capture the initial READY packet and many other websocket
// events and maintain an in-memory state of of guilds, channels, users, and
// so forth.  This information can be accessed through the Session.State struct.

package discordgo

import (
	"errors"
	"log"
)

// ErrNilState is returned when the state is nil.
var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.")

// NewState creates an empty state.
func NewState() *State {
	return &State{
		Ready: Ready{
			PrivateChannels: []*Channel{},
			Guilds:          []*Guild{},
		},
	}
}

// OnReady takes a Ready event and updates all internal state.
func (s *State) OnReady(r *Ready) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	s.Ready = *r

	for _, g := range s.Guilds {
		for _, c := range g.Channels {
			c.GuildID = g.ID
		}
	}

	return nil
}

// GuildAdd adds a guild to the current world state, or
// updates it if it already exists.
func (s *State) GuildAdd(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	// If the guild exists, replace it.
	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			// Don't stomp on properties that don't come in updates.
			guild.Members = g.Members
			guild.Presences = g.Presences
			guild.Channels = g.Channels
			guild.VoiceStates = g.VoiceStates
			s.Guilds[i] = guild
			return nil
		}
	}

	// Otherwise, update the channels to point to the right guild
	for _, c := range guild.Channels {
		c.GuildID = guild.ID
	}

	s.Guilds = append(s.Guilds, guild)
	return nil
}

// GuildRemove removes a guild from current world state.
func (s *State) GuildRemove(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...)
			return nil
		}
	}

	return errors.New("Guild not found.")
}

// Guild gets a guild by ID.
// Useful for querying if @me is in a guild:
//     _, err := discordgo.Session.State.Guild(guildID)
//     isInGuild := err == nil
func (s *State) Guild(guildID string) (*Guild, error) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	for _, g := range s.Guilds {
		if g.ID == guildID {
			return g, nil
		}
	}

	return nil, errors.New("Guild not found.")
}

// TODO: Consider moving Guild state update methods onto *Guild.

// MemberAdd adds a member to the current world state, or
// updates it if it already exists.
func (s *State) MemberAdd(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members[i] = member
			return nil
		}
	}

	guild.Members = append(guild.Members, member)
	return nil
}

// MemberRemove removes a member from current world state.
func (s *State) MemberRemove(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
			return nil
		}
	}

	return errors.New("Member not found.")
}

// Member gets a member by ID from a guild.
func (s *State) Member(guildID, userID string) (*Member, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range guild.Members {
		if m.User.ID == userID {
			return m, nil
		}
	}

	return nil, errors.New("Member not found.")
}

// ChannelAdd adds a guild to the current world state, or
// updates it if it already exists.
// Channels may exist either as PrivateChannels or inside
// a guild.
func (s *State) ChannelAdd(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()

		// If the channel exists, replace it.
		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				// Don't stomp on messages.
				channel.Messages = c.Messages
				s.PrivateChannels[i] = channel
				return nil
			}
		}

		s.PrivateChannels = append(s.PrivateChannels, channel)
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		s.Lock()
		defer s.Unlock()

		// If the channel exists, replace it.
		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				// Don't stomp on messages.
				channel.Messages = c.Messages
				guild.Channels[i] = channel
				return nil
			}
		}

		guild.Channels = append(guild.Channels, channel)
	}

	return nil
}

// ChannelRemove removes a channel from current world state.
func (s *State) ChannelRemove(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()

		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...)
				return nil
			}
		}
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		s.Lock()
		defer s.Unlock()

		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
				return nil
			}
		}
	}

	return errors.New("Channel not found.")
}

// GuildChannel gets a channel by ID from a guild.
func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, c := range guild.Channels {
		if c.ID == channelID {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
}

// PrivateChannel gets a private channel by ID.
func (s *State) PrivateChannel(channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	for _, c := range s.PrivateChannels {
		if c.ID == channelID {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
}

// Channel gets a channel by ID, it will look in all guilds an private channels.
func (s *State) Channel(channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	c, err := s.PrivateChannel(channelID)
	if err == nil {
		return c, nil
	}

	for _, g := range s.Guilds {
		c, err := s.GuildChannel(g.ID, channelID)
		if err == nil {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
}

// Emoji returns an emoji for a guild and emoji id.
func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, e := range guild.Emojis {
		if e.ID == emojiID {
			return e, nil
		}
	}

	return nil, errors.New("Emoji not found.")
}

// EmojiAdd adds an emoji to the current world state.
func (s *State) EmojiAdd(guildID string, emoji *Emoji) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, e := range guild.Emojis {
		if e.ID == emoji.ID {
			guild.Emojis[i] = emoji
			return nil
		}
	}

	guild.Emojis = append(guild.Emojis, emoji)
	return nil
}

// EmojisAdd adds multiple emojis to the world state.
func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error {
	for _, e := range emojis {
		if err := s.EmojiAdd(guildID, e); err != nil {
			return err
		}
	}
	return nil
}

// MessageAdd adds a message to the current world state, or updates it if it exists.
// If the channel cannot be found, the message is discarded.
// Messages are kept in state up to s.MaxMessageCount
func (s *State) MessageAdd(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// If the message exists, replace it.
	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages[i] = message
			return nil
		}
	}

	c.Messages = append(c.Messages, message)

	if len(c.Messages) > s.MaxMessageCount {
		s.Unlock()
		for len(c.Messages) > s.MaxMessageCount {
			err := s.MessageRemove(c.Messages[0])
			if err != nil {
				log.Println("message remove error: ", err)
			}
		}
		s.Lock()
	}
	return nil
}

// MessageRemove removes a message from the world state.
func (s *State) MessageRemove(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
			return nil
		}
	}

	return errors.New("Message not found.")
}

func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error {
	guild, err := s.Guild(update.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// Handle Leaving Channel
	if update.ChannelID == "" {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...)
				return nil
			}
		}
	} else {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates[i] = update.VoiceState
				return nil
			}
		}

		guild.VoiceStates = append(guild.VoiceStates, update.VoiceState)
	}

	return nil
}

// Message gets a message by channel and message ID.
func (s *State) Message(channelID, messageID string) (*Message, error) {
	if s == nil {
		return nil, ErrNilState
	}

	c, err := s.Channel(channelID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range c.Messages {
		if m.ID == messageID {
			return m, nil
		}
	}

	return nil, errors.New("Message not found.")
}

// onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) (err error) {
	if s == nil {
		return ErrNilState
	}
	if !se.StateEnabled {
		return nil
	}

	switch t := i.(type) {
	case *Ready:
		err = s.OnReady(t)
	case *GuildCreate:
		err = s.GuildAdd(t.Guild)
	case *GuildUpdate:
		err = s.GuildAdd(t.Guild)
	case *GuildDelete:
		err = s.GuildRemove(t.Guild)
	case *GuildMemberAdd:
		err = s.MemberAdd(t.Member)
	case *GuildMemberUpdate:
		err = s.MemberAdd(t.Member)
	case *GuildMemberRemove:
		err = s.MemberRemove(t.Member)
	case *GuildEmojisUpdate:
		err = s.EmojisAdd(t.GuildID, t.Emojis)
	case *ChannelCreate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelUpdate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelDelete:
		err = s.ChannelRemove(t.Channel)
	case *MessageCreate:
		err = s.MessageAdd(t.Message)
	case *MessageUpdate:
		err = s.MessageAdd(t.Message)
	case *MessageDelete:
		err = s.MessageRemove(t.Message)
	case *VoiceStateUpdate:
		err = s.voiceStateUpdate(t)
	}

	return
}
<commit_msg>Fix potential infinite loop on state.<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to state tracking.  If enabled, state
// tracking will capture the initial READY packet and many other websocket
// events and maintain an in-memory state of of guilds, channels, users, and
// so forth.  This information can be accessed through the Session.State struct.

package discordgo

import "errors"

// ErrNilState is returned when the state is nil.
var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.")

// NewState creates an empty state.
func NewState() *State {
	return &State{
		Ready: Ready{
			PrivateChannels: []*Channel{},
			Guilds:          []*Guild{},
		},
	}
}

// OnReady takes a Ready event and updates all internal state.
func (s *State) OnReady(r *Ready) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	s.Ready = *r

	for _, g := range s.Guilds {
		for _, c := range g.Channels {
			c.GuildID = g.ID
		}
	}

	return nil
}

// GuildAdd adds a guild to the current world state, or
// updates it if it already exists.
func (s *State) GuildAdd(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	// If the guild exists, replace it.
	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			// Don't stomp on properties that don't come in updates.
			guild.Members = g.Members
			guild.Presences = g.Presences
			guild.Channels = g.Channels
			guild.VoiceStates = g.VoiceStates
			s.Guilds[i] = guild
			return nil
		}
	}

	// Otherwise, update the channels to point to the right guild
	for _, c := range guild.Channels {
		c.GuildID = guild.ID
	}

	s.Guilds = append(s.Guilds, guild)
	return nil
}

// GuildRemove removes a guild from current world state.
func (s *State) GuildRemove(guild *Guild) error {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...)
			return nil
		}
	}

	return errors.New("Guild not found.")
}

// Guild gets a guild by ID.
// Useful for querying if @me is in a guild:
//     _, err := discordgo.Session.State.Guild(guildID)
//     isInGuild := err == nil
func (s *State) Guild(guildID string) (*Guild, error) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	for _, g := range s.Guilds {
		if g.ID == guildID {
			return g, nil
		}
	}

	return nil, errors.New("Guild not found.")
}

// TODO: Consider moving Guild state update methods onto *Guild.

// MemberAdd adds a member to the current world state, or
// updates it if it already exists.
func (s *State) MemberAdd(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members[i] = member
			return nil
		}
	}

	guild.Members = append(guild.Members, member)
	return nil
}

// MemberRemove removes a member from current world state.
func (s *State) MemberRemove(member *Member) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(member.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
			return nil
		}
	}

	return errors.New("Member not found.")
}

// Member gets a member by ID from a guild.
func (s *State) Member(guildID, userID string) (*Member, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range guild.Members {
		if m.User.ID == userID {
			return m, nil
		}
	}

	return nil, errors.New("Member not found.")
}

// ChannelAdd adds a guild to the current world state, or
// updates it if it already exists.
// Channels may exist either as PrivateChannels or inside
// a guild.
func (s *State) ChannelAdd(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()

		// If the channel exists, replace it.
		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				// Don't stomp on messages.
				channel.Messages = c.Messages
				s.PrivateChannels[i] = channel
				return nil
			}
		}

		s.PrivateChannels = append(s.PrivateChannels, channel)
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		s.Lock()
		defer s.Unlock()

		// If the channel exists, replace it.
		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				// Don't stomp on messages.
				channel.Messages = c.Messages
				guild.Channels[i] = channel
				return nil
			}
		}

		guild.Channels = append(guild.Channels, channel)
	}

	return nil
}

// ChannelRemove removes a channel from current world state.
func (s *State) ChannelRemove(channel *Channel) error {
	if s == nil {
		return ErrNilState
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()

		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...)
				return nil
			}
		}
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		}

		s.Lock()
		defer s.Unlock()

		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
				return nil
			}
		}
	}

	return errors.New("Channel not found.")
}

// GuildChannel gets a channel by ID from a guild.
func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, c := range guild.Channels {
		if c.ID == channelID {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
}

// PrivateChannel gets a private channel by ID.
func (s *State) PrivateChannel(channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	for _, c := range s.PrivateChannels {
		if c.ID == channelID {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
}

// Channel gets a channel by ID, it will look in all guilds an private channels.
func (s *State) Channel(channelID string) (*Channel, error) {
	if s == nil {
		return nil, ErrNilState
	}

	c, err := s.PrivateChannel(channelID)
	if err == nil {
		return c, nil
	}

	for _, g := range s.Guilds {
		c, err := s.GuildChannel(g.ID, channelID)
		if err == nil {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
}

// Emoji returns an emoji for a guild and emoji id.
func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, e := range guild.Emojis {
		if e.ID == emojiID {
			return e, nil
		}
	}

	return nil, errors.New("Emoji not found.")
}

// EmojiAdd adds an emoji to the current world state.
func (s *State) EmojiAdd(guildID string, emoji *Emoji) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, e := range guild.Emojis {
		if e.ID == emoji.ID {
			guild.Emojis[i] = emoji
			return nil
		}
	}

	guild.Emojis = append(guild.Emojis, emoji)
	return nil
}

// EmojisAdd adds multiple emojis to the world state.
func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error {
	for _, e := range emojis {
		if err := s.EmojiAdd(guildID, e); err != nil {
			return err
		}
	}
	return nil
}

// MessageAdd adds a message to the current world state, or updates it if it exists.
// If the channel cannot be found, the message is discarded.
// Messages are kept in state up to s.MaxMessageCount
func (s *State) MessageAdd(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// If the message exists, replace it.
	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages[i] = message
			return nil
		}
	}

	c.Messages = append(c.Messages, message)

	if len(c.Messages) > s.MaxMessageCount {
		c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
	}
	return nil
}

// MessageRemove removes a message from the world state.
func (s *State) MessageRemove(message *Message) error {
	if s == nil {
		return ErrNilState
	}

	c, err := s.Channel(message.ChannelID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
			return nil
		}
	}

	return errors.New("Message not found.")
}

func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error {
	guild, err := s.Guild(update.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// Handle Leaving Channel
	if update.ChannelID == "" {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...)
				return nil
			}
		}
	} else {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates[i] = update.VoiceState
				return nil
			}
		}

		guild.VoiceStates = append(guild.VoiceStates, update.VoiceState)
	}

	return nil
}

// Message gets a message by channel and message ID.
func (s *State) Message(channelID, messageID string) (*Message, error) {
	if s == nil {
		return nil, ErrNilState
	}

	c, err := s.Channel(channelID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, m := range c.Messages {
		if m.ID == messageID {
			return m, nil
		}
	}

	return nil, errors.New("Message not found.")
}

// onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) (err error) {
	if s == nil {
		return ErrNilState
	}
	if !se.StateEnabled {
		return nil
	}

	switch t := i.(type) {
	case *Ready:
		err = s.OnReady(t)
	case *GuildCreate:
		err = s.GuildAdd(t.Guild)
	case *GuildUpdate:
		err = s.GuildAdd(t.Guild)
	case *GuildDelete:
		err = s.GuildRemove(t.Guild)
	case *GuildMemberAdd:
		err = s.MemberAdd(t.Member)
	case *GuildMemberUpdate:
		err = s.MemberAdd(t.Member)
	case *GuildMemberRemove:
		err = s.MemberRemove(t.Member)
	case *GuildEmojisUpdate:
		err = s.EmojisAdd(t.GuildID, t.Emojis)
	case *ChannelCreate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelUpdate:
		err = s.ChannelAdd(t.Channel)
	case *ChannelDelete:
		err = s.ChannelRemove(t.Channel)
	case *MessageCreate:
		err = s.MessageAdd(t.Message)
	case *MessageUpdate:
		err = s.MessageAdd(t.Message)
	case *MessageDelete:
		err = s.MessageRemove(t.Message)
	case *VoiceStateUpdate:
		err = s.voiceStateUpdate(t)
	}

	return
}
<|endoftext|>


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

package main

import (
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"

	"github.com/jacobsa/comeback/state"
	"github.com/jacobsa/comeback/util"
	"github.com/jacobsa/gcloud/gcs"
)

var g_stateOnce sync.Once
var g_state state.State

var g_saveStateMutex sync.Mutex

func buildExistingScores(
	bucket gcs.Bucket) (existingScores util.StringSet, err error)

func initState() {
	cfg := getConfig()
	bucket := getBucket()
	var err error

	// Open the specified file.
	f, err := os.Open(cfg.StateFile)

	// Special case: if the error is that the file doesn't exist, ignore it.
	if err != nil && os.IsNotExist(err) {
		err = nil
		log.Println("No state file found. Using fresh state.")
	} else {
		// Handle other Open errors.
		if err != nil {
			log.Fatalln("Opening state file:", err)
		}

		defer f.Close()

		// Load the state struct.
		log.Println("Loading from state file.")

		g_state, err = state.LoadState(f)
		if err != nil {
			log.Fatalln("LoadState:", err)
		}
	}

	// Throw out the existing score cache if requested.
	if *g_discardScoreCache {
		g_state.ScoresForFiles = state.NewScoreMap()
	}

	// Make sure there are no nil interface values.
	if g_state.ScoresForFiles == nil {
		g_state.ScoresForFiles = state.NewScoreMap()
	}

	// If we don't know the set of hex scores in the store, or the set of scores
	// is stale, re-list.
	age := time.Now().Sub(g_state.RelistTime)
	const maxAge = 30 * 24 * time.Hour

	if g_state.ExistingScores == nil || age > maxAge {
		log.Println("Listing existing scores...")

		g_state.RelistTime = time.Now()
		g_state.ExistingScores, err = buildExistingScores(bucket)
		if err != nil {
			log.Fatalf("buildExistingScores: %v", err)
		}
	}
}

func getState() *state.State {
	g_stateOnce.Do(initState)
	return &g_state
}

func saveState() {
	// Make sure only one run can happen at a time.
	g_saveStateMutex.Lock()
	defer g_saveStateMutex.Unlock()

	var err error

	cfg := getConfig()
	stateStruct := getState()

	// Write out the state to a temporary file.
	f, err := ioutil.TempFile("", "comeback_state")
	if err != nil {
		log.Fatalln("Creating temporary state file:", err)
	}

	tempFilePath := f.Name()

	if err = state.SaveState(f, *stateStruct); err != nil {
		log.Fatalln("SaveState:", err)
	}

	if err = f.Close(); err != nil {
		log.Fatalln("Closing temporary state file:", err)
	}

	// Rename the file into the new location.
	if err = os.Rename(tempFilePath, cfg.StateFile); err != nil {
		log.Fatalln("Renaming temporary state file:", err)
	}
}
<commit_msg>buildExistingScores<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/state"
	"github.com/jacobsa/comeback/util"
	"github.com/jacobsa/comeback/wiring"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
)

var g_stateOnce sync.Once
var g_state state.State

var g_saveStateMutex sync.Mutex

func buildExistingScores(
	bucket gcs.Bucket) (existingScores util.StringSet, err error) {
	b := syncutil.NewBundle(context.Background())
	defer func() { err = b.Join() }()

	// List scores into a channel.
	scores := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(scores)
		err = blob.ListScores(ctx, bucket, wiring.BlobObjectNamePrefix, scores)
		if err != nil {
			err = fmt.Errorf("ListScores: %v", err)
			return
		}

		return
	})

	// Accumulate hex scores into a string set.
	existingScores = util.NewStringSet()
	b.Add(func(ctx context.Context) (err error) {
		for score := range scores {
			existingScores.Add(score.Hex())
		}

		return
	})

	return
}

func initState() {
	cfg := getConfig()
	bucket := getBucket()
	var err error

	// Open the specified file.
	f, err := os.Open(cfg.StateFile)

	// Special case: if the error is that the file doesn't exist, ignore it.
	if err != nil && os.IsNotExist(err) {
		err = nil
		log.Println("No state file found. Using fresh state.")
	} else {
		// Handle other Open errors.
		if err != nil {
			log.Fatalln("Opening state file:", err)
		}

		defer f.Close()

		// Load the state struct.
		log.Println("Loading from state file.")

		g_state, err = state.LoadState(f)
		if err != nil {
			log.Fatalln("LoadState:", err)
		}
	}

	// Throw out the existing score cache if requested.
	if *g_discardScoreCache {
		g_state.ScoresForFiles = state.NewScoreMap()
	}

	// Make sure there are no nil interface values.
	if g_state.ScoresForFiles == nil {
		g_state.ScoresForFiles = state.NewScoreMap()
	}

	// If we don't know the set of hex scores in the store, or the set of scores
	// is stale, re-list.
	age := time.Now().Sub(g_state.RelistTime)
	const maxAge = 30 * 24 * time.Hour

	if g_state.ExistingScores == nil || age > maxAge {
		log.Println("Listing existing scores...")

		g_state.RelistTime = time.Now()
		g_state.ExistingScores, err = buildExistingScores(bucket)
		if err != nil {
			log.Fatalf("buildExistingScores: %v", err)
		}
	}
}

func getState() *state.State {
	g_stateOnce.Do(initState)
	return &g_state
}

func saveState() {
	// Make sure only one run can happen at a time.
	g_saveStateMutex.Lock()
	defer g_saveStateMutex.Unlock()

	var err error

	cfg := getConfig()
	stateStruct := getState()

	// Write out the state to a temporary file.
	f, err := ioutil.TempFile("", "comeback_state")
	if err != nil {
		log.Fatalln("Creating temporary state file:", err)
	}

	tempFilePath := f.Name()

	if err = state.SaveState(f, *stateStruct); err != nil {
		log.Fatalln("SaveState:", err)
	}

	if err = f.Close(); err != nil {
		log.Fatalln("Closing temporary state file:", err)
	}

	// Rename the file into the new location.
	if err = os.Rename(tempFilePath, cfg.StateFile); err != nil {
		log.Fatalln("Renaming temporary state file:", err)
	}
}
<|endoftext|>


<commit_before>package checkpoints

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/golang-devops/release-co-pilot/execution"
	"github.com/golang-devops/release-co-pilot/logging"
	"github.com/golang-devops/release-co-pilot/util"
)

func NewIncomingWebhook(listenHTTPAddress string, randomTokenLength int) execution.Task {
	return &incomingWebhook{
		listenHTTPAddress: listenHTTPAddress,
		randomTokenLength: randomTokenLength,
	}
}

type incomingWebhook struct {
	listenHTTPAddress string
	randomTokenLength int

	resultChan              chan<- *incomingWebhookResult
	randomToken             string
	abortKeyword            string
	handlePostRequestLogger logging.Logger
}

type incomingWebhookResult struct {
	Error        error
	ServerClosed bool
}

func (i *incomingWebhook) handlePostRequest(w http.ResponseWriter, r *http.Request) {
	if !strings.EqualFold(r.Method, "POST") {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Only POST method currently allowed"))
		return
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		i.handlePostRequestLogger.WithError(err).WithFields(map[string]interface{}{
			"remote-address":  r.RemoteAddr,
			"request-headers": r.Header,
		}).Warn(fmt.Sprintf("Failed to read POST body"))
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf("Body is empty, try again.")))
		return
	}

	bodyStr := strings.TrimSpace(string(body))
	if bodyStr == i.randomToken {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("Thank you, token is accepted"))
		i.resultChan <- &incomingWebhookResult{}
		return
	}

	if bodyStr == i.abortKeyword {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("Abort keyword received"))
		i.resultChan <- &incomingWebhookResult{
			Error: errors.New("User aborted with keyword " + i.abortKeyword),
		}
		return
	}

	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte(fmt.Sprintf("Unexpected input '%s' received, try again.", bodyStr)))
}

func (i *incomingWebhook) runServer(logger logging.Logger, server *http.Server) {
	logger.Info(fmt.Sprintf("Now starting incoming webhook on '%s' to wait for token %s to continue or '%s' to abort", i.listenHTTPAddress, i.randomToken, i.abortKeyword))
	if err := server.ListenAndServe(); err != nil {
		if err != http.ErrServerClosed {
			logger.WithError(err).Error("Unable to start HTTP server")
			i.resultChan <- &incomingWebhookResult{
				Error:        errors.New("Server listen error: " + err.Error()),
				ServerClosed: true,
			}
		}
		logger.Debug("HTTP server connection closed")
		return
	}
}

func (i *incomingWebhook) runAndWait(logger logging.Logger) error {
	resultChan := make(chan *incomingWebhookResult)
	i.resultChan = resultChan

	mux := http.NewServeMux()
	i.handlePostRequestLogger = logger
	mux.HandleFunc("/", i.handlePostRequest)

	server := &http.Server{
		Addr:    i.listenHTTPAddress,
		Handler: mux,
	}

	go i.runServer(logger, server)

	result := <-resultChan

	if !result.ServerClosed {
		if err := server.Close(); err != nil {
			logger.WithError(err).Error("Unable to shutdown webhook server")
		}
	}
	if result.Error != nil {
		return result.Error
	}

	return nil
}

func (i *incomingWebhook) Execute(logger logging.Logger) error {
	logger = logger.WithFields(map[string]interface{}{
		"obj-type":            fmt.Sprintf("%T", i),
		"listen-http-address": i.listenHTTPAddress,
	})

	i.randomToken = util.RandomAlphaNumericString(i.randomTokenLength)
	i.abortKeyword = "abort"

	if err := i.runAndWait(logger); err != nil {
		return err
	}

	return nil
}
<commit_msg>feat: logging improvement for incoming_webhook and also reply with FORM and specify TOKEN in GET request.<commit_after>package checkpoints

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/golang-devops/release-co-pilot/execution"
	"github.com/golang-devops/release-co-pilot/logging"
	"github.com/golang-devops/release-co-pilot/util"
)

func NewIncomingWebhook(listenHTTPAddress string, randomTokenLength int) execution.Task {
	return &incomingWebhook{
		listenHTTPAddress: listenHTTPAddress,
		randomTokenLength: randomTokenLength,
	}
}

type incomingWebhook struct {
	listenHTTPAddress string
	randomTokenLength int

	resultChan           chan<- *incomingWebhookResult
	randomToken          string
	abortKeyword         string
	handleRequestsLogger logging.Logger
}

type incomingWebhookResult struct {
	Error        error
	ServerClosed bool
}

func (i *incomingWebhook) writeString(w http.ResponseWriter, str string) {
	if _, err := w.Write([]byte(str)); err != nil {
		i.handleRequestsLogger.WithError(err).Error("Unable to write: " + str)
	}
}

func (i *incomingWebhook) writeTokenInputForm(w http.ResponseWriter) {
	i.writeString(w, fmt.Sprintf(`

				<form method="POST">
					Token:

					<input readonly type="text" value="%s">

					<input type="text" name="token">
					<button type="submit">submit</button>
				</form>

	`, i.randomToken))
}

func (i *incomingWebhook) onDefaultGETRequest(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	i.writeTokenInputForm(w)
}

func (i *incomingWebhook) onDefaultPOSTRequest(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		i.handleRequestsLogger.WithError(err).WithFields(map[string]interface{}{
			"remote-address":  r.RemoteAddr,
			"request-headers": r.Header,
		}).Warn(fmt.Sprintf("Failed to read POST body"))
		w.WriteHeader(http.StatusBadRequest)
		i.writeString(w, fmt.Sprintf("Body is empty, try again."))
		return
	}

	bodyStr := strings.TrimSpace(string(body))

	if strings.HasPrefix(bodyStr, "token=") {
		bodyStr = bodyStr[len("token="):]
	}

	if bodyStr == i.randomToken {
		w.WriteHeader(http.StatusOK)
		i.writeString(w, fmt.Sprintf(`Thank you, token is accepted.`))
		i.resultChan <- &incomingWebhookResult{}
		return
	}

	if bodyStr == i.abortKeyword {
		w.WriteHeader(http.StatusOK)
		i.writeString(w, fmt.Sprintf("Abort keyword received."))
		i.resultChan <- &incomingWebhookResult{
			Error: errors.New("User aborted with keyword " + i.abortKeyword),
		}
		return
	}

	w.WriteHeader(http.StatusBadRequest)
	i.writeString(w, fmt.Sprintf("Unexpected input '%s' received, try again.", bodyStr))
}

func (i *incomingWebhook) handleDefaultRequest(w http.ResponseWriter, r *http.Request) {
	switch strings.ToUpper(r.Method) {
	case "GET":
		i.onDefaultGETRequest(w, r)
	case "POST":
		i.onDefaultPOSTRequest(w, r)
	default:
		w.WriteHeader(http.StatusBadRequest)
		i.writeString(w, "Only POST method currently allowed")
	}
}

func (i *incomingWebhook) runServer(logger logging.Logger, server *http.Server) {
	logger.Info(fmt.Sprintf("Now starting incoming webhook on '%s' to wait for token %s to continue or '%s' to abort", i.listenHTTPAddress, i.randomToken, i.abortKeyword))
	if err := server.ListenAndServe(); err != nil {
		if err != http.ErrServerClosed {
			logger.WithError(err).Error("Unable to start HTTP server")
			i.resultChan <- &incomingWebhookResult{
				Error:        errors.New("Server listen error: " + err.Error()),
				ServerClosed: true,
			}
		}
		// logger.Debug("HTTP server connection closed")
		return
	}
}

func (i *incomingWebhook) runAndWait(logger logging.Logger) error {
	resultChan := make(chan *incomingWebhookResult)
	i.resultChan = resultChan

	mux := http.NewServeMux()
	i.handleRequestsLogger = logger
	mux.HandleFunc("/", i.handleDefaultRequest)

	server := &http.Server{
		Addr:    i.listenHTTPAddress,
		Handler: mux,
	}

	go i.runServer(logger, server)

	result := <-resultChan

	if !result.ServerClosed {
		if err := server.Close(); err != nil {
			logger.WithError(err).Error("Unable to shutdown webhook server")
		}
	}
	if result.Error != nil {
		return result.Error
	}

	return nil
}

func (i *incomingWebhook) Execute(logger logging.Logger) error {
	logger = logger.WithFields(map[string]interface{}{
		"obj-type":            fmt.Sprintf("%T", i),
		"listen-http-address": i.listenHTTPAddress,
	})

	i.randomToken = util.RandomAlphaNumericString(i.randomTokenLength)
	i.abortKeyword = "abort"

	if err := i.runAndWait(logger); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package memsize_test

import (
	"testing"
	"unsafe"

	"github.com/fjl/memsize"
)

const (
	sizeofSlice     = unsafe.Sizeof([]byte{})
	sizeofMap       = unsafe.Sizeof(map[string]string{})
	sizeofInterface = unsafe.Sizeof((interface{})(nil))
	sizeofString    = unsafe.Sizeof("")
)

type (
	struct16 struct {
		x, y uint64
	}
	struct32ptr struct {
		x   uint32
		cld *struct32ptr
	}
	struct64array struct {
		array64
	}
	structslice struct {
		s []uint32
	}
	structstring struct {
		s string
	}
	array64 [64]byte
)

func TestTotal(t *testing.T) {
	tests := []struct {
		name string
		v    interface{}
		want uintptr
	}{
		{
			name: "struct16",
			v:    &struct16{},
			want: 16,
		},
		{
			name: "struct32ptr_nil",
			v:    &struct32ptr{},
			want: 16,
		},
		{
			name: "struct32ptr",
			v:    &struct32ptr{cld: &struct32ptr{}},
			want: 32,
		},
		{
			name: "struct32ptr_loop",
			v: func() *struct32ptr {
				v := &struct32ptr{}
				v.cld = v
				return v
			}(),
			want: 16,
		},
		{
			name: "struct64array",
			v:    &struct64array{},
			want: 64,
		},
		{
			name: "structslice",
			v:    &structslice{s: []uint32{1, 2, 3}},
			want: sizeofSlice + 3*4,
		},
		{
			name: "array64",
			v:    &array64{},
			want: 64,
		},
		{
			name: "byteslice",
			v:    &[]byte{1, 2, 3},
			want: 27,
		},
		{
			name: "slice3_ptrval",
			v:    &[]*struct16{{}, {}, {}},
			want: 96,
		},
		{
			name: "map3",
			v:    &map[uint64]uint64{1: 1, 2: 2, 3: 3},
			want: 56,
		},
		{
			name: "map3_ptrval",
			v:    &map[uint64]*struct16{1: {}, 2: {}, 3: {}},
			want: 104,
		},
		{
			name: "map3_ptrkey",
			v:    &map[*struct16]uint64{{x: 1}: 1, {x: 2}: 2, {x: 3}: 3},
			want: 104,
		},
		{
			name: "pointerpointer",
			v: func() **uint64 {
				i := uint64(0)
				p := &i
				return &p
			}(),
			want: 16,
		},
		{
			name: "structstring",
			v:    &structstring{"123"},
			want: sizeofString + 3,
		},
		{
			name: "slices_samearray",
			v: func() *[3][]byte {
				backarray := [64]byte{}
				return &[3][]byte{
					backarray[16:],
					backarray[4:16],
					backarray[0:4],
				}
			}(),
			want: 3*sizeofSlice + 64,
		},
		{
			name: "slices_nil",
			v: func() *[2][]byte {
				return &[2][]byte{nil, nil}
			}(),
			want: 2 * sizeofSlice,
		},
		{
			name: "slices_overlap_total",
			v: func() *[2][]byte {
				backarray := [32]byte{}
				return &[2][]byte{backarray[:], backarray[:]}
			}(),
			want: 2*sizeofSlice + 32,
		},
		{
			name: "slices_overlap",
			v: func() *[4][]uint16 {
				backarray := [32]uint16{}
				return &[4][]uint16{
					backarray[2:4],
					backarray[10:12],
					backarray[20:25],
					backarray[:],
				}
			}(),
			want: 4*sizeofSlice + 32*2,
		},
		{
			name: "slices_overlap_array",
			v: func() *struct {
				a [32]byte
				s [2][]byte
			} {
				v := struct {
					a [32]byte
					s [2][]byte
				}{}
				v.s[0] = v.a[2:4]
				v.s[1] = v.a[5:8]
				return &v
			}(),
			want: 32 + 2*sizeofSlice,
		},
		{
			name: "interface",
			v:    &[2]interface{}{uint64(0), &struct16{}},
			want: 2*sizeofInterface + 8 + 16,
		},
		{
			name: "interface_nil",
			v:    &[2]interface{}{nil, nil},
			want: 2 * sizeofInterface,
		},
	}
	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			var rs memsize.RootSet
			rs.Add("test", test.v)
			size := rs.Scan()
			if size.Total() != test.want {
				t.Errorf("total=%d, want %d", size.Total(), test.want)
				t.Logf("\n%s", size.Report())

			}
		})
	}
}
<commit_msg>memsize: add failing test<commit_after>package memsize_test

import (
	"testing"
	"unsafe"

	"github.com/fjl/memsize"
)

const (
	sizeofSlice     = unsafe.Sizeof([]byte{})
	sizeofMap       = unsafe.Sizeof(map[string]string{})
	sizeofInterface = unsafe.Sizeof((interface{})(nil))
	sizeofString    = unsafe.Sizeof("")
)

type (
	struct16 struct {
		x, y uint64
	}
	struct32ptr struct {
		x   uint32
		cld *struct32ptr
	}
	struct64array struct {
		array64
	}
	structslice struct {
		s []uint32
	}
	structstring struct {
		s string
	}
	structloop struct {
		s *structloop
	}
	array64 [64]byte
)

func TestTotal(t *testing.T) {
	tests := []struct {
		name string
		v    interface{}
		want uintptr
	}{
		{
			name: "struct16",
			v:    &struct16{},
			want: 16,
		},
		{
			name: "struct32ptr_nil",
			v:    &struct32ptr{},
			want: 16,
		},
		{
			name: "struct32ptr",
			v:    &struct32ptr{cld: &struct32ptr{}},
			want: 32,
		},
		{
			name: "struct32ptr_loop",
			v: func() *struct32ptr {
				v := &struct32ptr{}
				v.cld = v
				return v
			}(),
			want: 16,
		},
		{
			name: "struct64array",
			v:    &struct64array{},
			want: 64,
		},
		{
			name: "structslice",
			v:    &structslice{s: []uint32{1, 2, 3}},
			want: sizeofSlice + 3*4,
		},
		{
			name: "structloop",
			v: func() *structloop {
				v := new(structloop)
				v.s = v
				return v
			}(),
			want: 64,
		},
		{
			name: "array64",
			v:    &array64{},
			want: 64,
		},
		{
			name: "byteslice",
			v:    &[]byte{1, 2, 3},
			want: 27,
		},
		{
			name: "slice3_ptrval",
			v:    &[]*struct16{{}, {}, {}},
			want: 96,
		},
		{
			name: "map3",
			v:    &map[uint64]uint64{1: 1, 2: 2, 3: 3},
			want: 56,
		},
		{
			name: "map3_ptrval",
			v:    &map[uint64]*struct16{1: {}, 2: {}, 3: {}},
			want: 104,
		},
		{
			name: "map3_ptrkey",
			v:    &map[*struct16]uint64{{x: 1}: 1, {x: 2}: 2, {x: 3}: 3},
			want: 104,
		},
		{
			name: "pointerpointer",
			v: func() **uint64 {
				i := uint64(0)
				p := &i
				return &p
			}(),
			want: 16,
		},
		{
			name: "structstring",
			v:    &structstring{"123"},
			want: sizeofString + 3,
		},
		{
			name: "slices_samearray",
			v: func() *[3][]byte {
				backarray := [64]byte{}
				return &[3][]byte{
					backarray[16:],
					backarray[4:16],
					backarray[0:4],
				}
			}(),
			want: 3*sizeofSlice + 64,
		},
		{
			name: "slices_nil",
			v: func() *[2][]byte {
				return &[2][]byte{nil, nil}
			}(),
			want: 2 * sizeofSlice,
		},
		{
			name: "slices_overlap_total",
			v: func() *[2][]byte {
				backarray := [32]byte{}
				return &[2][]byte{backarray[:], backarray[:]}
			}(),
			want: 2*sizeofSlice + 32,
		},
		{
			name: "slices_overlap",
			v: func() *[4][]uint16 {
				backarray := [32]uint16{}
				return &[4][]uint16{
					backarray[2:4],
					backarray[10:12],
					backarray[20:25],
					backarray[:],
				}
			}(),
			want: 4*sizeofSlice + 32*2,
		},
		{
			name: "slices_overlap_array",
			v: func() *struct {
				a [32]byte
				s [2][]byte
			} {
				v := struct {
					a [32]byte
					s [2][]byte
				}{}
				v.s[0] = v.a[2:4]
				v.s[1] = v.a[5:8]
				return &v
			}(),
			want: 32 + 2*sizeofSlice,
		},
		{
			name: "interface",
			v:    &[2]interface{}{uint64(0), &struct16{}},
			want: 2*sizeofInterface + 8 + 16,
		},
		{
			name: "interface_nil",
			v:    &[2]interface{}{nil, nil},
			want: 2 * sizeofInterface,
		},
	}
	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			var rs memsize.RootSet
			rs.Add("test", test.v)
			size := rs.Scan()
			if size.Total() != test.want {
				t.Errorf("total=%d, want %d", size.Total(), test.want)
				t.Logf("\n%s", size.Report())

			}
		})
	}
}
<|endoftext|>


<commit_before>package xhyve

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/docker/machine/libmachine/log"
)

var (
	//	ErrMachineExist     = errors.New("machine already exists")
	//	ErrMachineNotExist  = errors.New("machine does not exist")
	ErrDdNotFound      = errors.New("xhyve not found")
	ErrUuidgenNotFound = errors.New("uuidgen not found")
	//	ErrUuid2macNotFound = errors.New("uuid2mac not found")
	ErrHdiutilNotFound = errors.New("hdiutil not found")
	ErrVBMNotFound     = errors.New("VBoxManage not found")
	vboxManageCmd      = setVBoxManageCmd()
)

func uuidgen() string {
	cmd := exec.Command("uuidgen")

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	log.Debugf("executing: %v", cmd)

	err := cmd.Run()
	if err != nil {
		if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
			err = ErrUuidgenNotFound
		}
	}

	out := stdout.String()
	return strings.Replace(out, "\n", "", 1)
}

func hdiutil(args ...string) error {
	cmd := exec.Command("hdiutil", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	log.Debugf("executing: %v %v", cmd, strings.Join(args, " "))

	err := cmd.Run()
	if err != nil {
		log.Error(err)
	}

	return nil
}

// detect the VBoxManage cmd's path if needed
func setVBoxManageCmd() string {
	cmd := "VBoxManage"
	path, err := exec.LookPath(cmd)
	if err != nil {
		return ""
	} else if err == nil {
		return path
	}
	if runtime.GOOS == "windows" {
		if p := os.Getenv("VBOX_INSTALL_PATH"); p != "" {
			if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil {
				return path
			}
		}
		if p := os.Getenv("VBOX_MSI_INSTALL_PATH"); p != "" {
			if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil {
				return path
			}
		}
		// look at HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\VirtualBox\InstallDir
		p := "C:\\Program Files\\Oracle\\VirtualBox"
		if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil {
			return path
		}
	}
	return cmd
}

func vbm(args ...string) error {
	_, _, err := vbmOutErr(args...)
	return err
}

func vbmOut(args ...string) (string, error) {
	stdout, _, err := vbmOutErr(args...)
	return stdout, err
}

func vbmOutErr(args ...string) (string, string, error) {
	cmd := exec.Command(vboxManageCmd, args...)
	log.Debugf("executing: %v %v", vboxManageCmd, strings.Join(args, " "))
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	stderrStr := stderr.String()
	log.Debugf("STDOUT: %v", stdout.String())
	log.Debugf("STDERR: %v", stderrStr)
	if err != nil {
		if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
			err = ErrVBMNotFound
		}
	} else {
		// VBoxManage will sometimes not set the return code, but has a fatal error
		// such as VBoxManage.exe: error: VT-x is not available. (VERR_VMX_NO_VMX)
		if strings.Contains(stderrStr, "error:") {
			err = fmt.Errorf("%v %v failed: %v", vboxManageCmd, strings.Join(args, " "), stderrStr)
		}
	}
	return stdout.String(), stderrStr, err
}

func vboxVersionDetect() (string, error) {
	if vboxManageCmd == "" {
		return "", nil
	}
	ver, err := vbmOut("-v")
	if err != nil {
		return "", err
	}
	return ver, err
}

func toPtr(s string) *string {
	return &s
}
<commit_msg>Cleanup unused code<commit_after>package xhyve

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/docker/machine/libmachine/log"
)

var (
	ErrDdNotFound      = errors.New("xhyve not found")
	ErrUuidgenNotFound = errors.New("uuidgen not found")
	ErrHdiutilNotFound = errors.New("hdiutil not found")
	ErrVBMNotFound     = errors.New("VBoxManage not found")
	vboxManageCmd      = setVBoxManageCmd()
)

func uuidgen() string {
	cmd := exec.Command("uuidgen")

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	log.Debugf("executing: %v", cmd)

	err := cmd.Run()
	if err != nil {
		if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
			err = ErrUuidgenNotFound
		}
	}

	out := stdout.String()
	return strings.Replace(out, "\n", "", 1)
}

func hdiutil(args ...string) error {
	cmd := exec.Command("hdiutil", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	log.Debugf("executing: %v %v", cmd, strings.Join(args, " "))

	err := cmd.Run()
	if err != nil {
		log.Error(err)
	}

	return nil
}

// detect the VBoxManage cmd's path if needed
func setVBoxManageCmd() string {
	cmd := "VBoxManage"
	path, err := exec.LookPath(cmd)
	if err != nil {
		return ""
	} else if err == nil {
		return path
	}
	if runtime.GOOS == "windows" {
		if p := os.Getenv("VBOX_INSTALL_PATH"); p != "" {
			if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil {
				return path
			}
		}
		if p := os.Getenv("VBOX_MSI_INSTALL_PATH"); p != "" {
			if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil {
				return path
			}
		}
		// look at HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\VirtualBox\InstallDir
		p := "C:\\Program Files\\Oracle\\VirtualBox"
		if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil {
			return path
		}
	}
	return cmd
}

func vbm(args ...string) error {
	_, _, err := vbmOutErr(args...)
	return err
}

func vbmOut(args ...string) (string, error) {
	stdout, _, err := vbmOutErr(args...)
	return stdout, err
}

func vbmOutErr(args ...string) (string, string, error) {
	cmd := exec.Command(vboxManageCmd, args...)
	log.Debugf("executing: %v %v", vboxManageCmd, strings.Join(args, " "))
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	stderrStr := stderr.String()
	log.Debugf("STDOUT: %v", stdout.String())
	log.Debugf("STDERR: %v", stderrStr)
	if err != nil {
		if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
			err = ErrVBMNotFound
		}
	} else {
		// VBoxManage will sometimes not set the return code, but has a fatal error
		// such as VBoxManage.exe: error: VT-x is not available. (VERR_VMX_NO_VMX)
		if strings.Contains(stderrStr, "error:") {
			err = fmt.Errorf("%v %v failed: %v", vboxManageCmd, strings.Join(args, " "), stderrStr)
		}
	}
	return stdout.String(), stderrStr, err
}

func vboxVersionDetect() (string, error) {
	if vboxManageCmd == "" {
		return "", nil
	}
	ver, err := vbmOut("-v")
	if err != nil {
		return "", err
	}
	return ver, err
}

func toPtr(s string) *string {
	return &s
}
<|endoftext|>