<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"

	"encoding/json"
	"io/ioutil"
	"net/url"
	"os/user"
	"path/filepath"

	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/console"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/utils/log"
)

const (
	mcConfigDir        = ".mc/"
	mcConfigWindowsDir = "mc/"
	configFile         = "config.json"
)

type auth struct {
	AccessKeyID     string
	SecretAccessKey string
}

type hostConfig struct {
	Auth auth
}

type mcConfig struct {
	Version uint
	Hosts   map[string]hostConfig
	Aliases map[string]string
}

const (
	currentConfigVersion = 1
)

// Global config data loaded from json config file durlng init(). This variable should only
// be accessed via getMcConfig()
var _config *mcConfig

func getMcConfigDir() (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	var p string
	// For windows the path is slightly differently
	if runtime.GOOS == "windows" {
		p = path.Join(u.HomeDir, mcConfigWindowsDir)
	} else {
		p = path.Join(u.HomeDir, mcConfigDir)
	}
	return p, nil
}
func getOrCreateMcConfigDir() (string, error) {
	p, err := getMcConfigDir()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	err = os.MkdirAll(p, 0700)
	if err != nil {
		return "", iodine.New(err, nil)
	}
	return p, nil
}

func getMcConfigPath() (string, error) {
	dir, err := getMcConfigDir()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	return path.Join(dir, configFile), nil
}

func mustGetMcConfigPath() string {
	p, _ := getMcConfigPath()
	return p
}

// getMcConfig returns the config data from file. Subsequent calls are
// cached in a private global variable
func getMcConfig() (cfg *mcConfig, err error) {
	if _config != nil {
		return _config, nil
	}

	_config, err = loadMcConfig()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	return _config, nil
}

// getMcConfig returns the config data from file. Subsequent calls are
// cached in a private global variable
func isMcConfigExist() bool {
	configFile, err := getMcConfigPath()
	if err != nil {
		return false
	}
	_, err = os.Stat(configFile)
	if err != nil {
		return false
	}
	return true
}

// chechMcConfig checks for errors in config file
func checkMcConfig(config *mcConfig) (err error) {
	// check for version
	switch {
	case (config.Version != currentConfigVersion):
		err := fmt.Errorf("Unsupported version [%d]. Current operating version is [%d]", config.Version, currentConfigVersion)
		return iodine.New(err, nil)

	case len(config.Hosts) > 1:
		for host, hostCfg := range config.Hosts {
			if host == "" {
				return iodine.New(fmt.Errorf("Empty host URL"), nil)
			}
			if hostCfg.Auth.AccessKeyID == "" {
				return iodine.New(fmt.Errorf("AccessKeyID is empty for Host [%s]", host), nil)
			}
			if hostCfg.Auth.SecretAccessKey == "" {
				return iodine.New(fmt.Errorf("SecretAccessKey is empty for Host [%s]", host), nil)
			}
		}
	case len(config.Aliases) > 0:
		for aliasName, aliasURL := range config.Aliases {
			_, err := url.Parse(aliasURL)
			if err != nil {
				return iodine.New(fmt.Errorf("Unable to parse URL [%s] for alias [%s]", aliasURL, aliasName), nil)
			}
			if !isValidAliasName(aliasName) {
				return iodine.New(fmt.Errorf("Not a valid alias name [%s]. Valid examples are: Area51, Grand-Nagus..", aliasName), nil)
			}
		}
	}
	return nil
}

// loadMcConfig decodes json configuration file to mcConfig structure
func loadMcConfig() (config *mcConfig, err error) {
	configFile, err := getMcConfigPath()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	_, err = os.Stat(configFile)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	return config, nil
}

// saveConfig writes configuration data in json format to config file.
func saveConfig(ctx *cli.Context) error {
	configData, err := parseConfigInput(ctx)
	if err != nil {
		return iodine.New(err, nil)
	}

	jsonConfig, err := json.MarshalIndent(configData, "", "\t")
	if err != nil {
		return iodine.New(err, nil)
	}

	_, err = getOrCreateMcConfigDir()
	if err != nil {
		return iodine.New(err, nil)
	}

	configPath, err := getMcConfigPath()
	if err != nil {
		return iodine.New(err, nil)
	}

	configFile, err := os.OpenFile(configPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
	if err != nil {
		return iodine.New(err, nil)
	}

	_, err = configFile.Write(jsonConfig)
	if err != nil {
		configFile.Close()
		return iodine.New(err, nil)
	}

	configFile.Close()

	// Invalidate cached config
	_config = nil

	// Reload and cache new config
	_, err = getMcConfig()
	if os.IsNotExist(iodine.ToError(err)) {
		return iodine.New(err, nil)
	}

	return nil
}

func parseConfigInput(c *cli.Context) (config *mcConfig, err error) {
	accessKeyID := c.String("accesskeyid")
	secretAccesskey := c.String("secretkey")

	if accessKeyID == "" {
		accessKeyID = "YOUR-ACCESS-KEY-ID-HERE"
	}

	if secretAccesskey == "" {
		secretAccesskey = "YOUR-SECRET-ACCESS-KEY-HERE"
	}

	alias := strings.Fields(c.String("alias"))
	switch true {
	case len(alias) == 0:
		config = &mcConfig{
			Version: currentConfigVersion,
			Hosts: map[string]hostConfig{
				"http*://s3*.amazonaws.com": {
					Auth: auth{
						AccessKeyID:     accessKeyID,
						SecretAccessKey: secretAccesskey,
					}},
				"http*://localhost:*": {
					Auth: auth{
						AccessKeyID:     accessKeyID,
						SecretAccessKey: secretAccesskey,
					}},
			},
			Aliases: map[string]string{
				"s3":        "https://s3.amazonaws.com",
				"localhost": "http://localhost:9000",
			},
		}
		return config, nil
	case len(alias) == 2:
		aliasName := alias[0]
		url := alias[1]
		if strings.HasPrefix(aliasName, "http") {
			return nil, iodine.New(errors.New("invalid alias cannot use http{s}"), nil)
		}
		if !strings.HasPrefix(url, "http") {
			return nil, iodine.New(errors.New("invalid url type only supports http{s}"), nil)
		}
		config = &mcConfig{
			Version: currentConfigVersion,
			Hosts: map[string]hostConfig{
				"http*://s3*.amazonaws.com": {
					Auth: auth{
						AccessKeyID:     accessKeyID,
						SecretAccessKey: secretAccesskey,
					}},
			},
			Aliases: map[string]string{
				"s3":        "https://s3.amazonaws.com",
				"localhost": "http://localhost:9000",
				aliasName:   url,
			},
		}
		return config, nil
	default:
		return nil, iodine.New(errors.New("invalid number of arguments for --alias, requires exact 2"), nil)
	}
}

// getHostURL -
func getHostURL(u *url.URL) string {
	return u.Scheme + "://" + u.Host
}

// getHostConfig retrieves host specific configuration such as access keys, certs.
func getHostConfig(requestURL string) (*hostConfig, error) {
	u, err := url.Parse(requestURL)
	if err != nil {
		return nil, iodine.New(err, nil)

	}
	config, err := getMcConfig()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	for globURL, cfg := range config.Hosts {
		match, err := filepath.Match(globURL, getHostURL(u))
		if err != nil {
			msg := fmt.Errorf("Error parsing glob'ed URL while comparing [%s] [%s]", globURL, requestURL)
			return nil, iodine.New(msg, map[string]string{
				"globURL": globURL,
				"hostURL": requestURL,
			})
		}
		if match {
			var hostCfg hostConfig
			hostCfg.Auth.AccessKeyID = cfg.Auth.AccessKeyID
			hostCfg.Auth.SecretAccessKey = cfg.Auth.SecretAccessKey
			return &hostCfg, nil
		}
	}
	return nil, iodine.New(errors.New("No matching host config found"), nil)
}

//getBashCompletionCmd generates bash completion file.
// TODO don't kill, return an error instead. caller should kill, not this function
func getBashCompletionCmd() {
	var b bytes.Buffer
	if os.Getenv("SHELL") != "/bin/bash" {
		console.Fatalln("Unsupported shell for bash completion detected.. exiting")
	}
	b.WriteString(mcBashCompletion)
	f, _ := getMcBashCompletionFilename()
	// TODO uncomment when ready
	//	if err != nil {
	//		return err
	//	}
	fl, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	defer fl.Close()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("Unable to create bash completion file")
	}
	_, err = fl.Write(b.Bytes())
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("Unable to write bash completion file")
	}
	msg := "\nConfiguration written to " + f
	msg = msg + "\n\n$ source ${HOME}/.mc/mc.bash_completion\n"
	msg = msg + "$ echo 'source ${HOME}/.mc/mc.bash_completion' >> ${HOME}/.bashrc"
	console.Infoln(msg)
}

// saveConfigCmd writes config file to disk
func saveConfigCmd(ctx *cli.Context) {
	err := saveConfig(ctx)
	if os.IsExist(iodine.ToError(err)) {
		log.Debug.Println(iodine.New(err, nil))
		configPath, _ := getMcConfigPath()
		console.Fatalln("mc: Configuration file " + configPath + " already exists")
	}

	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		configPath, _ := getMcConfigPath()
		console.Fatalln("mc: Unable to generate config file", configPath)
	}
	configPath, err := getMcConfigPath()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to identify config file path")
	}
	console.Infof("Configuration written to " + configPath + ". Please update your access credentials.\n")
}

// doConfigCmd is the handler for "mc config" sub-command.
func doConfigCmd(ctx *cli.Context) {
	if len(ctx.Args()) < 1 {
		cli.ShowCommandHelpAndExit(ctx, "config", 1) // last argument is exit code
	}
	switch true {
	case ctx.Bool("completion") == true:
		getBashCompletionCmd()
	default:
		saveConfigCmd(ctx)
	}
}
<commit_msg>Using switch instead of if statement for path<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"

	"encoding/json"
	"io/ioutil"
	"net/url"
	"os/user"
	"path/filepath"

	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/console"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/utils/log"
)

const (
	mcConfigDir        = ".mc/"
	mcConfigWindowsDir = "mc/"
	configFile         = "config.json"
)

type auth struct {
	AccessKeyID     string
	SecretAccessKey string
}

type hostConfig struct {
	Auth auth
}

type mcConfig struct {
	Version uint
	Hosts   map[string]hostConfig
	Aliases map[string]string
}

const (
	currentConfigVersion = 1
)

// Global config data loaded from json config file durlng init(). This variable should only
// be accessed via getMcConfig()
var _config *mcConfig

func getMcConfigDir() (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	// For windows the path is slightly differently
	switch runtime.GOOS {
	case "windows":
		return path.Join(u.HomeDir, mcConfigWindowsDir), nil
	default:
		return path.Join(u.HomeDir, mcConfigDir), nil
	}
}
func getOrCreateMcConfigDir() (string, error) {
	p, err := getMcConfigDir()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	err = os.MkdirAll(p, 0700)
	if err != nil {
		return "", iodine.New(err, nil)
	}
	return p, nil
}

func getMcConfigPath() (string, error) {
	dir, err := getMcConfigDir()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	return path.Join(dir, configFile), nil
}

func mustGetMcConfigPath() string {
	p, _ := getMcConfigPath()
	return p
}

// getMcConfig returns the config data from file. Subsequent calls are
// cached in a private global variable
func getMcConfig() (cfg *mcConfig, err error) {
	if _config != nil {
		return _config, nil
	}

	_config, err = loadMcConfig()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	return _config, nil
}

// getMcConfig returns the config data from file. Subsequent calls are
// cached in a private global variable
func isMcConfigExist() bool {
	configFile, err := getMcConfigPath()
	if err != nil {
		return false
	}
	_, err = os.Stat(configFile)
	if err != nil {
		return false
	}
	return true
}

// chechMcConfig checks for errors in config file
func checkMcConfig(config *mcConfig) (err error) {
	// check for version
	switch {
	case (config.Version != currentConfigVersion):
		err := fmt.Errorf("Unsupported version [%d]. Current operating version is [%d]", config.Version, currentConfigVersion)
		return iodine.New(err, nil)

	case len(config.Hosts) > 1:
		for host, hostCfg := range config.Hosts {
			if host == "" {
				return iodine.New(fmt.Errorf("Empty host URL"), nil)
			}
			if hostCfg.Auth.AccessKeyID == "" {
				return iodine.New(fmt.Errorf("AccessKeyID is empty for Host [%s]", host), nil)
			}
			if hostCfg.Auth.SecretAccessKey == "" {
				return iodine.New(fmt.Errorf("SecretAccessKey is empty for Host [%s]", host), nil)
			}
		}
	case len(config.Aliases) > 0:
		for aliasName, aliasURL := range config.Aliases {
			_, err := url.Parse(aliasURL)
			if err != nil {
				return iodine.New(fmt.Errorf("Unable to parse URL [%s] for alias [%s]", aliasURL, aliasName), nil)
			}
			if !isValidAliasName(aliasName) {
				return iodine.New(fmt.Errorf("Not a valid alias name [%s]. Valid examples are: Area51, Grand-Nagus..", aliasName), nil)
			}
		}
	}
	return nil
}

// loadMcConfig decodes json configuration file to mcConfig structure
func loadMcConfig() (config *mcConfig, err error) {
	configFile, err := getMcConfigPath()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	_, err = os.Stat(configFile)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	return config, nil
}

// saveConfig writes configuration data in json format to config file.
func saveConfig(ctx *cli.Context) error {
	configData, err := parseConfigInput(ctx)
	if err != nil {
		return iodine.New(err, nil)
	}

	jsonConfig, err := json.MarshalIndent(configData, "", "\t")
	if err != nil {
		return iodine.New(err, nil)
	}

	_, err = getOrCreateMcConfigDir()
	if err != nil {
		return iodine.New(err, nil)
	}

	configPath, err := getMcConfigPath()
	if err != nil {
		return iodine.New(err, nil)
	}

	configFile, err := os.OpenFile(configPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
	if err != nil {
		return iodine.New(err, nil)
	}

	_, err = configFile.Write(jsonConfig)
	if err != nil {
		configFile.Close()
		return iodine.New(err, nil)
	}

	configFile.Close()

	// Invalidate cached config
	_config = nil

	// Reload and cache new config
	_, err = getMcConfig()
	if os.IsNotExist(iodine.ToError(err)) {
		return iodine.New(err, nil)
	}

	return nil
}

func parseConfigInput(c *cli.Context) (config *mcConfig, err error) {
	accessKeyID := c.String("accesskeyid")
	secretAccesskey := c.String("secretkey")

	if accessKeyID == "" {
		accessKeyID = "YOUR-ACCESS-KEY-ID-HERE"
	}

	if secretAccesskey == "" {
		secretAccesskey = "YOUR-SECRET-ACCESS-KEY-HERE"
	}

	alias := strings.Fields(c.String("alias"))
	switch true {
	case len(alias) == 0:
		config = &mcConfig{
			Version: currentConfigVersion,
			Hosts: map[string]hostConfig{
				"http*://s3*.amazonaws.com": {
					Auth: auth{
						AccessKeyID:     accessKeyID,
						SecretAccessKey: secretAccesskey,
					}},
				"http*://localhost:*": {
					Auth: auth{
						AccessKeyID:     accessKeyID,
						SecretAccessKey: secretAccesskey,
					}},
			},
			Aliases: map[string]string{
				"s3":        "https://s3.amazonaws.com",
				"localhost": "http://localhost:9000",
			},
		}
		return config, nil
	case len(alias) == 2:
		aliasName := alias[0]
		url := alias[1]
		if strings.HasPrefix(aliasName, "http") {
			return nil, iodine.New(errors.New("invalid alias cannot use http{s}"), nil)
		}
		if !strings.HasPrefix(url, "http") {
			return nil, iodine.New(errors.New("invalid url type only supports http{s}"), nil)
		}
		config = &mcConfig{
			Version: currentConfigVersion,
			Hosts: map[string]hostConfig{
				"http*://s3*.amazonaws.com": {
					Auth: auth{
						AccessKeyID:     accessKeyID,
						SecretAccessKey: secretAccesskey,
					}},
			},
			Aliases: map[string]string{
				"s3":        "https://s3.amazonaws.com",
				"localhost": "http://localhost:9000",
				aliasName:   url,
			},
		}
		return config, nil
	default:
		return nil, iodine.New(errors.New("invalid number of arguments for --alias, requires exact 2"), nil)
	}
}

// getHostURL -
func getHostURL(u *url.URL) string {
	return u.Scheme + "://" + u.Host
}

// getHostConfig retrieves host specific configuration such as access keys, certs.
func getHostConfig(requestURL string) (*hostConfig, error) {
	u, err := url.Parse(requestURL)
	if err != nil {
		return nil, iodine.New(err, nil)

	}
	config, err := getMcConfig()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	for globURL, cfg := range config.Hosts {
		match, err := filepath.Match(globURL, getHostURL(u))
		if err != nil {
			msg := fmt.Errorf("Error parsing glob'ed URL while comparing [%s] [%s]", globURL, requestURL)
			return nil, iodine.New(msg, map[string]string{
				"globURL": globURL,
				"hostURL": requestURL,
			})
		}
		if match {
			var hostCfg hostConfig
			hostCfg.Auth.AccessKeyID = cfg.Auth.AccessKeyID
			hostCfg.Auth.SecretAccessKey = cfg.Auth.SecretAccessKey
			return &hostCfg, nil
		}
	}
	return nil, iodine.New(errors.New("No matching host config found"), nil)
}

//getBashCompletionCmd generates bash completion file.
// TODO don't kill, return an error instead. caller should kill, not this function
func getBashCompletionCmd() {
	var b bytes.Buffer
	if os.Getenv("SHELL") != "/bin/bash" {
		console.Fatalln("Unsupported shell for bash completion detected.. exiting")
	}
	b.WriteString(mcBashCompletion)
	f, _ := getMcBashCompletionFilename()
	// TODO uncomment when ready
	//	if err != nil {
	//		return err
	//	}
	fl, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	defer fl.Close()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("Unable to create bash completion file")
	}
	_, err = fl.Write(b.Bytes())
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("Unable to write bash completion file")
	}
	msg := "\nConfiguration written to " + f
	msg = msg + "\n\n$ source ${HOME}/.mc/mc.bash_completion\n"
	msg = msg + "$ echo 'source ${HOME}/.mc/mc.bash_completion' >> ${HOME}/.bashrc"
	console.Infoln(msg)
}

// saveConfigCmd writes config file to disk
func saveConfigCmd(ctx *cli.Context) {
	err := saveConfig(ctx)
	if os.IsExist(iodine.ToError(err)) {
		log.Debug.Println(iodine.New(err, nil))
		configPath, _ := getMcConfigPath()
		console.Fatalln("mc: Configuration file " + configPath + " already exists")
	}

	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		configPath, _ := getMcConfigPath()
		console.Fatalln("mc: Unable to generate config file", configPath)
	}
	configPath, err := getMcConfigPath()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to identify config file path")
	}
	console.Infof("Configuration written to " + configPath + ". Please update your access credentials.\n")
}

// doConfigCmd is the handler for "mc config" sub-command.
func doConfigCmd(ctx *cli.Context) {
	if len(ctx.Args()) < 1 {
		cli.ShowCommandHelpAndExit(ctx, "config", 1) // last argument is exit code
	}
	switch true {
	case ctx.Bool("completion") == true:
		getBashCompletionCmd()
	default:
		saveConfigCmd(ctx)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/s3git/s3git-go"
	"github.com/spf13/cobra"
)

// statusCmd represents the status command
var statusCmd = &cobra.Command{
	Use:   "status",
	Short: "Show changes to repository",
	Long: "Show changes to repository",
	Run: func(cmd *cobra.Command, args []string) {

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			fmt.Println(err)
		}

		list, err := repo.Status()
		if err != nil {
			er(err)
		}

		for elem := range list {
			fmt.Println(elem)
		}

	},
}

func init() {
	RootCmd.AddCommand(statusCmd)
}
<commit_msg>List status or staging dir clean<commit_after>package cmd

import (
	"fmt"

	"github.com/s3git/s3git-go"
	"github.com/spf13/cobra"
)

// statusCmd represents the status command
var statusCmd = &cobra.Command{
	Use:   "status",
	Short: "Show changes to repository",
	Long: "Show changes to repository",
	Run: func(cmd *cobra.Command, args []string) {

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			fmt.Println(err)
		}

		list, err := repo.Status()
		if err != nil {
			er(err)
		}

		elements := 0
		for elem := range list {
			if elements == 0 {
				fmt.Println("Changes to be committed:")
			}
			fmt.Println("\t", elem)
			elements++
		}

		if elements == 0 {
			fmt.Println("Nothing to commit, staging directory clean")
		}
	},
}

func init() {
	RootCmd.AddCommand(statusCmd)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
)

var cmdRemove = &Command{
	Run:       runRemove,
	UsageLine: "remove NAME",
	Short:     "Remove saved password",
	Long:      `Remove saved password by input name.`,
}

func runRemove(ctx context, args []string) error {
	if len(args) == 0 {
		return errors.New("item name is required")
	}
	cfg, err := GetConfig()
	if err != nil {
		return err
	}
	Initialize(cfg)
	key, err := GetKey(cfg.KeyFile)
	if err != nil {
		return err
	}
	is, err := LoadItems(key, cfg.DataFile)
	if err != nil {
		return err
	}

	name := args[0]
	fit := is.Find(name)
	if fit == nil {
		return fmt.Errorf("item not found: %s", name)
	}

	nis := Items([]Item{})
	for _, it := range is {
		if it.Name != fit.Name {
			nis = append(nis, it)
		}
	}
	nis.Save(key, cfg.DataFile)
	PrintSuccess(ctx.out, "password of '%s' is removed successfully", name)
	return nil
}
<commit_msg>if master password is registered, remove subcommand requires master password<commit_after>package main

import (
	"errors"
	"fmt"
)

var cmdRemove = &Command{
	Run:       runRemove,
	UsageLine: "remove NAME",
	Short:     "Remove saved password",
	Long:      `Remove saved password by input name.`,
}

func runRemove(ctx context, args []string) error {
	if len(args) == 0 {
		return errors.New("item name is required")
	}
	cfg, err := GetConfig()
	if err != nil {
		return err
	}
	Initialize(cfg)
	key, err := GetKey(cfg.KeyFile)
	if err != nil {
		return err
	}
	is, err := LoadItems(key, cfg.DataFile)
	if err != nil {
		return err
	}

	if is.HasMaster() {
		if err = confirmMasterPassword(is.Master()); err != nil {
			return err
		}
	}

	name := args[0]
	fit := is.Find(name)
	if fit == nil {
		return fmt.Errorf("item not found: %s", name)
	}

	nis := Items([]Item{})
	for _, it := range is {
		if it.Name != fit.Name {
			nis = append(nis, it)
		}
	}
	nis.Save(key, cfg.DataFile)
	PrintSuccess(ctx.out, "password of '%s' is removed successfully", name)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

type Opencnams struct {
	Results []Opencnam `json:"results"`
}

type Opencnam struct {
	Number string `json:"number"`
	Name   string `json:"name"`
}

func cnamReq(phonenum, sid, token string) (Opencnam, error) {
	oc := Opencnam{}
	resp, err := http.Get("https://api.opencnam.com/v2/phone/+" + phonenum + "?format=json&account_sid=" + sid + "&auth_token=" + token)
	if err != nil {
		return oc, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return oc, err
	}
	err = json.Unmarshal(data, &oc)
	if err != nil {
		return oc, err
	}
	oc.Number = strings.TrimLeft(oc.Number, "+")
	return oc, nil
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

func main() {
	phone := flag.String("phone", "", "a single phone number")
	phoneFile := flag.String("file", "", "a list of phone numbers")
	sid := flag.String("sid", "", "the opencnam api sid")
	token := flag.String("token", "", "the opencnam api auth token")
	flag.Parse()

	requests := []string{}
	cnams := &Opencnams{}

	if (*phone != "") && (*phoneFile != "") {
		log.Fatal("-phone and -file are mutually exclusive")
	}
	if *sid == "" {
		log.Fatal("an opencnam sid is required")
	}
	if *token == "" {
		log.Fatal("an opencnam auth token is required")
	}
	if *phone != "" {
		requests = append(requests, *phone)
	}
	if *phoneFile != "" {
		lines, err := readLines(*phoneFile)
		if err != nil {
			log.Fatalf("readLines: %s", err)
		}
		requests = append(requests, lines...)
	}
	for _, r := range requests {
		result, err := cnamReq(r, *sid, *token)
		if err != nil {
			log.Fatal(err)
		}
		cnams.Results = append(cnams.Results, result)
	}
	j, err := json.MarshalIndent(cnams, "", "    ")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(j))
}
<commit_msg>Fixed the JSON EOF error returned on an emply slice<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

type Opencnams struct {
	Results []Opencnam `json:"results"`
}

type Opencnam struct {
	Number string `json:"number"`
	Name   string `json:"name"`
}

func cnamReq(phonenum, sid, token string) (Opencnam, error) {
	oc := Opencnam{}
	resp, err := http.Get("https://api.opencnam.com/v2/phone/+" + phonenum + "?format=json&account_sid=" + sid + "&auth_token=" + token)
	if err != nil {
		return oc, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if len(data) == 0 {
		oc.Number = phonenum
		oc.Name = "undefined"
		return oc, err
	}
	if err != nil {
		return oc, err
	}
	err = json.Unmarshal(data, &oc)
	if err != nil {
		return oc, err
	}
	oc.Number = strings.TrimLeft(oc.Number, "+")
	return oc, nil
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

func main() {
	phone := flag.String("phone", "", "a single phone number")
	phoneFile := flag.String("file", "", "a list of phone numbers")
	sid := flag.String("sid", "", "the opencnam api sid")
	token := flag.String("token", "", "the opencnam api auth token")
	flag.Parse()

	requests := []string{}
	cnams := &Opencnams{}

	if (*phone != "") && (*phoneFile != "") {
		log.Fatal("-phone and -file are mutually exclusive")
	}
	if *sid == "" {
		log.Fatal("an opencnam sid is required")
	}
	if *token == "" {
		log.Fatal("an opencnam auth token is required")
	}
	if *phone != "" {
		requests = append(requests, *phone)
	}
	if *phoneFile != "" {
		lines, err := readLines(*phoneFile)
		if err != nil {
			log.Fatalf("readLines: %s", err)
		}
		requests = append(requests, lines...)
	}
	for _, r := range requests {
		result, err := cnamReq(r, *sid, *token)
		if err != nil {
			log.Fatal(err)
		}
		cnams.Results = append(cnams.Results, result)
	}
	j, err := json.MarshalIndent(cnams, "", "    ")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(j))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	pbs "github.com/brotherlogic/goserver/proto"
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	runner *Runner
	disk   diskChecker
}

func getHash(file string) (string, error) {
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	if len(home) == 0 {
		log.Printf("Error in home: %v", home)
	}
	gpath := home + "/gobuild"

	f, err := os.Open(strings.Replace(file, "$GOPATH", gpath, 1))
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	if _, err := io.Copy(h, f); err != nil {
		return "", err
	}

	return string(h.Sum(nil)), nil
}

func getIP(name string, server string) (string, int) {
	conn, _ := grpc.Dial("192.168.86.64:50055", grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	entry := pbd.RegistryEntry{Name: name, Identifier: server}
	r, err := registry.Discover(context.Background(), &entry)

	if err != nil {
		log.Printf("Lookup failed for %v,%v -> %v", name, server, err)
		return "", -1
	}

	return r.Ip, int(r.Port)
}

// updateState of the runner command
func updateState(com *runnerCommand) {
	elems := strings.Split(com.details.Spec.Name, "/")
	dServer, dPort := getIP(elems[len(elems)-1], com.details.Spec.Server)

	log.Printf("Unable to find: %v -> %v", elems, dPort)
	if dPort > 0 {
		dConn, err := grpc.Dial(dServer+":"+strconv.Itoa(dPort), grpc.WithInsecure())
		if err != nil {
			panic(err)
		}
		defer dConn.Close()

		c := pbs.NewGoserverServiceClient(dConn)
		_, err = c.IsAlive(context.Background(), &pbs.Alive{})
		log.Printf("UPDATED: %v,%v -> %v", dServer, dPort, err)
		com.details.Running = (err == nil)
	} else {
		//Mark as false if we can't locate the job
		com.details.Running = false
	}
}

// BuildJob builds out a job
func (s *Server) BuildJob(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.Checkout(in.Name)
	return &pb.Empty{}, nil
}

// List lists all running jobs
func (s *Server) List(ctx context.Context, in *pb.Empty) (*pb.JobList, error) {
	details := &pb.JobList{}
	for _, job := range s.runner.backgroundTasks {
		updateState(job)
		details.Details = append(details.Details, job.details)
	}

	return details, nil
}

// Run runs a background task
func (s *Server) Run(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.Run(in)
	return &pb.Empty{}, nil
}

//Update restarts a job with new settings
func (s *Server) Update(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.Update(in)
	return &pb.Empty{}, nil
}

// Kill a background task
func (s *Server) Kill(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.kill(in)
	return &pb.Empty{}, nil
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildSlaveServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//Init builds the default runner framework
func Init() *Runner {
	r := &Runner{gopath: "goautobuild"}
	r.runner = runCommand
	go r.run()
	return r
}

func runCommand(c *runnerCommand) {
	log.Printf("RUNNING COMMAND: %v", c)

	if c.command == nil {
		return
	}

	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	if len(home) == 0 {
		log.Printf("Error in home: %v", home)
	}
	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}

	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	found := false
	log.Printf("HERE = %v", c.command.Env)
	envl := os.Environ()
	for i, blah := range envl {
		if strings.HasPrefix(blah, "GOPATH") {
			envl[i] = path
			found = true
		}
	}
	if !found {
		envl = append(envl, path)
	}
	log.Printf("ENV = %v", envl)
	c.command.Env = envl

	out, err := c.command.StdoutPipe()
	out2, err2 := c.command.StderrPipe()
	if err != nil {
		log.Printf("Blah: %v", err)
	}

	if err2 != nil {
		log.Printf("Blah2: %v", err)
	}

	log.Printf("%v, %v and %v", c.command.Path, c.command.Args, c.command.Env)
	c.command.Start()

	if !c.background {
		str := ""

		if out != nil {
			buf := new(bytes.Buffer)
			buf.ReadFrom(out)
			str = buf.String()
		}

		if out2 != nil {
			buf2 := new(bytes.Buffer)
			buf2.ReadFrom(out2)
			str2 := buf2.String()
			log.Printf("%v and %v", str, str2)

		}
		c.command.Wait()
		c.output = str
		c.complete = true
	}
	log.Printf("DONE")
}

func (diskChecker prodDiskChecker) diskUsage(path string) int64 {
	return diskUsage(path)
}

func (s *Server) rebuildLoop() {
	for true {
		time.Sleep(time.Minute)

		var rebuildList []*pb.JobSpec
		var hashList []string
		for _, job := range s.runner.backgroundTasks {
			log.Printf("Job (started %v, now %v) %v", job.started, time.Now(), job)
			if time.Since(job.started) > time.Hour {
				log.Printf("Added to rebuild list (%v)", job)
				rebuildList = append(rebuildList, job.details.Spec)
				hashList = append(hashList, job.hash)
			}
		}

		log.Printf("Rebuilding %v", rebuildList)
		for i := range rebuildList {
			s.runner.Rebuild(rebuildList[i], hashList[i])
		}
	}
}

func main() {
	var quiet = flag.Bool("quiet", true, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	s := Server{&goserver.GoServer{}, Init(), prodDiskChecker{}}
	s.Register = s
	s.PrepServer()
	s.RegisterServingTask(s.rebuildLoop)
	s.RegisterServer("gobuildslave", false)
	s.Serve()
}
<commit_msg>Don't kill us<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	pbs "github.com/brotherlogic/goserver/proto"
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	runner *Runner
	disk   diskChecker
}

func getHash(file string) (string, error) {
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	if len(home) == 0 {
		log.Printf("Error in home: %v", home)
	}
	gpath := home + "/gobuild"

	f, err := os.Open(strings.Replace(file, "$GOPATH", gpath, 1))
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	if _, err := io.Copy(h, f); err != nil {
		return "", err
	}

	return string(h.Sum(nil)), nil
}

func getIP(name string, server string) (string, int) {
	conn, _ := grpc.Dial("192.168.86.64:50055", grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	entry := pbd.RegistryEntry{Name: name, Identifier: server}
	r, err := registry.Discover(context.Background(), &entry)

	if err != nil {
		log.Printf("Lookup failed for %v,%v -> %v", name, server, err)
		return "", -1
	}

	return r.Ip, int(r.Port)
}

// updateState of the runner command
func updateState(com *runnerCommand) {
	elems := strings.Split(com.details.Spec.Name, "/")
	dServer, dPort := getIP(elems[len(elems)-1], com.details.Spec.Server)

	log.Printf("Unable to find: %v -> %v", elems, dPort)
	if dPort > 0 {
		dConn, err := grpc.Dial(dServer+":"+strconv.Itoa(dPort), grpc.WithInsecure())
		if err != nil {
			panic(err)
		}
		defer dConn.Close()

		c := pbs.NewGoserverServiceClient(dConn)
		_, err = c.IsAlive(context.Background(), &pbs.Alive{})
		log.Printf("UPDATED: %v,%v -> %v", dServer, dPort, err)
		com.details.Running = (err == nil)
	} else {
		//Mark as false if we can't locate the job
		com.details.Running = false
	}
}

// BuildJob builds out a job
func (s *Server) BuildJob(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.Checkout(in.Name)
	return &pb.Empty{}, nil
}

// List lists all running jobs
func (s *Server) List(ctx context.Context, in *pb.Empty) (*pb.JobList, error) {
	details := &pb.JobList{}
	for _, job := range s.runner.backgroundTasks {
		updateState(job)
		details.Details = append(details.Details, job.details)
	}

	return details, nil
}

// Run runs a background task
func (s *Server) Run(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.Run(in)
	return &pb.Empty{}, nil
}

//Update restarts a job with new settings
func (s *Server) Update(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.Update(in)
	return &pb.Empty{}, nil
}

// Kill a background task
func (s *Server) Kill(ctx context.Context, in *pb.JobSpec) (*pb.Empty, error) {
	s.runner.kill(in)
	return &pb.Empty{}, nil
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildSlaveServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//Init builds the default runner framework
func Init() *Runner {
	r := &Runner{gopath: "goautobuild"}
	r.runner = runCommand
	go r.run()
	return r
}

func runCommand(c *runnerCommand) {
	log.Printf("RUNNING COMMAND: %v", c)

	if c.command == nil {
		return
	}

	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	if len(home) == 0 {
		log.Printf("Error in home: %v", home)
	}
	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}

	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	found := false
	log.Printf("HERE = %v", c.command.Env)
	envl := os.Environ()
	for i, blah := range envl {
		if strings.HasPrefix(blah, "GOPATH") {
			envl[i] = path
			found = true
		}
	}
	if !found {
		envl = append(envl, path)
	}
	log.Printf("ENV = %v", envl)
	c.command.Env = envl

	out, err := c.command.StdoutPipe()
	out2, err2 := c.command.StderrPipe()
	if err != nil {
		log.Printf("Blah: %v", err)
	}

	if err2 != nil {
		log.Printf("Blah2: %v", err)
	}

	log.Printf("%v, %v and %v", c.command.Path, c.command.Args, c.command.Env)
	c.command.Start()

	if !c.background {
		str := ""

		if out != nil {
			buf := new(bytes.Buffer)
			buf.ReadFrom(out)
			str = buf.String()
		}

		if out2 != nil {
			buf2 := new(bytes.Buffer)
			buf2.ReadFrom(out2)
			str2 := buf2.String()
			log.Printf("%v and %v", str, str2)

		}
		c.command.Wait()
		c.output = str
		c.complete = true
	}
	log.Printf("DONE")
}

func (diskChecker prodDiskChecker) diskUsage(path string) int64 {
	return diskUsage(path)
}

func (s *Server) rebuildLoop() {
	for true {
		time.Sleep(time.Minute)

		var rebuildList []*pb.JobSpec
		var hashList []string
		for _, job := range s.runner.backgroundTasks {
			log.Printf("Job (started %v, now %v) %v", job.started, time.Now(), job)
			if time.Since(job.started) > time.Hour {
				log.Printf("Added to rebuild list (%v)", job)
				rebuildList = append(rebuildList, job.details.Spec)
				hashList = append(hashList, job.hash)
			}
		}

		log.Printf("Rebuilding %v", rebuildList)
		for i := range rebuildList {
			s.runner.Rebuild(rebuildList[i], hashList[i])
		}
	}
}

func main() {
	var quiet = flag.Bool("quiet", true, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	s := Server{&goserver.GoServer{}, Init(), prodDiskChecker{}}
	s.Register = s
	s.PrepServer()
	s.GoServer.Killme = false
	s.RegisterServingTask(s.rebuildLoop)
	s.RegisterServer("gobuildslave", false)
	s.Serve()
}
<|endoftext|>


<commit_before>package discordgo

import (
	"encoding/json"
	"fmt"
)

// ComponentType is type of component.
type ComponentType uint

// MessageComponent types.
const (
	ActionsRowComponent ComponentType = 1
	ButtonComponent     ComponentType = 2
	SelectMenuComponent ComponentType = 3
	TextInputComponent  ComponentType = 4
)

// MessageComponent is a base interface for all message components.
type MessageComponent interface {
	json.Marshaler
	Type() ComponentType
}

type unmarshalableMessageComponent struct {
	MessageComponent
}

// UnmarshalJSON is a helper function to unmarshal MessageComponent object.
func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
	var v struct {
		Type ComponentType `json:"type"`
	}
	err := json.Unmarshal(src, &v)
	if err != nil {
		return err
	}

	switch v.Type {
	case ActionsRowComponent:
		umc.MessageComponent = &ActionsRow{}
	case ButtonComponent:
		umc.MessageComponent = &Button{}
	case SelectMenuComponent:
		umc.MessageComponent = &SelectMenu{}
	case TextInputComponent:
		umc.MessageComponent = &TextInput{}
	default:
		return fmt.Errorf("unknown component type: %d", v.Type)
	}
	return json.Unmarshal(src, umc.MessageComponent)
}

func MessageComponentFromJSON(b []byte) (MessageComponent, error) {
	var u unmarshalableMessageComponent
	err := u.UnmarshalJSON(b)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal into MessageComponent: %w", err)
	}
	return u.MessageComponent, nil
}

// ActionsRow is a container for components within one row.
type ActionsRow struct {
	Components []MessageComponent `json:"components"`
}

// MarshalJSON is a method for marshaling ActionsRow to a JSON object.
func (r ActionsRow) MarshalJSON() ([]byte, error) {
	type actionsRow ActionsRow

	return json.Marshal(struct {
		actionsRow
		Type ComponentType `json:"type"`
	}{
		actionsRow: actionsRow(r),
		Type:       r.Type(),
	})
}

// UnmarshalJSON is a helper function to unmarshal Actions Row.
func (r *ActionsRow) UnmarshalJSON(data []byte) error {
	var v struct {
		RawComponents []unmarshalableMessageComponent `json:"components"`
	}
	err := json.Unmarshal(data, &v)
	if err != nil {
		return err
	}
	r.Components = make([]MessageComponent, len(v.RawComponents))
	for i, v := range v.RawComponents {
		r.Components[i] = v.MessageComponent
	}

	return err
}

// Type is a method to get the type of a component.
func (r ActionsRow) Type() ComponentType {
	return ActionsRowComponent
}

// ButtonStyle is style of button.
type ButtonStyle uint

// Button styles.
const (
	// PrimaryButton is a button with blurple color.
	PrimaryButton ButtonStyle = 1
	// SecondaryButton is a button with grey color.
	SecondaryButton ButtonStyle = 2
	// SuccessButton is a button with green color.
	SuccessButton ButtonStyle = 3
	// DangerButton is a button with red color.
	DangerButton ButtonStyle = 4
	// LinkButton is a special type of button which navigates to a URL. Has grey color.
	LinkButton ButtonStyle = 5
)

// ComponentEmoji represents button emoji, if it does have one.
type ComponentEmoji struct {
	Name     string `json:"name,omitempty"`
	ID       string `json:"id,omitempty"`
	Animated bool   `json:"animated,omitempty"`
}

// Button represents button component.
type Button struct {
	Label    string         `json:"label"`
	Style    ButtonStyle    `json:"style"`
	Disabled bool           `json:"disabled"`
	Emoji    ComponentEmoji `json:"emoji"`

	// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
	URL      string `json:"url,omitempty"`
	CustomID string `json:"custom_id,omitempty"`
}

// MarshalJSON is a method for marshaling Button to a JSON object.
func (b Button) MarshalJSON() ([]byte, error) {
	type button Button

	if b.Style == 0 {
		b.Style = PrimaryButton
	}

	return json.Marshal(struct {
		button
		Type ComponentType `json:"type"`
	}{
		button: button(b),
		Type:   b.Type(),
	})
}

// Type is a method to get the type of a component.
func (Button) Type() ComponentType {
	return ButtonComponent
}

// SelectMenuOption represents an option for a select menu.
type SelectMenuOption struct {
	Label       string         `json:"label,omitempty"`
	Value       string         `json:"value"`
	Description string         `json:"description"`
	Emoji       ComponentEmoji `json:"emoji"`
	// Determines whenever option is selected by default or not.
	Default bool `json:"default"`
}

// SelectMenu represents select menu component.
type SelectMenu struct {
	CustomID string `json:"custom_id,omitempty"`
	// The text which will be shown in the menu if there's no default options or all options was deselected and component was closed.
	Placeholder string `json:"placeholder"`
	// This value determines the minimal amount of selected items in the menu.
	MinValues int `json:"min_values,omitempty"`
	// This value determines the maximal amount of selected items in the menu.
	// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
	MaxValues int                `json:"max_values,omitempty"`
	Options   []SelectMenuOption `json:"options"`
}

// Type is a method to get the type of a component.
func (SelectMenu) Type() ComponentType {
	return SelectMenuComponent
}

// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
func (m SelectMenu) MarshalJSON() ([]byte, error) {
	type selectMenu SelectMenu

	return json.Marshal(struct {
		selectMenu
		Type ComponentType `json:"type"`
	}{
		selectMenu: selectMenu(m),
		Type:       m.Type(),
	})
}

// TextInput represents text input component.
type TextInput struct {
	CustomID    string        `json:"custom_id,omitempty"`
	Label       string        `json:"label"`
	Style       TextStyleType `json:"style"`
	Placeholder string        `json:"placeholder,omitempty"`
	Value       string        `json:"value,omitempty"`
	Required    bool          `json:"required"`
	MinLength   int           `json:"min_length"`
	MaxLength   int           `json:"max_length,omitempty"`
}

// Type is a method to get the type of a component.
func (TextInput) Type() ComponentType {
	return TextInputComponent
}

// MarshalJSON is a method for marshaling TextInput to a JSON object.
func (m TextInput) MarshalJSON() ([]byte, error) {
	type inputText TextInput

	return json.Marshal(struct {
		inputText
		Type ComponentType `json:"type"`
	}{
		inputText: inputText(m),
		Type:      m.Type(),
	})
}

// TextStyleType is style of text in TextInput component.
type TextStyleType uint

// Text styles
const (
	TextStyleShort     TextStyleType = 1
	TextStyleParagraph TextStyleType = 2
)
<commit_msg>feat(components): renamed TextStyleType to TextInputStyleType<commit_after>package discordgo

import (
	"encoding/json"
	"fmt"
)

// ComponentType is type of component.
type ComponentType uint

// MessageComponent types.
const (
	ActionsRowComponent ComponentType = 1
	ButtonComponent     ComponentType = 2
	SelectMenuComponent ComponentType = 3
	TextInputComponent  ComponentType = 4
)

// MessageComponent is a base interface for all message components.
type MessageComponent interface {
	json.Marshaler
	Type() ComponentType
}

type unmarshalableMessageComponent struct {
	MessageComponent
}

// UnmarshalJSON is a helper function to unmarshal MessageComponent object.
func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
	var v struct {
		Type ComponentType `json:"type"`
	}
	err := json.Unmarshal(src, &v)
	if err != nil {
		return err
	}

	switch v.Type {
	case ActionsRowComponent:
		umc.MessageComponent = &ActionsRow{}
	case ButtonComponent:
		umc.MessageComponent = &Button{}
	case SelectMenuComponent:
		umc.MessageComponent = &SelectMenu{}
	case TextInputComponent:
		umc.MessageComponent = &TextInput{}
	default:
		return fmt.Errorf("unknown component type: %d", v.Type)
	}
	return json.Unmarshal(src, umc.MessageComponent)
}

func MessageComponentFromJSON(b []byte) (MessageComponent, error) {
	var u unmarshalableMessageComponent
	err := u.UnmarshalJSON(b)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal into MessageComponent: %w", err)
	}
	return u.MessageComponent, nil
}

// ActionsRow is a container for components within one row.
type ActionsRow struct {
	Components []MessageComponent `json:"components"`
}

// MarshalJSON is a method for marshaling ActionsRow to a JSON object.
func (r ActionsRow) MarshalJSON() ([]byte, error) {
	type actionsRow ActionsRow

	return json.Marshal(struct {
		actionsRow
		Type ComponentType `json:"type"`
	}{
		actionsRow: actionsRow(r),
		Type:       r.Type(),
	})
}

// UnmarshalJSON is a helper function to unmarshal Actions Row.
func (r *ActionsRow) UnmarshalJSON(data []byte) error {
	var v struct {
		RawComponents []unmarshalableMessageComponent `json:"components"`
	}
	err := json.Unmarshal(data, &v)
	if err != nil {
		return err
	}
	r.Components = make([]MessageComponent, len(v.RawComponents))
	for i, v := range v.RawComponents {
		r.Components[i] = v.MessageComponent
	}

	return err
}

// Type is a method to get the type of a component.
func (r ActionsRow) Type() ComponentType {
	return ActionsRowComponent
}

// ButtonStyle is style of button.
type ButtonStyle uint

// Button styles.
const (
	// PrimaryButton is a button with blurple color.
	PrimaryButton ButtonStyle = 1
	// SecondaryButton is a button with grey color.
	SecondaryButton ButtonStyle = 2
	// SuccessButton is a button with green color.
	SuccessButton ButtonStyle = 3
	// DangerButton is a button with red color.
	DangerButton ButtonStyle = 4
	// LinkButton is a special type of button which navigates to a URL. Has grey color.
	LinkButton ButtonStyle = 5
)

// ComponentEmoji represents button emoji, if it does have one.
type ComponentEmoji struct {
	Name     string `json:"name,omitempty"`
	ID       string `json:"id,omitempty"`
	Animated bool   `json:"animated,omitempty"`
}

// Button represents button component.
type Button struct {
	Label    string         `json:"label"`
	Style    ButtonStyle    `json:"style"`
	Disabled bool           `json:"disabled"`
	Emoji    ComponentEmoji `json:"emoji"`

	// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
	URL      string `json:"url,omitempty"`
	CustomID string `json:"custom_id,omitempty"`
}

// MarshalJSON is a method for marshaling Button to a JSON object.
func (b Button) MarshalJSON() ([]byte, error) {
	type button Button

	if b.Style == 0 {
		b.Style = PrimaryButton
	}

	return json.Marshal(struct {
		button
		Type ComponentType `json:"type"`
	}{
		button: button(b),
		Type:   b.Type(),
	})
}

// Type is a method to get the type of a component.
func (Button) Type() ComponentType {
	return ButtonComponent
}

// SelectMenuOption represents an option for a select menu.
type SelectMenuOption struct {
	Label       string         `json:"label,omitempty"`
	Value       string         `json:"value"`
	Description string         `json:"description"`
	Emoji       ComponentEmoji `json:"emoji"`
	// Determines whenever option is selected by default or not.
	Default bool `json:"default"`
}

// SelectMenu represents select menu component.
type SelectMenu struct {
	CustomID string `json:"custom_id,omitempty"`
	// The text which will be shown in the menu if there's no default options or all options was deselected and component was closed.
	Placeholder string `json:"placeholder"`
	// This value determines the minimal amount of selected items in the menu.
	MinValues int `json:"min_values,omitempty"`
	// This value determines the maximal amount of selected items in the menu.
	// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
	MaxValues int                `json:"max_values,omitempty"`
	Options   []SelectMenuOption `json:"options"`
}

// Type is a method to get the type of a component.
func (SelectMenu) Type() ComponentType {
	return SelectMenuComponent
}

// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
func (m SelectMenu) MarshalJSON() ([]byte, error) {
	type selectMenu SelectMenu

	return json.Marshal(struct {
		selectMenu
		Type ComponentType `json:"type"`
	}{
		selectMenu: selectMenu(m),
		Type:       m.Type(),
	})
}

// TextInput represents text input component.
type TextInput struct {
	CustomID    string        `json:"custom_id,omitempty"`
	Label       string        `json:"label"`
	Style       TextStyleType `json:"style"`
	Placeholder string        `json:"placeholder,omitempty"`
	Value       string        `json:"value,omitempty"`
	Required    bool          `json:"required"`
	MinLength   int           `json:"min_length"`
	MaxLength   int           `json:"max_length,omitempty"`
}

// Type is a method to get the type of a component.
func (TextInput) Type() ComponentType {
	return TextInputComponent
}

// MarshalJSON is a method for marshaling TextInput to a JSON object.
func (m TextInput) MarshalJSON() ([]byte, error) {
	type inputText TextInput

	return json.Marshal(struct {
		inputText
		Type ComponentType `json:"type"`
	}{
		inputText: inputText(m),
		Type:      m.Type(),
	})
}

// TextInputStyleType is style of text in TextInput component.
type TextInputStyleType uint

// Text styles
const (
	TextInputShort     TextStyleType = 1
	TextInputParagraph TextStyleType = 2
)
<|endoftext|>


<commit_before>package integration_test

import (
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Coverage Specs", func() {
	AfterEach(func() {
		//os.RemoveAll("./_fixtures/coverage_fixture/coverage_fixture.coverprofile")
	})

	It("runs coverage analysis in series and in parallel", func() {
		session := startGinkgo("./_fixtures/coverage_fixture", "-cover")
		Eventually(session).Should(gexec.Exit(0))
		output := session.Out.Contents()
		Ω(output).Should(ContainSubstring("coverage: 80.0% of statements"))

		serialCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		//os.RemoveAll("./_fixtures/coverage_fixture/coverage_fixture.coverprofile")

		Eventually(startGinkgo("./_fixtures/coverage_fixture", "-cover", "-nodes=4")).Should(gexec.Exit(0))

		parallelCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		Ω(parallelCoverProfileOutput).Should(Equal(serialCoverProfileOutput))
	})

	FIt("runs coverage analysis on external packages in series and in parallel", func() {
		session := startGinkgo("./_fixtures/coverage_fixture", "-coverpkg=github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture,github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture/external_coverage_fixture")
		Eventually(session).Should(gexec.Exit(0))
		output := session.Out.Contents()
		Ω(output).Should(ContainSubstring("coverage: 71.4% of statements in github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture, github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture/external_coverage_fixture"))

		serialCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		//os.RemoveAll("./_fixtures/coverage_fixture/coverage_fixture.coverprofile")

		Eventually(startGinkgo("./_fixtures/coverage_fixture", "-coverpkg=github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture,github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture/external_coverage_fixture", "-nodes=4")).Should(gexec.Exit(0))

		parallelCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		Ω(parallelCoverProfileOutput).Should(Equal(serialCoverProfileOutput))
	})
})
<commit_msg>Whoops, forgot to cleanup test<commit_after>package integration_test

import (
	"os"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Coverage Specs", func() {
	AfterEach(func() {
		os.RemoveAll("./_fixtures/coverage_fixture/coverage_fixture.coverprofile")
	})

	It("runs coverage analysis in series and in parallel", func() {
		session := startGinkgo("./_fixtures/coverage_fixture", "-cover")
		Eventually(session).Should(gexec.Exit(0))
		output := session.Out.Contents()
		Ω(output).Should(ContainSubstring("coverage: 80.0% of statements"))

		serialCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		os.RemoveAll("./_fixtures/coverage_fixture/coverage_fixture.coverprofile")

		Eventually(startGinkgo("./_fixtures/coverage_fixture", "-cover", "-nodes=4")).Should(gexec.Exit(0))

		parallelCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		Ω(parallelCoverProfileOutput).Should(Equal(serialCoverProfileOutput))
	})

	It("runs coverage analysis on external packages in series and in parallel", func() {
		session := startGinkgo("./_fixtures/coverage_fixture", "-coverpkg=github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture,github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture/external_coverage_fixture")
		Eventually(session).Should(gexec.Exit(0))
		output := session.Out.Contents()
		Ω(output).Should(ContainSubstring("coverage: 71.4% of statements in github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture, github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture/external_coverage_fixture"))

		serialCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		os.RemoveAll("./_fixtures/coverage_fixture/coverage_fixture.coverprofile")

		Eventually(startGinkgo("./_fixtures/coverage_fixture", "-coverpkg=github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture,github.com/onsi/ginkgo/integration/_fixtures/coverage_fixture/external_coverage_fixture", "-nodes=4")).Should(gexec.Exit(0))

		parallelCoverProfileOutput, err := exec.Command("go", "tool", "cover", "-func=./_fixtures/coverage_fixture/coverage_fixture.coverprofile").CombinedOutput()
		Ω(err).ShouldNot(HaveOccurred())

		Ω(parallelCoverProfileOutput).Should(Equal(serialCoverProfileOutput))
	})
})
<|endoftext|>


<commit_before>package dbadapter

import (
	"database/sql"
	"fmt"
	"github.com/coopernurse/gorp"
	_ "github.com/go-sql-driver/mysql"
	"gosync/config"
	"gosync/fstools"
	"log"
	"os"
	"time"
)

/*
CREATE TABLE IF NOT EXISTS `backups` (
`id` int(10) unsigned NOT NULL,
  `path` text COLLATE utf8_unicode_ci NOT NULL,
  `filename` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `checksum` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `atime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `mtime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `uid` int(5) NOT NULL,
  `gid` int(5) NOT NULL,
  `perms` int(4) NOT NULL,
  `host_updated` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
*/

type FsTable struct {
	Id         int    `id`
	Path       string `path`
	IsDir      int    `is_dir`
	Filename   string `filename`
	Checksum   string `checksum`
	Mtime      int    `mtime`
	Uid        int    `uid`
	Gid        int    `gid`
	Perms      string `perms`
	HostName   string `host_updated`
	LastUpdate int    `last_update`
}

func MySQLSetupTables(cfg *config.Configuration) {
	dbmap := initDb(cfg)
	defer dbmap.Db.Close()
	log.Println("Database initialized")
	for key, _ := range cfg.Listeners {
		//table := dbmap.AddTableWithName(FsTable{}, key).SetKeys(true, "Id")
		dbmap.AddTableWithName(FsTable{}, key).SetKeys(true, "Id")
		err := dbmap.CreateTablesIfNotExists()
		checkErr(err, "Create tables failed")
		count, err := dbmap.SelectInt("select count(*) from " + key)
		checkErr(err, "select count(*) failed")
		if count < 1 {
			log.Println("New table build starting for: " + key)
		}

	}

}

func MySQLInsertItem(cfg *config.Configuration, table string, item fstools.FsItem) bool {
	dbmap := initDb(cfg)
	defer dbmap.Db.Close()
	var isDirectory = 0
	if item.IsDir {
		isDirectory = 1
	}
	hostname, _ := os.Hostname()
	row := &FsTable{
		Path:       item.Filename,
		IsDir:      isDirectory,
		Filename:   item.Filename,
		Checksum:   item.Checksum,
		Mtime:      item.Mtime,
		Uid:        item.Uid,
		Gid:        item.Gid,
		Perms:      item.Perms,
		HostName:   hostname,
		LastUpdate: int(time.Now().Unix()),
	}
	err := dbmap.Insert(row)
	if err != nil {
		checkErr(err, "Error occurred adding item to table: "+table)
		return false
	} else {
		return true
	}

}

func MySQLCheckEmpty(cfg *config.Configuration, table string) bool {
	dbmap := initDb(cfg)
	defer dbmap.Db.Close()
	count, err := dbmap.SelectInt("select count(*) from " + table)
	checkErr(err, "select count(*) failed on "+table)
	var isEmpty = true
	if count > 0 {
		isEmpty = false
	}
	return isEmpty
}

func initDb(cfg *config.Configuration) *gorp.DbMap {
	// connect to db using standard Go database/sql API
	// use whatever database/sql driver you wish
	//root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local
	//user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true

	//log.Println("CONNECT_STRING:" + cfg.Database.Dsn)
	db, err := sql.Open("mysql", cfg.Database.Dsn)
	if err != nil {
		panic(err)
	}
	// construct a gorp DbMap
	dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}

	return dbmap
}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatalln(msg, err)
	}
}

func main() {
	db, err := sql.Open("mysql", "user:password@/database")
	if err != nil {
		panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
	}
	defer db.Close()

	// Prepare statement for inserting data
	stmtIns, err := db.Prepare("INSERT INTO squareNum VALUES( ?, ? )") // ? = placeholder
	if err != nil {
		panic(err.Error()) // proper error handling instead of panic in your app
	}
	defer stmtIns.Close() // Close the statement when we leave main() / the program terminates

	// Prepare statement for reading data
	stmtOut, err := db.Prepare("SELECT squareNumber FROM squarenum WHERE number = ?")
	if err != nil {
		panic(err.Error()) // proper error handling instead of panic in your app
	}
	defer stmtOut.Close()

	// Insert square numbers for 0-24 in the database
	for i := 0; i < 25; i++ {
		_, err = stmtIns.Exec(i, (i * i)) // Insert tuples (i, i^2)
		if err != nil {
			panic(err.Error()) // proper error handling instead of panic in your app
		}
	}

	var squareNum int // we "scan" the result in here

	// Query the square-number of 13
	err = stmtOut.QueryRow(13).Scan(&squareNum) // WHERE number = 13
	if err != nil {
		panic(err.Error()) // proper error handling instead of panic in your app
	}
	fmt.Printf("The square number of 13 is: %d", squareNum)

	// Query another number.. 1 maybe?
	err = stmtOut.QueryRow(1).Scan(&squareNum) // WHERE number = 1
	if err != nil {
		panic(err.Error()) // proper error handling instead of panic in your app
	}
	fmt.Printf("The square number of 1 is: %d", squareNum)
}
<commit_msg>Some refactoring to use base interfaces<commit_after>package dbadapter

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"gosync/config"
	"gosync/fstools"
	"log"
	"os"
	"time"
)

func createTableQuery(table string) string {
	return fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s (
	id int(10) unsigned NOT NULL,
  path text COLLATE utf8_unicode_ci NOT NULL,
  filename varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  checksum varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  atime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  mtime timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  uid int(5) NOT NULL,
  gid int(5) NOT NULL,
  perms int(4) NOT NULL,
  host_updated varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
`, table)
}

type FsTable struct {
	Id         int    `id`
	Path       string `path`
	IsDir      int    `is_dir`
	Filename   string `filename`
	Checksum   string `checksum`
	Mtime      int    `mtime`
	Uid        int    `uid`
	Gid        int    `gid`
	Perms      string `perms`
	HostName   string `host_updated`
	LastUpdate int    `last_update`
}

func MySQLSetupTables(cfg *config.Configuration) {
	var db *sql.DB
	db = initDb(cfg)
	defer db.Close()
	log.Println("Database initialized")

	for key, _ := range cfg.Listeners {
		_, err := db.Query(createTableQuery(key))
		if err != nil {
			panic(err.Error()) // proper error handling instead of panic in your app
		}

	}

}

func MySQLInsertItem(cfg *config.Configuration, table string, item fstools.FsItem) bool {
	var db *sql.DB
	db = initDb(cfg)
	defer db.Close()

	var isDirectory = 0
	if item.IsDir {
		isDirectory = 1
	}

	hostname, _ := os.Hostname()
	row := &FsTable{
		Path:       item.Filename,
		IsDir:      isDirectory,
		Filename:   item.Filename,
		Checksum:   item.Checksum,
		Mtime:      item.Mtime,
		Uid:        item.Uid,
		Gid:        item.Gid,
		Perms:      item.Perms,
		HostName:   hostname,
		LastUpdate: int(time.Now().Unix()),
	}
	/*
		err := dbmap.Insert(row)
		if err != nil {
			checkErr(err, "Error occurred adding item to table: "+table)
			return false
		} else {
			return true
		}*/
	log.Printf("Stub in for adding data %v", row)
	return true
}

func MySQLCheckEmpty(cfg *config.Configuration, table string) bool {
	var db *sql.DB
	db = initDb(cfg)
	defer db.Close()

	var count int
	query := fmt.Sprintf("SELECT count(*) FROM %s", table)
	err := db.QueryRow(query).Scan(&count)
	if err != nil {
		log.Fatalf("Critical error, cannot read from table %s : %v", table, err.Error())
	}
	var isEmpty = true
	if count > 0 {
		isEmpty = false
	}
	return isEmpty
}

func initDb(cfg *config.Configuration) *sql.DB {
	db, err := sql.Open("mysql", cfg.Database.Dsn)
	if err != nil {
		log.Fatalf("Error connecting to database: %v", err.Error())
	}
	return db
}

func checkErr(err error, msg string) {
	if err != nil {
		log.Fatalln(msg, err)
	}
}
<|endoftext|>


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

package sandbox

import (
	"testing"
	"time"
	"github.com/petar/GoGauge/gauge"
	"github.com/petar/GoDCCP/dccp"
	"github.com/petar/GoDCCP/dccp/ccid3"
)

func TestDropRate(t *testing.T) {
	hca, hcb, _ := NewLine(dccp.NewLogger(NewTime(), "line"), "client", "server", 1e9, 10)
	ccid := ccid3.CCID3{}
	gauge.Select("client", "server", "line", "conn", "s", "s-x", "s-strober", "s-tracker", "r")
	/* cc := */ dccp.NewConnClient("client", hca, ccid.NewSender(), ccid.NewReceiver(), 0)
	/* cs := */ dccp.NewConnServer("server", hcb, ccid.NewSender(), ccid.NewReceiver())
	time.Sleep(1e9)
}
<commit_msg>sandbox + new logger/timer<commit_after>// Copyright 2011 GoDCCP Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

package sandbox

import (
	"testing"
	"time"
	"github.com/petar/GoGauge/gauge"
	"github.com/petar/GoDCCP/dccp"
	"github.com/petar/GoDCCP/dccp/ccid3"
)

func TestDropRate(t *testing.T) {

	gauge.Select("client", "server", "line", "conn", "s", "s-x", "s-strober", "s-tracker", "r")

	var tt dccp.Time = dccp.RealTime{}

	hca, hcb, _ := NewLine(dccp.NewLogger(tt, "line"), "client", "server", 1e9, 10)
	ccid := ccid3.CCID3{}

	clog := dccp.NewLogger(tt, "client")
	/* cc := */ dccp.NewConnClient(tt, clog, hca, ccid.NewSender(tt, clog), ccid.NewReceiver(tt, clog), 0)

	slog := dccp.NewLogger(tt, "server")
	/* cs := */ dccp.NewConnServer(tt, slog, hcb, ccid.NewSender(tt, slog), ccid.NewReceiver(tt, slog))

	time.Sleep(10e9)
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"

	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"strings"
)

var barMap = map[string]string{}

type beerInfo struct {
	brewery string
	brew    string
}

func findBeer(node *html.Node, beers *[]beerInfo) {
	if node.DataAtom == atom.Div {
		for _, attr := range node.Attr {
			if attr.Key == "id" && strings.HasPrefix(attr.Val, "beer-") {
				brewery, brew := "", ""
				findBrewery(node, &brewery)
				findBrew(node, &brew)
				*beers = append(*beers, beerInfo{brewery, brew})
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		findBeer(kid, beers)
	}
}

func findBrewery(node *html.Node, brewery *string) bool {
	if node.DataAtom == atom.H4 {
		if content := node.FirstChild; content != nil {
			*brewery = content.Data
			return true
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrewery(kid, brewery) {
			return true
		}
	}
	return false
}

func findBrew(node *html.Node, brew *string) bool {
	for _, attr := range node.Attr {
		if attr.Key == "class" && attr.Val == "beer-name" {
			if content := node.FirstChild; content != nil {
				*brew = content.Data
				return true
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBrew(kid, brew) {
			return true
		}
	}
	return false
}

func findBarDesc(node *html.Node, desc *string) bool {
	if node.DataAtom == atom.Meta {
		isDesc := false
		for _, attr := range node.Attr {
			if attr.Key == "name" && attr.Val == "description" {
				isDesc = true
				break
			}
		}
		if isDesc {
			for _, attr := range node.Attr {
				if attr.Key == "content" {
					*desc = attr.Val
					break
				}
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if findBarDesc(kid, desc) {
			return true
		}
	}
	return false
}

func checkId(id string) bool {
	ok, err := regexp.MatchString("^[[:xdigit:]]{24}$", id)
	if err != nil {
		return false
	}
	return ok
}

func readRc() {
	usr, err := user.Current()
	if err != nil {
		return
	}
	data, err := ioutil.ReadFile(usr.HomeDir + "/.taplistrc")
	if err != nil {
		return
	}
	lines := strings.Split(string(data), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		idx := strings.IndexAny(line, " \t")
		if idx < len(line)-1 {
			id, name := line[:idx], strings.TrimSpace(line[idx:])
			barMap[id] = name
		}
	}
}

func lookupBar(arg string) (string, string) {
	for id, name := range barMap {
		if strings.Contains(strings.ToLower(name), strings.ToLower(arg)) {
			return id, name
		}
	}
	return "", ""
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("taplist: ")

	if len(os.Args) != 2 {
		log.Fatalln("usage: taplist <id> | <name>")
	}
	readRc()
	arg := strings.ToLower(os.Args[1])
	id, name := "", ""
	if checkId(arg) {
		id, name = arg, arg
	} else {
		id, name = lookupBar(arg)
	}
	if id == "" {
		log.Fatalln(arg + " doesn't look like a valid name or taplister bar id")
	}

	resp, err := http.Get("http://www.taplister.com/bars/" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	doc, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	desc, beers := "", []beerInfo{}
	findBarDesc(doc, &desc)
	findBeer(doc, &beers)
	if desc != "" {
		fmt.Println(desc + "\n")
	} else {
		fmt.Printf("%d beers on tap at "+name+"\n\n", len(beers))
	}
	for _, beer := range beers {
		fmt.Printf("%-38.38s  %s\n", beer.brewery, beer.brew)
	}
}
<commit_msg>Moved recursion in searches to a function.<commit_after>package main

import (
	"code.google.com/p/go.net/html"
	"code.google.com/p/go.net/html/atom"

	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"strings"
)

var barMap = map[string]string{}

type beerInfo struct {
	brewery string
	brew    string
}

func recFind(node *html.Node, result *string, fn func(*html.Node, *string) bool) bool {
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		if fn(kid, result) {
			return true
		}
	}
	return false
}

func findBeer(node *html.Node, beers *[]beerInfo) {
	if node.DataAtom == atom.Div {
		for _, attr := range node.Attr {
			if attr.Key == "id" && strings.HasPrefix(attr.Val, "beer-") {
				brewery, brew := "", ""
				findBrewery(node, &brewery)
				findBrew(node, &brew)
				*beers = append(*beers, beerInfo{brewery, brew})
				return
			}
		}
	}
	for kid := node.FirstChild; kid != nil; kid = kid.NextSibling {
		findBeer(kid, beers)
	}
}

func findBrewery(node *html.Node, brewery *string) bool {
	if node.DataAtom == atom.H4 {
		if content := node.FirstChild; content != nil {
			*brewery = content.Data
			return true
		}
	}
	return recFind(node, brewery, findBrewery)
}

func findBrew(node *html.Node, brew *string) bool {
	for _, attr := range node.Attr {
		if attr.Key == "class" && attr.Val == "beer-name" {
			if content := node.FirstChild; content != nil {
				*brew = content.Data
				return true
			}
		}
	}
	return recFind(node, brew, findBrew)
}

func findBarDesc(node *html.Node, desc *string) bool {
	if node.DataAtom == atom.Meta {
		isDesc := false
		for _, attr := range node.Attr {
			if attr.Key == "name" && attr.Val == "description" {
				isDesc = true
				break
			}
		}
		if isDesc {
			for _, attr := range node.Attr {
				if attr.Key == "content" {
					*desc = attr.Val
					break
				}
			}
		}
	}
	return recFind(node, desc, findBarDesc)
}

func checkId(id string) bool {
	ok, err := regexp.MatchString("^[[:xdigit:]]{24}$", id)
	if err != nil {
		return false
	}
	return ok
}

func readRc() {
	usr, err := user.Current()
	if err != nil {
		return
	}
	data, err := ioutil.ReadFile(usr.HomeDir + "/.taplistrc")
	if err != nil {
		return
	}
	lines := strings.Split(string(data), "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		idx := strings.IndexAny(line, " \t")
		if idx < len(line)-1 {
			id, name := line[:idx], strings.TrimSpace(line[idx:])
			barMap[id] = name
		}
	}
}

func lookupBar(arg string) (string, string) {
	for id, name := range barMap {
		if strings.Contains(strings.ToLower(name), strings.ToLower(arg)) {
			return id, name
		}
	}
	return "", ""
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("taplist: ")

	if len(os.Args) != 2 {
		log.Fatalln("usage: taplist <id> | <name>")
	}
	readRc()
	arg := strings.ToLower(os.Args[1])
	id, name := "", ""
	if checkId(arg) {
		id, name = arg, arg
	} else {
		id, name = lookupBar(arg)
	}
	if id == "" {
		log.Fatalln(arg + " doesn't look like a valid name or taplister bar id")
	}

	resp, err := http.Get("http://www.taplister.com/bars/" + id)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	doc, err := html.Parse(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	desc, beers := "", []beerInfo{}
	findBarDesc(doc, &desc)
	findBeer(doc, &beers)
	if desc != "" {
		fmt.Println(desc + "\n")
	} else {
		fmt.Printf("%d beers on tap at "+name+"\n\n", len(beers))
	}
	for _, beer := range beers {
		fmt.Printf("%-38.38s  %s\n", beer.brewery, beer.brew)
	}
}
<|endoftext|>


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

package restorable

import (
	"errors"
	"fmt"
	"image/color"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphicscommand"
)

// drawImageHistoryItem is an item for history of draw-image commands.
type drawImageHistoryItem struct {
	image    *Image
	vertices []float32
	indices  []uint16
	colorm   *affine.ColorM
	mode     graphics.CompositeMode
	filter   graphics.Filter
	address  graphics.Address
}

// Image represents an image that can be restored when GL context is lost.
type Image struct {
	image *graphicscommand.Image

	basePixels []byte

	// drawImageHistory is a set of draw-image commands.
	// TODO: This should be merged with the similar command queue in package graphics (#433).
	drawImageHistory []*drawImageHistoryItem

	// stale indicates whether the image needs to be synced with GPU as soon as possible.
	stale bool

	// volatile indicates whether the image is cleared whenever a frame starts.
	volatile bool

	// screen indicates whether the image is used as an actual screen.
	screen bool

	w2 int
	h2 int
}

var dummyImage *Image

func init() {
	dummyImage = &Image{
		image: graphicscommand.NewImage(16, 16),
	}
}

// NewImage creates an empty image with the given size.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewImage(width, height int, volatile bool) *Image {
	i := &Image{
		image:    graphicscommand.NewImage(width, height),
		volatile: volatile,
	}

	// There are not 'drawImageHistoryItem's for this image and dummyImage.
	// This means dummyImage might not be restored yet when this image is restored.
	// However, that's ok since this image will be stale or have its updated pixel data soon,
	// and this image can be restored without dummyImage.
	//
	// dummyImage should be restored later anyway.
	sw, sh := dummyImage.Size()
	dw := graphics.NextPowerOf2Int(width)
	dh := graphics.NextPowerOf2Int(height)
	vs := graphics.QuadVertices(dw, dh, 0, 0, sw, sh,
		float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh),
		0, 0,
		1, 1, 1, 1)
	is := graphics.QuadIndices()
	i.image.DrawImage(dummyImage.image, vs, is, nil, graphics.CompositeModeClear, graphics.FilterNearest, graphics.AddressClampToZero)

	theImages.add(i)
	return i
}

// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewScreenFramebufferImage(width, height int) *Image {
	i := &Image{
		image:  graphicscommand.NewScreenFramebufferImage(width, height),
		screen: true,
	}
	theImages.add(i)
	return i
}

func (i *Image) IsVolatile() bool {
	return i.volatile
}

// BasePixelsForTesting returns the image's basePixels for testing.
func (i *Image) BasePixelsForTesting() []byte {
	return i.basePixels
}

// Pixels returns the image's pixel bytes.
//
// Pixels tries to read pixels from GPU if needed.
// It is assured that GPU is not accessed if the opration against the image is only ReplacePixels.
func (i *Image) Pixels() []byte {
	i.readPixelsFromGPUIfNeeded()
	return i.basePixels
}

// Size returns the image's size.
func (i *Image) Size() (int, int) {
	return i.image.Size()
}

// SizePowerOf2 returns the next power of 2 values for the size.
func (i *Image) SizePowerOf2() (int, int) {
	if i.w2 == 0 || i.h2 == 0 {
		w, h := i.image.Size()
		i.w2 = graphics.NextPowerOf2Int(w)
		i.h2 = graphics.NextPowerOf2Int(h)
	}
	return i.w2, i.h2
}

// makeStale makes the image stale.
func (i *Image) makeStale() {
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = true

	// Don't have to call makeStale recursively here.
	// Restoring is done after topological sorting is done.
	// If an image depends on another stale image, this means that
	// the former image can be restored from the latest state of the latter image.
}

// ReplacePixels replaces the image pixels with the given pixels slice.
//
// If pixels is nil, ReplacePixels clears the specified reagion.
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
	w, h := i.image.Size()
	if width <= 0 || height <= 0 {
		panic("restorable: width/height must be positive")
	}
	if x < 0 || y < 0 || w <= x || h <= y || x+width <= 0 || y+height <= 0 || w < x+width || h < y+height {
		panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height))
	}

	// TODO: Avoid making other images stale if possible. (#514)
	// For this purpuse, images should remember which part of that is used for DrawImage.
	theImages.makeStaleIfDependingOn(i)

	if pixels == nil {
		pixels = make([]byte, 4*width*height)
	}
	i.image.ReplacePixels(pixels, x, y, width, height)

	if x == 0 && y == 0 && width == w && height == h {
		if pixels != nil {
			if i.basePixels == nil {
				i.basePixels = make([]byte, 4*w*h)
			}
			copy(i.basePixels, pixels)
		} else {
			// If basePixels is nil, the restored pixels are cleared.
			// See restore() implementation.
			i.basePixels = nil
		}
		i.drawImageHistory = nil
		i.stale = false
		return
	}

	if len(i.drawImageHistory) > 0 {
		panic("restorable: ReplacePixels for a part after DrawImage is forbidden")
	}

	if i.stale {
		return
	}

	idx := 4 * (y*w + x)
	if pixels != nil {
		if i.basePixels == nil {
			i.basePixels = make([]byte, 4*w*h)
		}
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width])
			idx += 4 * w
		}
	} else if i.basePixels != nil {
		zeros := make([]byte, 4*width)
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], zeros)
			idx += 4 * w
		}
	}
}

// DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter, address graphics.Address) {
	if len(vertices) == 0 {
		return
	}
	theImages.makeStaleIfDependingOn(i)

	if img.stale || img.volatile || i.screen || !IsRestoringEnabled() {
		i.makeStale()
	} else {
		i.appendDrawImageHistory(img, vertices, indices, colorm, mode, filter, address)
	}
	i.image.DrawImage(img.image, vertices, indices, colorm, mode, filter, address)
}

// appendDrawImageHistory appends a draw-image history item to the image.
func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter, address graphics.Address) {
	if i.stale || i.volatile || i.screen {
		return
	}
	const maxDrawImageHistoryNum = 100
	if len(i.drawImageHistory)+1 > maxDrawImageHistoryNum {
		i.makeStale()
		return
	}
	// All images must be resolved and not stale each after frame.
	// So we don't have to care if image is stale or not here.
	item := &drawImageHistoryItem{
		image:    image,
		vertices: vertices,
		indices:  indices,
		colorm:   colorm,
		mode:     mode,
		filter:   filter,
		address:  address,
	}
	i.drawImageHistory = append(i.drawImageHistory, item)
}

func (i *Image) readPixelsFromGPUIfNeeded() {
	if i.basePixels == nil || i.drawImageHistory != nil || i.stale {
		graphicscommand.FlushCommands()
		i.readPixelsFromGPU()
		i.drawImageHistory = nil
		i.stale = false
	}
}

// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) color.RGBA {
	w, h := i.image.Size()
	if x < 0 || y < 0 || w <= x || h <= y {
		return color.RGBA{}
	}

	i.readPixelsFromGPUIfNeeded()

	// Even after readPixelsFromGPU, basePixels might be nil when OpenGL error happens.
	if i.basePixels == nil {
		return color.RGBA{}
	}

	idx := 4*x + 4*y*w
	r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
	if i.stale {
		return
	}
	if i.dependsOn(target) {
		i.makeStale()
	}
}

// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU() {
	i.basePixels = i.image.Pixels()
	i.drawImageHistory = nil
	i.stale = false
}

// resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() {
	if !IsRestoringEnabled() {
		return
	}

	if i.volatile {
		return
	}
	if i.screen {
		return
	}
	if !i.stale {
		return
	}
	i.readPixelsFromGPU()
}

// dependsOn returns a boolean value indicating whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
	for _, c := range i.drawImageHistory {
		if c.image == target {
			return true
		}
	}
	return false
}

// dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} {
	r := map[*Image]struct{}{}
	for _, c := range i.drawImageHistory {
		r[c.image] = struct{}{}
	}
	return r
}

// hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool {
	if i.stale {
		return false
	}
	return len(i.drawImageHistory) > 0
}

// Restore restores *graphicscommand.Image from the pixels using its state.
func (i *Image) restore() error {
	w, h := i.image.Size()
	if i.screen {
		// The screen image should also be recreated because framebuffer might
		// be changed.
		i.image = graphicscommand.NewScreenFramebufferImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.volatile {
		i.image = graphicscommand.NewImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.stale {
		// TODO: panic here?
		return errors.New("restorable: pixels must not be stale when restoring")
	}
	gimg := graphicscommand.NewImage(w, h)
	if i.basePixels != nil {
		gimg.ReplacePixels(i.basePixels, 0, 0, w, h)
	} else {
		// Clear the image explicitly.
		pix := make([]uint8, w*h*4)
		gimg.ReplacePixels(pix, 0, 0, w, h)
	}
	for _, c := range i.drawImageHistory {
		// All dependencies must be already resolved.
		if c.image.hasDependency() {
			panic("not reached")
		}
		gimg.DrawImage(c.image.image, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address)
	}
	i.image = gimg

	i.basePixels = gimg.Pixels()
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// Dispose disposes the image.
//
// After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() {
	theImages.remove(i)

	i.image.Dispose()
	i.image = nil
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
}

// IsInvalidated returns a boolean value indicating whether the image is invalidated.
//
// If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) {
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	graphicscommand.FlushCommands()
	if !IsRestoringEnabled() {
		return false, nil
	}

	return i.image.IsInvalidated(), nil
}
<commit_msg>restorable: Bug fix: volatile image must be cleared when recovering from the context lost<commit_after>// Copyright 2016 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package restorable

import (
	"errors"
	"fmt"
	"image/color"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphicscommand"
)

// drawImageHistoryItem is an item for history of draw-image commands.
type drawImageHistoryItem struct {
	image    *Image
	vertices []float32
	indices  []uint16
	colorm   *affine.ColorM
	mode     graphics.CompositeMode
	filter   graphics.Filter
	address  graphics.Address
}

// Image represents an image that can be restored when GL context is lost.
type Image struct {
	image *graphicscommand.Image

	basePixels []byte

	// drawImageHistory is a set of draw-image commands.
	// TODO: This should be merged with the similar command queue in package graphics (#433).
	drawImageHistory []*drawImageHistoryItem

	// stale indicates whether the image needs to be synced with GPU as soon as possible.
	stale bool

	// volatile indicates whether the image is cleared whenever a frame starts.
	volatile bool

	// screen indicates whether the image is used as an actual screen.
	screen bool

	w2 int
	h2 int
}

var dummyImage *Image

func init() {
	dummyImage = &Image{
		image: graphicscommand.NewImage(16, 16),
	}
}

// NewImage creates an empty image with the given size.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewImage(width, height int, volatile bool) *Image {
	i := &Image{
		image:    graphicscommand.NewImage(width, height),
		volatile: volatile,
	}
	i.clear()
	theImages.add(i)
	return i
}

// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewScreenFramebufferImage(width, height int) *Image {
	i := &Image{
		image:  graphicscommand.NewScreenFramebufferImage(width, height),
		screen: true,
	}
	i.clear()
	theImages.add(i)
	return i
}

func (i *Image) clear() {
	// There are not 'drawImageHistoryItem's for this image and dummyImage.
	// This means dummyImage might not be restored yet when this image is restored.
	// However, that's ok since this image will be stale or have its updated pixel data soon,
	// and this image can be restored without dummyImage.
	//
	// dummyImage should be restored later anyway.
	w, h := i.Size()
	sw, sh := dummyImage.Size()
	dw := graphics.NextPowerOf2Int(w)
	dh := graphics.NextPowerOf2Int(h)
	vs := graphics.QuadVertices(dw, dh, 0, 0, sw, sh,
		float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh),
		0, 0,
		1, 1, 1, 1)
	is := graphics.QuadIndices()
	i.image.DrawImage(dummyImage.image, vs, is, nil, graphics.CompositeModeClear, graphics.FilterNearest, graphics.AddressClampToZero)

	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
}

func (i *Image) IsVolatile() bool {
	return i.volatile
}

// BasePixelsForTesting returns the image's basePixels for testing.
func (i *Image) BasePixelsForTesting() []byte {
	return i.basePixels
}

// Pixels returns the image's pixel bytes.
//
// Pixels tries to read pixels from GPU if needed.
// It is assured that GPU is not accessed if the opration against the image is only ReplacePixels.
func (i *Image) Pixels() []byte {
	i.readPixelsFromGPUIfNeeded()
	return i.basePixels
}

// Size returns the image's size.
func (i *Image) Size() (int, int) {
	return i.image.Size()
}

// SizePowerOf2 returns the next power of 2 values for the size.
func (i *Image) SizePowerOf2() (int, int) {
	if i.w2 == 0 || i.h2 == 0 {
		w, h := i.image.Size()
		i.w2 = graphics.NextPowerOf2Int(w)
		i.h2 = graphics.NextPowerOf2Int(h)
	}
	return i.w2, i.h2
}

// makeStale makes the image stale.
func (i *Image) makeStale() {
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = true

	// Don't have to call makeStale recursively here.
	// Restoring is done after topological sorting is done.
	// If an image depends on another stale image, this means that
	// the former image can be restored from the latest state of the latter image.
}

// ReplacePixels replaces the image pixels with the given pixels slice.
//
// If pixels is nil, ReplacePixels clears the specified reagion.
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
	w, h := i.image.Size()
	if width <= 0 || height <= 0 {
		panic("restorable: width/height must be positive")
	}
	if x < 0 || y < 0 || w <= x || h <= y || x+width <= 0 || y+height <= 0 || w < x+width || h < y+height {
		panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height))
	}

	// TODO: Avoid making other images stale if possible. (#514)
	// For this purpuse, images should remember which part of that is used for DrawImage.
	theImages.makeStaleIfDependingOn(i)

	if pixels == nil {
		pixels = make([]byte, 4*width*height)
	}
	i.image.ReplacePixels(pixels, x, y, width, height)

	if x == 0 && y == 0 && width == w && height == h {
		if pixels != nil {
			if i.basePixels == nil {
				i.basePixels = make([]byte, 4*w*h)
			}
			copy(i.basePixels, pixels)
		} else {
			// If basePixels is nil, the restored pixels are cleared.
			// See restore() implementation.
			i.basePixels = nil
		}
		i.drawImageHistory = nil
		i.stale = false
		return
	}

	if len(i.drawImageHistory) > 0 {
		panic("restorable: ReplacePixels for a part after DrawImage is forbidden")
	}

	if i.stale {
		return
	}

	idx := 4 * (y*w + x)
	if pixels != nil {
		if i.basePixels == nil {
			i.basePixels = make([]byte, 4*w*h)
		}
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width])
			idx += 4 * w
		}
	} else if i.basePixels != nil {
		zeros := make([]byte, 4*width)
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], zeros)
			idx += 4 * w
		}
	}
}

// DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter, address graphics.Address) {
	if len(vertices) == 0 {
		return
	}
	theImages.makeStaleIfDependingOn(i)

	if img.stale || img.volatile || i.screen || !IsRestoringEnabled() {
		i.makeStale()
	} else {
		i.appendDrawImageHistory(img, vertices, indices, colorm, mode, filter, address)
	}
	i.image.DrawImage(img.image, vertices, indices, colorm, mode, filter, address)
}

// appendDrawImageHistory appends a draw-image history item to the image.
func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter, address graphics.Address) {
	if i.stale || i.volatile || i.screen {
		return
	}
	const maxDrawImageHistoryNum = 100
	if len(i.drawImageHistory)+1 > maxDrawImageHistoryNum {
		i.makeStale()
		return
	}
	// All images must be resolved and not stale each after frame.
	// So we don't have to care if image is stale or not here.
	item := &drawImageHistoryItem{
		image:    image,
		vertices: vertices,
		indices:  indices,
		colorm:   colorm,
		mode:     mode,
		filter:   filter,
		address:  address,
	}
	i.drawImageHistory = append(i.drawImageHistory, item)
}

func (i *Image) readPixelsFromGPUIfNeeded() {
	if i.basePixels == nil || i.drawImageHistory != nil || i.stale {
		graphicscommand.FlushCommands()
		i.readPixelsFromGPU()
		i.drawImageHistory = nil
		i.stale = false
	}
}

// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) color.RGBA {
	w, h := i.image.Size()
	if x < 0 || y < 0 || w <= x || h <= y {
		return color.RGBA{}
	}

	i.readPixelsFromGPUIfNeeded()

	// Even after readPixelsFromGPU, basePixels might be nil when OpenGL error happens.
	if i.basePixels == nil {
		return color.RGBA{}
	}

	idx := 4*x + 4*y*w
	r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
	if i.stale {
		return
	}
	if i.dependsOn(target) {
		i.makeStale()
	}
}

// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU() {
	i.basePixels = i.image.Pixels()
	i.drawImageHistory = nil
	i.stale = false
}

// resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() {
	if !IsRestoringEnabled() {
		return
	}

	if i.volatile {
		return
	}
	if i.screen {
		return
	}
	if !i.stale {
		return
	}
	i.readPixelsFromGPU()
}

// dependsOn returns a boolean value indicating whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
	for _, c := range i.drawImageHistory {
		if c.image == target {
			return true
		}
	}
	return false
}

// dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} {
	r := map[*Image]struct{}{}
	for _, c := range i.drawImageHistory {
		r[c.image] = struct{}{}
	}
	return r
}

// hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool {
	if i.stale {
		return false
	}
	return len(i.drawImageHistory) > 0
}

// Restore restores *graphicscommand.Image from the pixels using its state.
func (i *Image) restore() error {
	w, h := i.image.Size()
	if i.screen {
		// The screen image should also be recreated because framebuffer might
		// be changed.
		i.image = graphicscommand.NewScreenFramebufferImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.volatile {
		i.image = graphicscommand.NewImage(w, h)
		i.clear()
		return nil
	}
	if i.stale {
		// TODO: panic here?
		return errors.New("restorable: pixels must not be stale when restoring")
	}

	gimg := graphicscommand.NewImage(w, h)
	if i.basePixels != nil {
		gimg.ReplacePixels(i.basePixels, 0, 0, w, h)
	} else {
		// Clear the image explicitly.
		pix := make([]uint8, w*h*4)
		gimg.ReplacePixels(pix, 0, 0, w, h)
	}
	for _, c := range i.drawImageHistory {
		// All dependencies must be already resolved.
		if c.image.hasDependency() {
			panic("not reached")
		}
		gimg.DrawImage(c.image.image, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address)
	}
	i.image = gimg

	i.basePixels = gimg.Pixels()
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// Dispose disposes the image.
//
// After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() {
	theImages.remove(i)

	i.image.Dispose()
	i.image = nil
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
}

// IsInvalidated returns a boolean value indicating whether the image is invalidated.
//
// If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) {
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	graphicscommand.FlushCommands()
	if !IsRestoringEnabled() {
		return false, nil
	}

	return i.image.IsInvalidated(), nil
}
<|endoftext|>