<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
)

func main() {
	var file string
	var bucket string
	i := impl{}

	cli.AppHelpTemplate = `NAME:
  {{.Name}} - {{.Usage}}

VERSION:
  {{.Version}}

USAGE:
  {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}

GLOBAL OPTIONS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
AUTHOR:
  {{range .Authors}}{{ . }}{{end}}
`
	app := cli.NewApp()
	app.Name = "bolter"
	app.Usage = "view boltdb file in your terminal"
	app.Version = "1.0.0"
	app.Author = "Hasit Mistry"
	app.Email = "hasitnm@gmail.com"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "file, f",
			Usage:       "boltdb `FILE` to view",
			Destination: &file,
		},
		cli.StringFlag{
			Name:        "bucket, b",
			Usage:       "boltdb `BUCKET` to view",
			Destination: &bucket,
		},
	}
	app.Action = func(c *cli.Context) error {
		if file != "" {
			if _, err := os.Stat(file); os.IsNotExist(err) {
				log.Fatal(err)
				return err
			}
			i.initDB(file)
			defer i.DB.Close()
			if bucket != "" {
				i.listBucketItems(bucket)
			} else {
				i.listBuckets()
			}
		} else {
			cli.ShowAppHelp(c)
		}
		return nil
	}
	app.Run(os.Args)
}

type impl struct {
	DB *bolt.DB
}

type item struct {
	Key   string `json:"Key"`
	Value string `json:"Value"`
}

type bucket struct {
	Name string `json:"Name"`
}

func (i *impl) initDB(file string) {
	var err error
	// Read-only permission
	i.DB, err = bolt.Open(file, 0400, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func (i *impl) listBucketItems(bucket string) {
	items := []item{}
	err := i.DB.View(func(tx *bolt.Tx) error {
		nbs := strings.Split(bucket, ".")
		b := tx.Bucket([]byte(nbs[0]))
		if b == nil {
			return nil
		}
		for _, nb := range nbs[1:] {
			b = b.Bucket([]byte(nb))
			if b == nil {
				return nil
			}
		}
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if v == nil {
				k = append(k, byte('*'))
			}
			items = append(items, item{Key: string(k), Value: string(v)})
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Bucket: %s\n", bucket)
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Key", "Value"})
	for _, item := range items {
		row := []string{item.Key, item.Value}
		table.Append(row)
	}
	table.Render()
}

func (i *impl) listBuckets() {
	buckets := []bucket{}
	err := i.DB.View(func(tx *bolt.Tx) error {
		return tx.ForEach(func(bucketname []byte, _ *bolt.Bucket) error {
			buckets = append(buckets, bucket{Name: string(bucketname)})
			return nil
		})
	})
	if err != nil {
		log.Fatal(err)
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Buckets"})
	for _, b := range buckets {
		row := []string{b.Name}
		table.Append(row)
	}
	table.Render()
}
<commit_msg>Fixes #4<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
)

func main() {
	var file string
	var bucket string
	i := impl{}

	cli.AppHelpTemplate = `NAME:
  {{.Name}} - {{.Usage}}

VERSION:
  {{.Version}}

USAGE:
  {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}

GLOBAL OPTIONS:
  {{range .VisibleFlags}}{{.}}
  {{end}}
AUTHOR:
  {{range .Authors}}{{ . }}{{end}}
`
	app := cli.NewApp()
	app.Name = "bolter"
	app.Usage = "view boltdb file in your terminal"
	app.Version = "1.0.0"
	app.Author = "Hasit Mistry"
	app.Email = "hasitnm@gmail.com"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "file, f",
			Usage:       "boltdb `FILE` to view",
			Destination: &file,
		},
		cli.StringFlag{
			Name:        "bucket, b",
			Usage:       "boltdb `BUCKET` to view",
			Destination: &bucket,
		},
	}
	app.Action = func(c *cli.Context) error {
		if file != "" {
			if _, err := os.Stat(file); os.IsNotExist(err) {
				log.Fatal(err)
				return err
			}
			i.initDB(file)
			defer i.DB.Close()
			if bucket != "" {
				i.listBucketItems(bucket)
			} else {
				i.listBuckets()
			}
		} else {
			cli.ShowAppHelp(c)
		}
		return nil
	}
	app.Run(os.Args)
}

type impl struct {
	DB *bolt.DB
}

type item struct {
	Key   string `json:"Key"`
	Value string `json:"Value"`
}

type bucket struct {
	Name string `json:"Name"`
}

func (i *impl) initDB(file string) {
	var err error
	// Read-only permission
	i.DB, err = bolt.Open(file, 0400, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func (i *impl) listBucketItems(bucket string) {
	items := []item{}
	err := i.DB.View(func(tx *bolt.Tx) error {
		if i := len(bucket) - 1; bucket[i:] == "." {
			bucket = bucket[:i]
		}
		nbs := strings.Split(bucket, ".")
		b := tx.Bucket([]byte(nbs[0]))
		if b == nil {
			return nil
		}
		for _, nb := range nbs[1:] {
			b = b.Bucket([]byte(nb))
			if b == nil {
				return nil
			}
		}
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if v == nil {
				k = append(k, byte('*'))
			}
			items = append(items, item{Key: string(k), Value: string(v)})
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Bucket: %s\n", bucket)
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Key", "Value"})
	for _, item := range items {
		row := []string{item.Key, item.Value}
		table.Append(row)
	}
	table.Render()
}

func (i *impl) listBuckets() {
	buckets := []bucket{}
	err := i.DB.View(func(tx *bolt.Tx) error {
		return tx.ForEach(func(bucketname []byte, _ *bolt.Bucket) error {
			buckets = append(buckets, bucket{Name: string(bucketname)})
			return nil
		})
	})
	if err != nil {
		log.Fatal(err)
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Buckets"})
	for _, b := range buckets {
		row := []string{b.Name}
		table.Append(row)
	}
	table.Render()
}
<|endoftext|>


<commit_before>package pmb

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/loggo/loggo"
)

var logger = loggo.GetLogger("api")

type PMBConfig map[string]string

type PMB struct {
	config PMBConfig
}

func GetPMB(uris map[string]string) *PMB {
	config := getConfig(uris)

	return &PMB{config: config}
}

func getConfig(uris map[string]string) PMBConfig {
	config := make(PMBConfig)

	if len(uris["primary"]) > 0 {
		config["primary"] = uris["primary"]
	} else if primaryURI := os.Getenv("PMB_PRIMARY_URI"); len(primaryURI) > 0 {
		config["primary"] = primaryURI
	}

	if key := os.Getenv("PMB_KEY"); len(key) > 0 {
		config["key"] = key
	} else {
		config["key"] = ""
	}
	logger.Debugf("Config: %s", config)

	return config
}

func (pmb *PMB) GetConnection(id string, isIntroducer bool) (*Connection, error) {

	if len(pmb.config["primary"]) > 0 {
		return connectWithKey(pmb.config["primary"], id, pmb.config["key"], isIntroducer)
	}

	return nil, errors.New("No URI found, use '-p' to specify one")
}

func connectWithKey(URI string, id string, key string, isIntroducer bool) (*Connection, error) {
	conn, err := connect(URI, id)
	if err != nil {
		return nil, err
	}

	if len(key) > 0 {
		conn.Key = key

		// if we're not the introducer, check if the auth is valid
		if !isIntroducer {
			err = testAuth(conn, id)
			if err != nil {
				return nil, err
			}
		}

		return conn, nil

	} else {

		// keep requesting auth until we can verify that it's valid
		for {
			conn.Key = ""
			conn.Key, err = requestSecret(conn)
			if err != nil {
				return nil, err
			}

			err = testAuth(conn, id)
			if err != nil {
				logger.Warningf("Error with key: %s", err)
			} else {
				return conn, nil
			}
		}
	}

	return conn, nil
}

func testAuth(conn *Connection, id string) error {

	conn.Out <- Message{Contents: map[string]interface{}{
		"type": "TestAuth",
	}}

	timeout := time.After(1 * time.Second)
	for {
		select {
		case message := <-conn.In:
			data := message.Contents
			if data["type"].(string) == "AuthValid" && data["origin"].(string) == id {
				return nil
			}
		case _ = <-timeout:
			return fmt.Errorf("Auth key was invalid.")
		}
	}
}

func requestSecret(conn *Connection) (string, error) {
	data := map[string]interface{}{
		"type": "RequestAuth",
	}
	conn.Out <- Message{Contents: data}

	time.Sleep(200 * time.Millisecond)

	tty, err := os.Open("/dev/tty")
	if err != nil {
		fmt.Errorf("failed to open /dev/tty", err)
	}

	fmt.Printf("Enter secret: ")
	secret, err := bufio.NewReader(tty).ReadString('\n')
	if err != nil {
		return "", err
	}

	return strings.TrimSpace(secret), nil
}

func (pmb *PMB) PrimaryURI() string {
	return pmb.config["primary"]
}
<commit_msg>wait longer for auth confirmation<commit_after>package pmb

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/loggo/loggo"
)

var logger = loggo.GetLogger("api")

type PMBConfig map[string]string

type PMB struct {
	config PMBConfig
}

func GetPMB(uris map[string]string) *PMB {
	config := getConfig(uris)

	return &PMB{config: config}
}

func getConfig(uris map[string]string) PMBConfig {
	config := make(PMBConfig)

	if len(uris["primary"]) > 0 {
		config["primary"] = uris["primary"]
	} else if primaryURI := os.Getenv("PMB_PRIMARY_URI"); len(primaryURI) > 0 {
		config["primary"] = primaryURI
	}

	if key := os.Getenv("PMB_KEY"); len(key) > 0 {
		config["key"] = key
	} else {
		config["key"] = ""
	}
	logger.Debugf("Config: %s", config)

	return config
}

func (pmb *PMB) GetConnection(id string, isIntroducer bool) (*Connection, error) {

	if len(pmb.config["primary"]) > 0 {
		return connectWithKey(pmb.config["primary"], id, pmb.config["key"], isIntroducer)
	}

	return nil, errors.New("No URI found, use '-p' to specify one")
}

func connectWithKey(URI string, id string, key string, isIntroducer bool) (*Connection, error) {
	conn, err := connect(URI, id)
	if err != nil {
		return nil, err
	}

	if len(key) > 0 {
		conn.Key = key

		// if we're not the introducer, check if the auth is valid
		if !isIntroducer {
			err = testAuth(conn, id)
			if err != nil {
				return nil, err
			}
		}

		return conn, nil

	} else {

		// keep requesting auth until we can verify that it's valid
		for {
			conn.Key = ""
			conn.Key, err = requestSecret(conn)
			if err != nil {
				return nil, err
			}

			err = testAuth(conn, id)
			if err != nil {
				logger.Warningf("Error with key: %s", err)
			} else {
				return conn, nil
			}
		}
	}

	return conn, nil
}

func testAuth(conn *Connection, id string) error {

	conn.Out <- Message{Contents: map[string]interface{}{
		"type": "TestAuth",
	}}

	timeout := time.After(10 * time.Second)
	for {
		select {
		case message := <-conn.In:
			data := message.Contents
			if data["type"].(string) == "AuthValid" && data["origin"].(string) == id {
				return nil
			}
		case _ = <-timeout:
			return fmt.Errorf("Auth key was invalid.")
		}
	}
}

func requestSecret(conn *Connection) (string, error) {
	data := map[string]interface{}{
		"type": "RequestAuth",
	}
	conn.Out <- Message{Contents: data}

	time.Sleep(200 * time.Millisecond)

	tty, err := os.Open("/dev/tty")
	if err != nil {
		fmt.Errorf("failed to open /dev/tty", err)
	}

	fmt.Printf("Enter secret: ")
	secret, err := bufio.NewReader(tty).ReadString('\n')
	if err != nil {
		return "", err
	}

	return strings.TrimSpace(secret), nil
}

func (pmb *PMB) PrimaryURI() string {
	return pmb.config["primary"]
}
<|endoftext|>


<commit_before>package agent

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/hashicorp/serf/serf"
	"github.com/mitchellh/mapstructure"
	"io"
	"net"
	"os"
	"path/filepath"
	"strings"
)

// This is the default port that we use for Serf communication
const DefaultBindPort int = 7946

// DefaultConfig contains the defaults for configurations.
var DefaultConfig = &Config{
	BindAddr:     "0.0.0.0",
	LogLevel:     "INFO",
	RPCAddr:      "127.0.0.1:7373",
	Protocol:     serf.ProtocolVersionMax,
	ReplayOnJoin: false,
	Profile:      "lan",
}

// Config is the configuration that can be set for an Agent. Some of these
// configurations are exposed as command-line flags to `serf agent`, whereas
// many of the more advanced configurations can only be set by creating
// a configuration file.
type Config struct {
	// All the configurations in this section are identical to their
	// Serf counterparts. See the documentation for Serf.Config for
	// more info.
	NodeName string `mapstructure:"node_name"`
	Role     string `mapstructure:"role"`

	// BindAddr is the address that the Serf agent's communication ports
	// will bind to. Serf will use this address to bind to for both TCP
	// and UDP connections. If no port is present in the address, the default
	// port will be used.
	BindAddr string `mapstructure:"bind"`

	// EncryptKey is the secret key to use for encrypting communication
	// traffic for Serf. The secret key must be exactly 16-bytes, base64
	// encoded. The easiest way to do this on Unix machines is this command:
	// "head -c16 /dev/urandom | base64". If this is not specified, the
	// traffic will not be encrypted.
	EncryptKey string `mapstructure:"encrypt_key"`

	// LogLevel is the level of the logs to output.
	LogLevel string `mapstructure:"log_level"`

	// RPCAddr is the address and port to listen on for the agent's RPC
	// interface.
	RPCAddr string `mapstructure:"rpc_addr"`

	// Protocol is the Serf protocol version to use.
	Protocol int `mapstructure:"protocol"`

	// ReplayOnJoin tells Serf to replay past user events
	// when joining based on a `StartJoin`.
	ReplayOnJoin bool `mapstructure:"replay_on_join"`

	// StartJoin is a list of addresses to attempt to join when the
	// agent starts. If Serf is unable to communicate with any of these
	// addresses, then the agent will error and exit.
	StartJoin []string `mapstructure:"start_join"`

	// EventHandlers is a list of event handlers that will be invoked.
	EventHandlers []string `mapstructure:"event_handlers"`

	// Profile is used to select a timing profile for Serf. The supported choices
	// are "wan", "lan", and "local". The default is "lan"
	Profile string `mapstructure:"profile"`

	// SnapshotPath is used to allow Serf to snapshot important transactional
	// state to make a more graceful recovery possible. This enables auto
	// re-joining a cluster on failure and avoids old message replay.
	SnapshotPath string `mapstructure:"snapshot_path"`
}

// BindAddrParts returns the parts of the BindAddr that should be
// used to configure Serf.
func (c *Config) BindAddrParts() (string, int, error) {
	checkAddr := c.BindAddr

START:
	_, _, err := net.SplitHostPort(checkAddr)
	if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
		checkAddr = fmt.Sprintf("%s:%d", checkAddr, DefaultBindPort)
		goto START
	}
	if err != nil {
		return "", 0, err
	}

	// Get the address
	addr, err := net.ResolveTCPAddr("tcp", checkAddr)
	if err != nil {
		return "", 0, err
	}

	return addr.IP.String(), addr.Port, nil
}

// EncryptBytes returns the encryption key configured.
func (c *Config) EncryptBytes() ([]byte, error) {
	return base64.StdEncoding.DecodeString(c.EncryptKey)
}

// EventScripts returns the list of EventScripts associated with this
// configuration and specified by the "event_handlers" configuration.
func (c *Config) EventScripts() []EventScript {
	result := make([]EventScript, 0, len(c.EventHandlers))
	for _, v := range c.EventHandlers {
		part := ParseEventScript(v)
		result = append(result, part...)
	}
	return result
}

// DecodeConfig reads the configuration from the given reader in JSON
// format and decodes it into a proper Config structure.
func DecodeConfig(r io.Reader) (*Config, error) {
	var raw interface{}
	dec := json.NewDecoder(r)
	if err := dec.Decode(&raw); err != nil {
		return nil, err
	}

	// Decode
	var md mapstructure.Metadata
	var result Config
	msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Metadata: &md,
		Result:   &result,
	})
	if err != nil {
		return nil, err
	}

	if err := msdec.Decode(raw); err != nil {
		return nil, err
	}

	// If we never set the protocol, then set it to the default
	found := false
	for _, k := range md.Keys {
		if k == "protocol" {
			found = true
			break
		}
	}

	if !found {
		result.Protocol = DefaultConfig.Protocol
	}

	return &result, nil
}

// MergeConfig merges two configurations together to make a single new
// configuration.
func MergeConfig(a, b *Config) *Config {
	var result Config = *a

	// Copy the strings if they're set
	if b.NodeName != "" {
		result.NodeName = b.NodeName
	}
	if b.Role != "" {
		result.Role = b.Role
	}
	if b.BindAddr != "" {
		result.BindAddr = b.BindAddr
	}
	if b.EncryptKey != "" {
		result.EncryptKey = b.EncryptKey
	}
	if b.LogLevel != "" {
		result.LogLevel = b.LogLevel
	}
	if b.Protocol >= 0 {
		result.Protocol = b.Protocol
	}
	if b.RPCAddr != "" {
		result.RPCAddr = b.RPCAddr
	}
	if b.ReplayOnJoin != false {
		result.ReplayOnJoin = b.ReplayOnJoin
	}
	if b.Profile != "" {
		result.Profile = b.Profile
	}
	if b.SnapshotPath != "" {
		result.SnapshotPath = b.SnapshotPath
	}

	// Copy the event handlers
	result.EventHandlers = make([]string, 0, len(a.EventHandlers)+len(b.EventHandlers))
	result.EventHandlers = append(result.EventHandlers, a.EventHandlers...)
	result.EventHandlers = append(result.EventHandlers, b.EventHandlers...)

	// Copy the start join addresses
	result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin))
	result.StartJoin = append(result.StartJoin, a.StartJoin...)
	result.StartJoin = append(result.StartJoin, b.StartJoin...)

	return &result
}

// ReadConfigPaths reads the paths in the given order to load configurations.
// The paths can be to files or directories. If the path is a directory,
// we read one directory deep and read any files ending in ".json" as
// configuration files.
func ReadConfigPaths(paths []string) (*Config, error) {
	result := new(Config)
	for _, path := range paths {
		f, err := os.Open(path)
		if err != nil {
			return nil, fmt.Errorf("Error reading '%s': %s", path, err)
		}

		fi, err := f.Stat()
		if err != nil {
			f.Close()
			return nil, fmt.Errorf("Error reading '%s': %s", path, err)
		}

		if !fi.IsDir() {
			config, err := DecodeConfig(f)
			f.Close()

			if err != nil {
				return nil, fmt.Errorf("Error decoding '%s': %s", path, err)
			}

			result = MergeConfig(result, config)
			continue
		}

		contents, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			return nil, fmt.Errorf("Error reading '%s': %s", path, err)
		}

		for _, fi := range contents {
			// Don't recursively read contents
			if fi.IsDir() {
				continue
			}

			// If it isn't a JSON file, ignore it
			if !strings.HasSuffix(fi.Name(), ".json") {
				continue
			}

			subpath := filepath.Join(path, fi.Name())
			f, err := os.Open(subpath)
			if err != nil {
				return nil, fmt.Errorf("Error reading '%s': %s", subpath, err)
			}

			config, err := DecodeConfig(f)
			f.Close()

			if err != nil {
				return nil, fmt.Errorf("Error decoding '%s': %s", subpath, err)
			}

			result = MergeConfig(result, config)
		}
	}

	return result, nil
}
<commit_msg>agent: Adding flags to control signal handling<commit_after>package agent

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/hashicorp/serf/serf"
	"github.com/mitchellh/mapstructure"
	"io"
	"net"
	"os"
	"path/filepath"
	"strings"
)

// This is the default port that we use for Serf communication
const DefaultBindPort int = 7946

// DefaultConfig contains the defaults for configurations.
var DefaultConfig = &Config{
	BindAddr:     "0.0.0.0",
	LogLevel:     "INFO",
	RPCAddr:      "127.0.0.1:7373",
	Protocol:     serf.ProtocolVersionMax,
	ReplayOnJoin: false,
	Profile:      "lan",
	LeaveOnInt:   true,
}

// Config is the configuration that can be set for an Agent. Some of these
// configurations are exposed as command-line flags to `serf agent`, whereas
// many of the more advanced configurations can only be set by creating
// a configuration file.
type Config struct {
	// All the configurations in this section are identical to their
	// Serf counterparts. See the documentation for Serf.Config for
	// more info.
	NodeName string `mapstructure:"node_name"`
	Role     string `mapstructure:"role"`

	// BindAddr is the address that the Serf agent's communication ports
	// will bind to. Serf will use this address to bind to for both TCP
	// and UDP connections. If no port is present in the address, the default
	// port will be used.
	BindAddr string `mapstructure:"bind"`

	// EncryptKey is the secret key to use for encrypting communication
	// traffic for Serf. The secret key must be exactly 16-bytes, base64
	// encoded. The easiest way to do this on Unix machines is this command:
	// "head -c16 /dev/urandom | base64". If this is not specified, the
	// traffic will not be encrypted.
	EncryptKey string `mapstructure:"encrypt_key"`

	// LogLevel is the level of the logs to output.
	LogLevel string `mapstructure:"log_level"`

	// RPCAddr is the address and port to listen on for the agent's RPC
	// interface.
	RPCAddr string `mapstructure:"rpc_addr"`

	// Protocol is the Serf protocol version to use.
	Protocol int `mapstructure:"protocol"`

	// ReplayOnJoin tells Serf to replay past user events
	// when joining based on a `StartJoin`.
	ReplayOnJoin bool `mapstructure:"replay_on_join"`

	// StartJoin is a list of addresses to attempt to join when the
	// agent starts. If Serf is unable to communicate with any of these
	// addresses, then the agent will error and exit.
	StartJoin []string `mapstructure:"start_join"`

	// EventHandlers is a list of event handlers that will be invoked.
	EventHandlers []string `mapstructure:"event_handlers"`

	// Profile is used to select a timing profile for Serf. The supported choices
	// are "wan", "lan", and "local". The default is "lan"
	Profile string `mapstructure:"profile"`

	// SnapshotPath is used to allow Serf to snapshot important transactional
	// state to make a more graceful recovery possible. This enables auto
	// re-joining a cluster on failure and avoids old message replay.
	SnapshotPath string `mapstructure:"snapshot_path"`

	// LeaveOnTerm controls if Serf does a graceful leave when receiving
	// the TERM signal. Defaults false.
	LeaveOnTerm bool `mapstructure:"leave_on_terminate"`

	// LeaveOnInt controls if Serf does a graceful leave when receiving
	// the INT signal. Defaults true.
	LeaveOnInt bool `mapstructure:"leave_on_interrupt"`
}

// BindAddrParts returns the parts of the BindAddr that should be
// used to configure Serf.
func (c *Config) BindAddrParts() (string, int, error) {
	checkAddr := c.BindAddr

START:
	_, _, err := net.SplitHostPort(checkAddr)
	if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
		checkAddr = fmt.Sprintf("%s:%d", checkAddr, DefaultBindPort)
		goto START
	}
	if err != nil {
		return "", 0, err
	}

	// Get the address
	addr, err := net.ResolveTCPAddr("tcp", checkAddr)
	if err != nil {
		return "", 0, err
	}

	return addr.IP.String(), addr.Port, nil
}

// EncryptBytes returns the encryption key configured.
func (c *Config) EncryptBytes() ([]byte, error) {
	return base64.StdEncoding.DecodeString(c.EncryptKey)
}

// EventScripts returns the list of EventScripts associated with this
// configuration and specified by the "event_handlers" configuration.
func (c *Config) EventScripts() []EventScript {
	result := make([]EventScript, 0, len(c.EventHandlers))
	for _, v := range c.EventHandlers {
		part := ParseEventScript(v)
		result = append(result, part...)
	}
	return result
}

// DecodeConfig reads the configuration from the given reader in JSON
// format and decodes it into a proper Config structure.
func DecodeConfig(r io.Reader) (*Config, error) {
	var raw interface{}
	dec := json.NewDecoder(r)
	if err := dec.Decode(&raw); err != nil {
		return nil, err
	}

	// Decode
	var md mapstructure.Metadata
	var result Config
	msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Metadata: &md,
		Result:   &result,
	})
	if err != nil {
		return nil, err
	}

	if err := msdec.Decode(raw); err != nil {
		return nil, err
	}

	// If we never set the protocol, then set it to the default
	found := false
	for _, k := range md.Keys {
		if k == "protocol" {
			found = true
			break
		}
	}

	if !found {
		result.Protocol = DefaultConfig.Protocol
	}

	return &result, nil
}

// MergeConfig merges two configurations together to make a single new
// configuration.
func MergeConfig(a, b *Config) *Config {
	var result Config = *a

	// Copy the strings if they're set
	if b.NodeName != "" {
		result.NodeName = b.NodeName
	}
	if b.Role != "" {
		result.Role = b.Role
	}
	if b.BindAddr != "" {
		result.BindAddr = b.BindAddr
	}
	if b.EncryptKey != "" {
		result.EncryptKey = b.EncryptKey
	}
	if b.LogLevel != "" {
		result.LogLevel = b.LogLevel
	}
	if b.Protocol >= 0 {
		result.Protocol = b.Protocol
	}
	if b.RPCAddr != "" {
		result.RPCAddr = b.RPCAddr
	}
	if b.ReplayOnJoin != false {
		result.ReplayOnJoin = b.ReplayOnJoin
	}
	if b.Profile != "" {
		result.Profile = b.Profile
	}
	if b.SnapshotPath != "" {
		result.SnapshotPath = b.SnapshotPath
	}

	// Copy the event handlers
	result.EventHandlers = make([]string, 0, len(a.EventHandlers)+len(b.EventHandlers))
	result.EventHandlers = append(result.EventHandlers, a.EventHandlers...)
	result.EventHandlers = append(result.EventHandlers, b.EventHandlers...)

	// Copy the start join addresses
	result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin))
	result.StartJoin = append(result.StartJoin, a.StartJoin...)
	result.StartJoin = append(result.StartJoin, b.StartJoin...)

	return &result
}

// ReadConfigPaths reads the paths in the given order to load configurations.
// The paths can be to files or directories. If the path is a directory,
// we read one directory deep and read any files ending in ".json" as
// configuration files.
func ReadConfigPaths(paths []string) (*Config, error) {
	result := new(Config)
	for _, path := range paths {
		f, err := os.Open(path)
		if err != nil {
			return nil, fmt.Errorf("Error reading '%s': %s", path, err)
		}

		fi, err := f.Stat()
		if err != nil {
			f.Close()
			return nil, fmt.Errorf("Error reading '%s': %s", path, err)
		}

		if !fi.IsDir() {
			config, err := DecodeConfig(f)
			f.Close()

			if err != nil {
				return nil, fmt.Errorf("Error decoding '%s': %s", path, err)
			}

			result = MergeConfig(result, config)
			continue
		}

		contents, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			return nil, fmt.Errorf("Error reading '%s': %s", path, err)
		}

		for _, fi := range contents {
			// Don't recursively read contents
			if fi.IsDir() {
				continue
			}

			// If it isn't a JSON file, ignore it
			if !strings.HasSuffix(fi.Name(), ".json") {
				continue
			}

			subpath := filepath.Join(path, fi.Name())
			f, err := os.Open(subpath)
			if err != nil {
				return nil, fmt.Errorf("Error reading '%s': %s", subpath, err)
			}

			config, err := DecodeConfig(f)
			f.Close()

			if err != nil {
				return nil, fmt.Errorf("Error decoding '%s': %s", subpath, err)
			}

			result = MergeConfig(result, config)
		}
	}

	return result, nil
}
<|endoftext|>


<commit_before>package runkeeper

import (
	"log"
	"time"

	runkeeper "github.com/svdberg/syncmysport-runkeeper/Godeps/_workspace/src/github.com/c9s/go-runkeeper"
	dm "github.com/svdberg/syncmysport-runkeeper/datamodel"
)

const API = "API"

func ConvertToActivity(rkActivity *runkeeper.FitnessActivity) *dm.Activity {
	returnActivity := dm.CreateActivity()
	if rkActivity.Type == "Other" {
		returnActivity.Type = "Activity"
	} else {
		returnActivity.Type = rkActivity.Type
	}

	//RK time is 'Local', convert to UTC
	//TODO. This might be wrong and perhaps should be the negative zone (e.g. -1 * ..)
	negativeOffset := -1 * rkActivity.UtcOffset * 60 * 60
	// sourceLocation := time.FixedZone("RKSourceLocation", negativeOffset)
	// correctedTime := time.Time(rkActivity.StartTime).In(sourceLocation)
	log.Printf("RK Local date: %s, start date: %s, unix: %d, offset: %d", time.Time(rkActivity.StartTime), correctedTime, time.Time(rkActivity.StartTime).Unix(), rkActivity.UtcOffset)
	returnActivity.StartTime = int(rkActivity.StartTime.Unix() + negativeOffset)
	returnActivity.UtcOffSet = rkActivity.UtcOffset
	returnActivity.Duration = int(rkActivity.Duration)
	returnActivity.Name = rkActivity.Notes
	returnActivity.Notes = rkActivity.Notes
	returnActivity.Private = false
	returnActivity.Stationary = rkActivity.HasMap
	returnActivity.AverageHeartRate = rkActivity.AverageHeartRate
	returnActivity.Calories = rkActivity.TotalCalories
	returnActivity.Distance = rkActivity.TotalDistance
	returnActivity.GPS = convertFromPath(rkActivity.Path)
	returnActivity.HeartRate = convertFromHR(rkActivity.HeartRate)

	// log.Printf("INPUT: %s, OUTPUT: %s", rkActivity, returnActivity)
	return returnActivity
}

func ConvertToRkActivity(activity *dm.Activity) *runkeeper.FitnessActivityNew {
	rkActivity := runkeeper.CreateNewFitnessActivity(activity.Name, float64(activity.Duration))

	rkActivity.Type = activity.Type
	//runkeeper only nows the following types:
	//Running, Cycling, Mountain Biking, Walking,
	//Hiking, Downhill Skiing, Cross-Country Skiing,
	//Snowboarding, Skating, Swimming, Wheelchair, Rowing, Elliptical, Other
	//
	//check if Type is one of these, otherwise Other.
	rkKnownTypes := map[string]string{
		"Running":              "Running",
		"Cycling":              "Cycling",
		"Mountain Biking":      "Mountain Biking",
		"Walking":              "Walking",
		"Hiking":               "Hiking",
		"Downhill Skiing":      "Downhill Skiing",
		"Cross-Country Skiing": "Cross-Country Skiing",
		"Snowboarding":         "Snowboarding",
		"Skating":              "Skating",
		"Swimming":             "Swimming",
		"Wheelchair":           "Wheelchair",
		"Rowing":               "Rowing",
		"Elliptical":           "Elliptical",
		"Other":                "Other"}

	_, ok := rkKnownTypes[activity.Type]
	if !ok {
		rkActivity.Type = "Other"
	}

	//runkeeper times are in local timezones, so covert back to the local time
	rkLocalLocation := time.FixedZone("rkZone", activity.UtcOffSet*60*60)
	rkActivity.StartTime = runkeeper.Time(time.Unix(int64(activity.StartTime), 0).In(rkLocalLocation))
	log.Printf("SMS time: %s, converted to RK time: %s for offset: %d", activity.StartTime, rkActivity.StartTime, activity.UtcOffSet)
	rkActivity.Notes = activity.Name
	rkActivity.TotalDistance = activity.Distance
	rkActivity.AverageHeartRate = activity.AverageHeartRate
	rkActivity.TotalCalories = activity.Calories
	rkActivity.Source = activity.Source
	rkActivity.EntryMode = API

	rkActivity.Path = convertToPath(activity.GPS)
	rkActivity.HeartRate = convertToHR(activity.HeartRate)
	return rkActivity
}

func convertToPath(gps []dm.GPS) []runkeeper.Path {
	rkPath := make([]runkeeper.Path, len(gps))
	for i, gp := range gps {
		rkPath[i] = runkeeper.Path{gp.Altitude, gp.Longitude, "gps", gp.Latitude, gp.Timestamp}
	}
	return rkPath
}

func convertFromPath(path []runkeeper.Path) []dm.GPS {
	dmPath := make([]dm.GPS, len(path))
	for i, rp := range path {
		dmPath[i] = dm.GPS{rp.Timestamp, rp.Altitude, rp.Longitude, rp.Latitude}
	}
	return dmPath
}

func convertToHR(hr []dm.HeartRate) []runkeeper.HeartRate {
	rkHr := make([]runkeeper.HeartRate, len(hr))
	for i, h := range hr {
		rkHr[i] = runkeeper.HeartRate{h.Timestamp, h.Heartrate}
	}
	return rkHr
}

func convertFromHR(rkHr []runkeeper.HeartRate) []dm.HeartRate {
	dmHr := make([]dm.HeartRate, len(rkHr))
	for i, h := range rkHr {
		dmHr[i] = dm.HeartRate{h.TimeStamp, h.HearRateNr}
	}
	return dmHr
}
<commit_msg>Fix Time.time issue<commit_after>package runkeeper

import (
	"log"
	"time"

	runkeeper "github.com/svdberg/syncmysport-runkeeper/Godeps/_workspace/src/github.com/c9s/go-runkeeper"
	dm "github.com/svdberg/syncmysport-runkeeper/datamodel"
)

const API = "API"

func ConvertToActivity(rkActivity *runkeeper.FitnessActivity) *dm.Activity {
	returnActivity := dm.CreateActivity()
	if rkActivity.Type == "Other" {
		returnActivity.Type = "Activity"
	} else {
		returnActivity.Type = rkActivity.Type
	}

	//RK time is 'Local', convert to UTC
	//TODO. This might be wrong and perhaps should be the negative zone (e.g. -1 * ..)
	negativeOffset := -1 * rkActivity.UtcOffset * 60 * 60
	// sourceLocation := time.FixedZone("RKSourceLocation", negativeOffset)
	// correctedTime := time.Time(rkActivity.StartTime).In(sourceLocation)
	// log.Printf("RK Local date: %s, start date: %s, unix: %d, offset: %d", time.Time(rkActivity.StartTime), correctedTime, time.Time(rkActivity.StartTime).Unix(), rkActivity.UtcOffset)
	returnActivity.StartTime = int(time.Time(rkActivity.StartTime).Unix() + int64(negativeOffset))
	returnActivity.UtcOffSet = rkActivity.UtcOffset
	log.Printf("RK Local date: %s, start date: %s, unix: %d, offset: %d", time.Time(rkActivity.StartTime), "NA", time.Time(rkActivity.StartTime).Unix(), rkActivity.UtcOffset)
	returnActivity.Duration = int(rkActivity.Duration)
	returnActivity.Name = rkActivity.Notes
	returnActivity.Notes = rkActivity.Notes
	returnActivity.Private = false
	returnActivity.Stationary = rkActivity.HasMap
	returnActivity.AverageHeartRate = rkActivity.AverageHeartRate
	returnActivity.Calories = rkActivity.TotalCalories
	returnActivity.Distance = rkActivity.TotalDistance
	returnActivity.GPS = convertFromPath(rkActivity.Path)
	returnActivity.HeartRate = convertFromHR(rkActivity.HeartRate)

	// log.Printf("INPUT: %s, OUTPUT: %s", rkActivity, returnActivity)
	return returnActivity
}

func ConvertToRkActivity(activity *dm.Activity) *runkeeper.FitnessActivityNew {
	rkActivity := runkeeper.CreateNewFitnessActivity(activity.Name, float64(activity.Duration))

	rkActivity.Type = activity.Type
	//runkeeper only nows the following types:
	//Running, Cycling, Mountain Biking, Walking,
	//Hiking, Downhill Skiing, Cross-Country Skiing,
	//Snowboarding, Skating, Swimming, Wheelchair, Rowing, Elliptical, Other
	//
	//check if Type is one of these, otherwise Other.
	rkKnownTypes := map[string]string{
		"Running":              "Running",
		"Cycling":              "Cycling",
		"Mountain Biking":      "Mountain Biking",
		"Walking":              "Walking",
		"Hiking":               "Hiking",
		"Downhill Skiing":      "Downhill Skiing",
		"Cross-Country Skiing": "Cross-Country Skiing",
		"Snowboarding":         "Snowboarding",
		"Skating":              "Skating",
		"Swimming":             "Swimming",
		"Wheelchair":           "Wheelchair",
		"Rowing":               "Rowing",
		"Elliptical":           "Elliptical",
		"Other":                "Other"}

	_, ok := rkKnownTypes[activity.Type]
	if !ok {
		rkActivity.Type = "Other"
	}

	//runkeeper times are in local timezones, so covert back to the local time
	rkLocalLocation := time.FixedZone("rkZone", activity.UtcOffSet*60*60)
	rkActivity.StartTime = runkeeper.Time(time.Unix(int64(activity.StartTime), 0).In(rkLocalLocation))
	log.Printf("SMS time: %s, converted to RK time: %s for offset: %d", activity.StartTime, rkActivity.StartTime, activity.UtcOffSet)
	rkActivity.Notes = activity.Name
	rkActivity.TotalDistance = activity.Distance
	rkActivity.AverageHeartRate = activity.AverageHeartRate
	rkActivity.TotalCalories = activity.Calories
	rkActivity.Source = activity.Source
	rkActivity.EntryMode = API

	rkActivity.Path = convertToPath(activity.GPS)
	rkActivity.HeartRate = convertToHR(activity.HeartRate)
	return rkActivity
}

func convertToPath(gps []dm.GPS) []runkeeper.Path {
	rkPath := make([]runkeeper.Path, len(gps))
	for i, gp := range gps {
		rkPath[i] = runkeeper.Path{gp.Altitude, gp.Longitude, "gps", gp.Latitude, gp.Timestamp}
	}
	return rkPath
}

func convertFromPath(path []runkeeper.Path) []dm.GPS {
	dmPath := make([]dm.GPS, len(path))
	for i, rp := range path {
		dmPath[i] = dm.GPS{rp.Timestamp, rp.Altitude, rp.Longitude, rp.Latitude}
	}
	return dmPath
}

func convertToHR(hr []dm.HeartRate) []runkeeper.HeartRate {
	rkHr := make([]runkeeper.HeartRate, len(hr))
	for i, h := range hr {
		rkHr[i] = runkeeper.HeartRate{h.Timestamp, h.Heartrate}
	}
	return rkHr
}

func convertFromHR(rkHr []runkeeper.HeartRate) []dm.HeartRate {
	dmHr := make([]dm.HeartRate, len(rkHr))
	for i, h := range rkHr {
		dmHr[i] = dm.HeartRate{h.TimeStamp, h.HearRateNr}
	}
	return dmHr
}
<|endoftext|>


<commit_before>package newsapi

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

const newsAPIHost = "https://newsapi.org"

type Client struct {
	client http.Client
	apiKey string
}

type ArticlesResponse struct {
	Status       string    `json:"status"`
	TotalResults int       `json:"totalResults"`
	Articles     []Article `json:"articles"`
}

type Article struct {
	Source      Source    `json:"source"`
	Author      string    `json:"author"`
	Title       string    `json:"title"`
	Description string    `json:"description"`
	Url         string    `json:"url"`
	UrlToImage  string    `json:"urlToImage"`
	PublishedAt time.Time `json:"publishedAt"`
	Content     string    `json:"content"`
}

type SourcesResponse struct {
	Status  string   `json:"status"`
	Sources []Source `json:"sources"`
}

type Source struct {
	Id          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Url         string `json:"url"`
	Category    string `json:"category"`
	Language    string `json:"language"`
	Country     string `json:"country"`
}

type RequestError struct {
	Status  string `json:"status"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

func NewClient(httpClient *http.Client, apiKey string) *Client {
	return &Client{
		client: *httpClient,
		apiKey: apiKey,
	}
}

func (e *RequestError) Error() string {
	return fmt.Sprintf("%s - %s - %s", e.Status, e.Code, e.Message)
}

func (c *Client) GetTopHeadlinesBySources(query, sources string) (*ArticlesResponse, error) {
	return c.getHeadlines("/v2/top-headlines", query, sources)
}

func (c *Client) GetAllHeadlinesBySources(query, sources string) (*ArticlesResponse, error) {
	return c.getHeadlines("/v2/everything", query, sources)
}

func (c *Client) getHeadlines(path, query, sources string) (*ArticlesResponse, error) {
	req, err := http.NewRequest("GET", newsAPIHost+path, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-Api-Key", c.apiKey)
	queryParams := req.URL.Query()
	if query != "" {
		queryParams.Add("q", query)
	}
	if sources != "" {
		queryParams.Add("sources", sources)
	}
	req.URL.RawQuery = queryParams.Encode()

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		var requestError RequestError
		if err := json.NewDecoder(resp.Body).Decode(&requestError); err != nil {
			return nil, err
		}
		return nil, &requestError
	}
	var result ArticlesResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}
	return &result, nil
}

func (c *Client) GetSources(category, language, country string) ([]Source, error) {
	req, err := http.NewRequest("GET", newsAPIHost+"/v2/sources", nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-Api-Key", c.apiKey)
	queryParams := req.URL.Query()
	if category != "" {
		queryParams.Add("category", category)
	}
	if language != "" {
		queryParams.Add("language", language)
	}
	if country != "" {
		queryParams.Add("country", country)
	}
	req.URL.RawQuery = queryParams.Encode()

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		var requestError RequestError
		if err := json.NewDecoder(resp.Body).Decode(&requestError); err != nil {
			return nil, err
		}
		return nil, &requestError
	}
	var result SourcesResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}
	return result.Sources, nil
}
<commit_msg>refactor: Return values instead of pointer<commit_after>package newsapi

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

const newsAPIHost = "https://newsapi.org"

type Client struct {
	client http.Client
	apiKey string
}

type ArticlesResponse struct {
	Status       string    `json:"status"`
	TotalResults int       `json:"totalResults"`
	Articles     []Article `json:"articles"`
}

type Article struct {
	Source      Source    `json:"source"`
	Author      string    `json:"author"`
	Title       string    `json:"title"`
	Description string    `json:"description"`
	Url         string    `json:"url"`
	UrlToImage  string    `json:"urlToImage"`
	PublishedAt time.Time `json:"publishedAt"`
	Content     string    `json:"content"`
}

type SourcesResponse struct {
	Status  string   `json:"status"`
	Sources []Source `json:"sources"`
}

type Source struct {
	Id          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Url         string `json:"url"`
	Category    string `json:"category"`
	Language    string `json:"language"`
	Country     string `json:"country"`
}

type RequestError struct {
	Status  string `json:"status"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

func NewClient(httpClient *http.Client, apiKey string) *Client {
	return &Client{
		client: *httpClient,
		apiKey: apiKey,
	}
}

func (e *RequestError) Error() string {
	return fmt.Sprintf("%s - %s - %s", e.Status, e.Code, e.Message)
}

func (c *Client) GetTopHeadlinesBySources(query, sources string, pageSize int) (ArticlesResponse, error) {
	return c.getHeadlines("/v2/top-headlines", query, sources, pageSize)
}

func (c *Client) GetAllHeadlinesBySources(query, sources string, pageSize int) (ArticlesResponse, error) {
	return c.getHeadlines("/v2/everything", query, sources, pageSize)
}

func (c *Client) getHeadlines(path, query, sources string, pageSize int) (result ArticlesResponse, err error) {
	req, err := http.NewRequest("GET", newsAPIHost+path, nil)
	if err != nil {
		return result, err
	}
	c.setHeaders(req)
	queryParams := req.URL.Query()
	if query != "" {
		queryParams.Add("q", query)
	}
	if sources != "" {
		queryParams.Add("sources", sources)
	}
	if pageSize > 0 {
		queryParams.Add("pageSize", strconv.Itoa(pageSize))
	}
	req.URL.RawQuery = queryParams.Encode()

	resp, err := c.client.Do(req)
	if err != nil {
		return result, err
	}
	if resp.StatusCode != http.StatusOK {
		var requestError RequestError
		if err := json.NewDecoder(resp.Body).Decode(&requestError); err != nil {
			return result, err
		}
		return result, &requestError
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return result, err
	}
	return result, nil
}

func (c *Client) GetSources(category, language, country string) (sources []Source, err error) {
	req, err := http.NewRequest("GET", newsAPIHost+"/v2/sources", nil)
	if err != nil {
		return sources, err
	}
	c.setHeaders(req)
	queryParams := req.URL.Query()
	if category != "" {
		queryParams.Add("category", category)
	}
	if language != "" {
		queryParams.Add("language", language)
	}
	if country != "" {
		queryParams.Add("country", country)
	}
	req.URL.RawQuery = queryParams.Encode()

	resp, err := c.client.Do(req)
	if err != nil {
		return sources, err
	}
	if resp.StatusCode != http.StatusOK {
		return sources, errors.New("Response failed with status code: " + resp.Status)
	}
	var result SourcesResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return sources, err
	}
	return result.Sources, nil
}

func (c *Client) setHeaders(request *http.Request) {
	request.Header.Set("X-Api-Key", c.apiKey)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Hugo Authors. All rights reserved.
//
// 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 commands

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/gohugoio/hugo/config"
	"github.com/gohugoio/hugo/helpers"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"

	qt "github.com/frankban/quicktest"
)

// Issue 9518
func TestServerPanicOnConfigError(t *testing.T) {
	c := qt.New(t)

	config := `
[markup]
[markup.highlight]
linenos='table'
`

	r := runServerTest(c,
		serverTestOptions{
			config: config,
		},
	)

	c.Assert(r.err, qt.IsNotNil)
	c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
}

func TestServer404(t *testing.T) {
	c := qt.New(t)

	r := runServerTest(c,
		serverTestOptions{
			pathsToGet:  []string{"this/does/not/exist"},
			getNumHomes: 1,
		},
	)

	c.Assert(r.err, qt.IsNil)
	pr := r.pathsResults["this/does/not/exist"]
	c.Assert(pr.statusCode, qt.Equals, http.StatusNotFound)
	c.Assert(pr.body, qt.Contains, "404: 404 Page not found|Not Found.")
}

func TestServerPathEncodingIssues(t *testing.T) {
	c := qt.New(t)

	// Issue 10287
	c.Run("Unicode paths", func(c *qt.C) {
		r := runServerTest(c,
			serverTestOptions{
				pathsToGet:  []string{"hügö/"},
				getNumHomes: 1,
			},
		)

		c.Assert(r.err, qt.IsNil)
		c.Assert(r.pathsResults["hügö/"].body, qt.Contains, "This is hügö")
	})

	// Issue 10314
	c.Run("Windows multilingual 404", func(c *qt.C) {
		config := `
baseURL = 'https://example.org/'
title = 'Hugo Forum Topic #40568'

defaultContentLanguageInSubdir = true

[languages.en]
contentDir = 'content/en'
languageCode = 'en-US'
languageName = 'English'
weight = 1

[languages.es]
contentDir = 'content/es'
languageCode = 'es-ES'
languageName = 'Espanol'
weight = 2

[server]
[[server.redirects]]
from = '/en/**'
to = '/en/404.html'
status = 404

[[server.redirects]]
from = '/es/**'
to = '/es/404.html'
status = 404
`
		r := runServerTest(c,
			serverTestOptions{
				config:      config,
				pathsToGet:  []string{"en/this/does/not/exist", "es/this/does/not/exist"},
				getNumHomes: 1,
			},
		)

		c.Assert(r.err, qt.IsNil)
		pr1 := r.pathsResults["en/this/does/not/exist"]
		pr2 := r.pathsResults["es/this/does/not/exist"]
		c.Assert(pr1.statusCode, qt.Equals, http.StatusNotFound)
		c.Assert(pr2.statusCode, qt.Equals, http.StatusNotFound)
		c.Assert(pr1.body, qt.Contains, "404: 404 Page not found|Not Found.")
		c.Assert(pr2.body, qt.Contains, "404: 404 Page not found|Not Found.")

	})

}
func TestServerFlags(t *testing.T) {
	c := qt.New(t)

	assertPublic := func(c *qt.C, r serverTestResult, renderStaticToDisk bool) {
		c.Assert(r.err, qt.IsNil)
		c.Assert(r.homesContent[0], qt.Contains, "Environment: development")
		c.Assert(r.publicDirnames["myfile.txt"], qt.Equals, renderStaticToDisk)

	}

	for _, test := range []struct {
		flag   string
		assert func(c *qt.C, r serverTestResult)
	}{
		{"", func(c *qt.C, r serverTestResult) {
			assertPublic(c, r, false)
		}},
		{"--renderToDisk", func(c *qt.C, r serverTestResult) {
			assertPublic(c, r, true)
		}},
		{"--renderStaticToDisk", func(c *qt.C, r serverTestResult) {
			assertPublic(c, r, true)
		}},
	} {
		c.Run(test.flag, func(c *qt.C) {
			config := `
baseURL="https://example.org"
`

			var args []string
			if test.flag != "" {
				args = strings.Split(test.flag, "=")
			}

			opts := serverTestOptions{
				config:      config,
				args:        args,
				getNumHomes: 1,
			}

			r := runServerTest(c, opts)

			test.assert(c, r)

		})

	}

}

func TestServerBugs(t *testing.T) {
	c := qt.New(t)

	for _, test := range []struct {
		name       string
		config     string
		flag       string
		numservers int
		assert     func(c *qt.C, r serverTestResult)
	}{
		{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
		}},
		// Issue 9788
		{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
		}},
		{"PostProcess, disk", "", "--renderToDisk", 1, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
		}},
		// Isue 9901
		{"Multihost", `
defaultContentLanguage = 'en'
[languages]
[languages.en]
baseURL = 'https://example.com'
title = 'My blog'
weight = 1
[languages.fr]
baseURL = 'https://example.fr'
title = 'Mon blogue'
weight = 2
`, "", 2, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			for i, s := range []string{"My blog", "Mon blogue"} {
				c.Assert(r.homesContent[i], qt.Contains, s)
			}
		}},
	} {
		c.Run(test.name, func(c *qt.C) {
			if test.config == "" {
				test.config = `
baseURL="https://example.org"
`
			}

			var args []string
			if test.flag != "" {
				args = strings.Split(test.flag, "=")
			}

			opts := serverTestOptions{
				config:      test.config,
				getNumHomes: test.numservers,
				pathsToGet:  []string{"this/does/not/exist"},
				args:        args,
			}

			r := runServerTest(c, opts)
			pr := r.pathsResults["this/does/not/exist"]
			c.Assert(pr.statusCode, qt.Equals, http.StatusNotFound)
			c.Assert(pr.body, qt.Contains, "404: 404 Page not found|Not Found.")
			test.assert(c, r)

		})

	}

}

type serverTestResult struct {
	err            error
	homesContent   []string
	content404     string
	publicDirnames map[string]bool
	pathsResults   map[string]pathResult
}

type pathResult struct {
	statusCode int
	body       string
}

type serverTestOptions struct {
	getNumHomes int
	config      string
	pathsToGet  []string
	args        []string
}

func runServerTest(c *qt.C, opts serverTestOptions) serverTestResult {
	dir := createSimpleTestSite(c, testSiteConfig{configTOML: opts.config})
	result := serverTestResult{
		publicDirnames: make(map[string]bool),
		pathsResults:   make(map[string]pathResult),
	}

	sp, err := helpers.FindAvailablePort()
	c.Assert(err, qt.IsNil)
	port := sp.Port

	defer func() {
		os.RemoveAll(dir)
	}()

	stop := make(chan bool)

	b := newCommandsBuilder()
	scmd := b.newServerCmdSignaled(stop)

	cmd := scmd.getCommand()
	args := append([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)}, opts.args...)
	cmd.SetArgs(args)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error {
		_, err := cmd.ExecuteC()
		return err
	})

	if opts.getNumHomes > 0 {
		// Esp. on slow CI machines, we need to wait a little before the web
		// server is ready.
		wait := 567 * time.Millisecond
		if os.Getenv("CI") != "" {
			wait = 2 * time.Second
		}
		time.Sleep(wait)
		result.homesContent = make([]string, opts.getNumHomes)
		for i := 0; i < opts.getNumHomes; i++ {
			func() {
				resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
				c.Assert(err, qt.IsNil)
				c.Assert(resp.StatusCode, qt.Equals, http.StatusOK)
				if err == nil {
					defer resp.Body.Close()
					result.homesContent[i] = helpers.ReaderToString(resp.Body)
				}
			}()
		}
	}

	for _, path := range opts.pathsToGet {
		func() {
			resp, err := http.Get(fmt.Sprintf("http://localhost:%d/%s", port, path))
			c.Assert(err, qt.IsNil)
			pr := pathResult{
				statusCode: resp.StatusCode,
			}

			if err == nil {
				defer resp.Body.Close()
				pr.body = helpers.ReaderToString(resp.Body)
			}
			result.pathsResults[path] = pr
		}()
	}

	time.Sleep(1 * time.Second)

	select {
	case <-stop:
	case stop <- true:
	}

	pubFiles, err := os.ReadDir(filepath.Join(dir, "public"))
	c.Assert(err, qt.IsNil)
	for _, f := range pubFiles {
		result.publicDirnames[f.Name()] = true
	}

	result.err = wg.Wait()

	return result

}

func TestFixURL(t *testing.T) {
	type data struct {
		TestName   string
		CLIBaseURL string
		CfgBaseURL string
		AppendPort bool
		Port       int
		Result     string
	}
	tests := []data{
		{"Basic http localhost", "", "http://foo.com", true, 1313, "http://localhost:1313/"},
		{"Basic https production, http localhost", "", "https://foo.com", true, 1313, "http://localhost:1313/"},
		{"Basic subdir", "", "http://foo.com/bar", true, 1313, "http://localhost:1313/bar/"},
		{"Basic production", "http://foo.com", "http://foo.com", false, 80, "http://foo.com/"},
		{"Production subdir", "http://foo.com/bar", "http://foo.com/bar", false, 80, "http://foo.com/bar/"},
		{"No http", "", "foo.com", true, 1313, "//localhost:1313/"},
		{"Override configured port", "", "foo.com:2020", true, 1313, "//localhost:1313/"},
		{"No http production", "foo.com", "foo.com", false, 80, "//foo.com/"},
		{"No http production with port", "foo.com", "foo.com", true, 2020, "//foo.com:2020/"},
		{"No config", "", "", true, 1313, "//localhost:1313/"},
	}

	for _, test := range tests {
		t.Run(test.TestName, func(t *testing.T) {
			b := newCommandsBuilder()
			s := b.newServerCmd()
			v := config.NewWithTestDefaults()
			baseURL := test.CLIBaseURL
			v.Set("baseURL", test.CfgBaseURL)
			s.serverAppend = test.AppendPort
			s.serverPort = test.Port
			result, err := s.fixURL(v, baseURL, s.serverPort)
			if err != nil {
				t.Errorf("Unexpected error %s", err)
			}
			if result != test.Result {
				t.Errorf("Expected %q, got %q", test.Result, result)
			}
		})
	}
}

func TestRemoveErrorPrefixFromLog(t *testing.T) {
	c := qt.New(t)
	content := `ERROR 2018/10/07 13:11:12 Error while rendering "home": template: _default/baseof.html:4:3: executing "main" at <partial "logo" .>: error calling partial: template: partials/logo.html:5:84: executing "partials/logo.html" at <$resized.AHeight>: can't evaluate field AHeight in type *resource.Image
ERROR 2018/10/07 13:11:12 Rebuild failed: logged 1 error(s)
`

	withoutError := removeErrorPrefixFromLog(content)

	c.Assert(strings.Contains(withoutError, "ERROR"), qt.Equals, false)
}

func isWindowsCI() bool {
	return runtime.GOOS == "windows" && os.Getenv("CI") != ""
}
<commit_msg>Skip flakey server tests on GitHub Action on Windows<commit_after>// Copyright 2015 The Hugo Authors. All rights reserved.
//
// 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 commands

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/gohugoio/hugo/config"
	"github.com/gohugoio/hugo/helpers"
	"github.com/gohugoio/hugo/htesting"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"

	qt "github.com/frankban/quicktest"
)

// Issue 9518
func TestServerPanicOnConfigError(t *testing.T) {
	c := qt.New(t)

	config := `
[markup]
[markup.highlight]
linenos='table'
`

	r := runServerTest(c,
		serverTestOptions{
			config: config,
		},
	)

	c.Assert(r.err, qt.IsNotNil)
	c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
}

func TestServer404(t *testing.T) {
	c := qt.New(t)

	r := runServerTest(c,
		serverTestOptions{
			pathsToGet:  []string{"this/does/not/exist"},
			getNumHomes: 1,
		},
	)

	c.Assert(r.err, qt.IsNil)
	pr := r.pathsResults["this/does/not/exist"]
	c.Assert(pr.statusCode, qt.Equals, http.StatusNotFound)
	c.Assert(pr.body, qt.Contains, "404: 404 Page not found|Not Found.")
}

func TestServerPathEncodingIssues(t *testing.T) {
	c := qt.New(t)

	// Issue 10287
	c.Run("Unicode paths", func(c *qt.C) {
		r := runServerTest(c,
			serverTestOptions{
				pathsToGet:  []string{"hügö/"},
				getNumHomes: 1,
			},
		)

		c.Assert(r.err, qt.IsNil)
		c.Assert(r.pathsResults["hügö/"].body, qt.Contains, "This is hügö")
	})

	// Issue 10314
	c.Run("Windows multilingual 404", func(c *qt.C) {
		config := `
baseURL = 'https://example.org/'
title = 'Hugo Forum Topic #40568'

defaultContentLanguageInSubdir = true

[languages.en]
contentDir = 'content/en'
languageCode = 'en-US'
languageName = 'English'
weight = 1

[languages.es]
contentDir = 'content/es'
languageCode = 'es-ES'
languageName = 'Espanol'
weight = 2

[server]
[[server.redirects]]
from = '/en/**'
to = '/en/404.html'
status = 404

[[server.redirects]]
from = '/es/**'
to = '/es/404.html'
status = 404
`
		r := runServerTest(c,
			serverTestOptions{
				config:      config,
				pathsToGet:  []string{"en/this/does/not/exist", "es/this/does/not/exist"},
				getNumHomes: 1,
			},
		)

		c.Assert(r.err, qt.IsNil)
		pr1 := r.pathsResults["en/this/does/not/exist"]
		pr2 := r.pathsResults["es/this/does/not/exist"]
		c.Assert(pr1.statusCode, qt.Equals, http.StatusNotFound)
		c.Assert(pr2.statusCode, qt.Equals, http.StatusNotFound)
		c.Assert(pr1.body, qt.Contains, "404: 404 Page not found|Not Found.")
		c.Assert(pr2.body, qt.Contains, "404: 404 Page not found|Not Found.")

	})

}
func TestServerFlags(t *testing.T) {
	c := qt.New(t)

	assertPublic := func(c *qt.C, r serverTestResult, renderStaticToDisk bool) {
		c.Assert(r.err, qt.IsNil)
		c.Assert(r.homesContent[0], qt.Contains, "Environment: development")
		c.Assert(r.publicDirnames["myfile.txt"], qt.Equals, renderStaticToDisk)

	}

	for _, test := range []struct {
		flag   string
		assert func(c *qt.C, r serverTestResult)
	}{
		{"", func(c *qt.C, r serverTestResult) {
			assertPublic(c, r, false)
		}},
		{"--renderToDisk", func(c *qt.C, r serverTestResult) {
			assertPublic(c, r, true)
		}},
		{"--renderStaticToDisk", func(c *qt.C, r serverTestResult) {
			assertPublic(c, r, true)
		}},
	} {
		c.Run(test.flag, func(c *qt.C) {
			config := `
baseURL="https://example.org"
`

			var args []string
			if test.flag != "" {
				args = strings.Split(test.flag, "=")
			}

			opts := serverTestOptions{
				config:      config,
				args:        args,
				getNumHomes: 1,
			}

			r := runServerTest(c, opts)

			test.assert(c, r)

		})

	}

}

func TestServerBugs(t *testing.T) {
	// TODO(bep) this is flaky on Windows on GH Actions.
	if htesting.IsGitHubAction() && runtime.GOOS == "windows" {
		t.Skip("skipping on windows")
	}
	c := qt.New(t)

	for _, test := range []struct {
		name       string
		config     string
		flag       string
		numservers int
		assert     func(c *qt.C, r serverTestResult)
	}{
		{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
		}},
		// Issue 9788
		{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
		}},
		{"PostProcess, disk", "", "--renderToDisk", 1, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
		}},
		// Isue 9901
		{"Multihost", `
defaultContentLanguage = 'en'
[languages]
[languages.en]
baseURL = 'https://example.com'
title = 'My blog'
weight = 1
[languages.fr]
baseURL = 'https://example.fr'
title = 'Mon blogue'
weight = 2
`, "", 2, func(c *qt.C, r serverTestResult) {
			c.Assert(r.err, qt.IsNil)
			for i, s := range []string{"My blog", "Mon blogue"} {
				c.Assert(r.homesContent[i], qt.Contains, s)
			}
		}},
	} {
		c.Run(test.name, func(c *qt.C) {
			if test.config == "" {
				test.config = `
baseURL="https://example.org"
`
			}

			var args []string
			if test.flag != "" {
				args = strings.Split(test.flag, "=")
			}

			opts := serverTestOptions{
				config:      test.config,
				getNumHomes: test.numservers,
				pathsToGet:  []string{"this/does/not/exist"},
				args:        args,
			}

			r := runServerTest(c, opts)
			pr := r.pathsResults["this/does/not/exist"]
			c.Assert(pr.statusCode, qt.Equals, http.StatusNotFound)
			c.Assert(pr.body, qt.Contains, "404: 404 Page not found|Not Found.")
			test.assert(c, r)

		})

	}

}

type serverTestResult struct {
	err            error
	homesContent   []string
	content404     string
	publicDirnames map[string]bool
	pathsResults   map[string]pathResult
}

type pathResult struct {
	statusCode int
	body       string
}

type serverTestOptions struct {
	getNumHomes int
	config      string
	pathsToGet  []string
	args        []string
}

func runServerTest(c *qt.C, opts serverTestOptions) serverTestResult {
	dir := createSimpleTestSite(c, testSiteConfig{configTOML: opts.config})
	result := serverTestResult{
		publicDirnames: make(map[string]bool),
		pathsResults:   make(map[string]pathResult),
	}

	sp, err := helpers.FindAvailablePort()
	c.Assert(err, qt.IsNil)
	port := sp.Port

	defer func() {
		os.RemoveAll(dir)
	}()

	stop := make(chan bool)

	b := newCommandsBuilder()
	scmd := b.newServerCmdSignaled(stop)

	cmd := scmd.getCommand()
	args := append([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)}, opts.args...)
	cmd.SetArgs(args)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error {
		_, err := cmd.ExecuteC()
		return err
	})

	if opts.getNumHomes > 0 {
		// Esp. on slow CI machines, we need to wait a little before the web
		// server is ready.
		wait := 567 * time.Millisecond
		if os.Getenv("CI") != "" {
			wait = 2 * time.Second
		}
		time.Sleep(wait)
		result.homesContent = make([]string, opts.getNumHomes)
		for i := 0; i < opts.getNumHomes; i++ {
			func() {
				resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
				c.Assert(err, qt.IsNil)
				c.Assert(resp.StatusCode, qt.Equals, http.StatusOK)
				if err == nil {
					defer resp.Body.Close()
					result.homesContent[i] = helpers.ReaderToString(resp.Body)
				}
			}()
		}
	}

	for _, path := range opts.pathsToGet {
		func() {
			resp, err := http.Get(fmt.Sprintf("http://localhost:%d/%s", port, path))
			c.Assert(err, qt.IsNil)
			pr := pathResult{
				statusCode: resp.StatusCode,
			}

			if err == nil {
				defer resp.Body.Close()
				pr.body = helpers.ReaderToString(resp.Body)
			}
			result.pathsResults[path] = pr
		}()
	}

	time.Sleep(1 * time.Second)

	select {
	case <-stop:
	case stop <- true:
	}

	pubFiles, err := os.ReadDir(filepath.Join(dir, "public"))
	c.Assert(err, qt.IsNil)
	for _, f := range pubFiles {
		result.publicDirnames[f.Name()] = true
	}

	result.err = wg.Wait()

	return result

}

func TestFixURL(t *testing.T) {
	type data struct {
		TestName   string
		CLIBaseURL string
		CfgBaseURL string
		AppendPort bool
		Port       int
		Result     string
	}
	tests := []data{
		{"Basic http localhost", "", "http://foo.com", true, 1313, "http://localhost:1313/"},
		{"Basic https production, http localhost", "", "https://foo.com", true, 1313, "http://localhost:1313/"},
		{"Basic subdir", "", "http://foo.com/bar", true, 1313, "http://localhost:1313/bar/"},
		{"Basic production", "http://foo.com", "http://foo.com", false, 80, "http://foo.com/"},
		{"Production subdir", "http://foo.com/bar", "http://foo.com/bar", false, 80, "http://foo.com/bar/"},
		{"No http", "", "foo.com", true, 1313, "//localhost:1313/"},
		{"Override configured port", "", "foo.com:2020", true, 1313, "//localhost:1313/"},
		{"No http production", "foo.com", "foo.com", false, 80, "//foo.com/"},
		{"No http production with port", "foo.com", "foo.com", true, 2020, "//foo.com:2020/"},
		{"No config", "", "", true, 1313, "//localhost:1313/"},
	}

	for _, test := range tests {
		t.Run(test.TestName, func(t *testing.T) {
			b := newCommandsBuilder()
			s := b.newServerCmd()
			v := config.NewWithTestDefaults()
			baseURL := test.CLIBaseURL
			v.Set("baseURL", test.CfgBaseURL)
			s.serverAppend = test.AppendPort
			s.serverPort = test.Port
			result, err := s.fixURL(v, baseURL, s.serverPort)
			if err != nil {
				t.Errorf("Unexpected error %s", err)
			}
			if result != test.Result {
				t.Errorf("Expected %q, got %q", test.Result, result)
			}
		})
	}
}

func TestRemoveErrorPrefixFromLog(t *testing.T) {
	c := qt.New(t)
	content := `ERROR 2018/10/07 13:11:12 Error while rendering "home": template: _default/baseof.html:4:3: executing "main" at <partial "logo" .>: error calling partial: template: partials/logo.html:5:84: executing "partials/logo.html" at <$resized.AHeight>: can't evaluate field AHeight in type *resource.Image
ERROR 2018/10/07 13:11:12 Rebuild failed: logged 1 error(s)
`

	withoutError := removeErrorPrefixFromLog(content)

	c.Assert(strings.Contains(withoutError, "ERROR"), qt.Equals, false)
}

func isWindowsCI() bool {
	return runtime.GOOS == "windows" && os.Getenv("CI") != ""
}
<|endoftext|>


<commit_before>package chart

import (
	"fmt"
	"time"
)

// MarketHoursRange is a special type of range that compresses a time range into just the
// market (i.e. NYSE operating hours and days) range.
type MarketHoursRange struct {
	Min time.Time
	Max time.Time

	MarketOpen  time.Time
	MarketClose time.Time

	HolidayProvider HolidayProvider

	ValueFormatter ValueFormatter

	Descending bool
	Domain     int
}

// IsDescending returns if the range is descending.
func (mhr MarketHoursRange) IsDescending() bool {
	return mhr.Descending
}

// GetTimezone returns the timezone for the market hours range.
func (mhr MarketHoursRange) GetTimezone() *time.Location {
	return mhr.GetMarketOpen().Location()
}

// IsZero returns if the range is setup or not.
func (mhr MarketHoursRange) IsZero() bool {
	return mhr.Min.IsZero() && mhr.Max.IsZero()
}

// GetMin returns the min value.
func (mhr MarketHoursRange) GetMin() float64 {
	return Time.ToFloat64(mhr.Min)
}

// GetMax returns the max value.
func (mhr MarketHoursRange) GetMax() float64 {
	return Time.ToFloat64(mhr.GetEffectiveMax())
}

// GetEffectiveMax gets either the close on the max, or the max itself.
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
	maxClose := Date.On(mhr.MarketClose, mhr.Max)
	if maxClose.After(mhr.Max) {
		return maxClose
	}
	return mhr.Max
}

// SetMin sets the min value.
func (mhr *MarketHoursRange) SetMin(min float64) {
	mhr.Min = Time.FromFloat64(min)
	mhr.Min = mhr.Min.In(mhr.GetTimezone())
}

// SetMax sets the max value.
func (mhr *MarketHoursRange) SetMax(max float64) {
	mhr.Max = Time.FromFloat64(max)
	mhr.Max = mhr.Max.In(mhr.GetTimezone())
}

// GetDelta gets the delta.
func (mhr MarketHoursRange) GetDelta() float64 {
	min := mhr.GetMin()
	max := mhr.GetMax()
	return max - min
}

// GetDomain gets the domain.
func (mhr MarketHoursRange) GetDomain() int {
	return mhr.Domain
}

// SetDomain sets the domain.
func (mhr *MarketHoursRange) SetDomain(domain int) {
	mhr.Domain = domain
}

// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
	if mhr.HolidayProvider == nil {
		return defaultHolidayProvider
	}
	return mhr.HolidayProvider
}

// GetMarketOpen returns the market open time.
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
	if mhr.MarketOpen.IsZero() {
		return NYSEOpen
	}
	return mhr.MarketOpen
}

// GetMarketClose returns the market close time.
func (mhr MarketHoursRange) GetMarketClose() time.Time {
	if mhr.MarketClose.IsZero() {
		return NYSEClose
	}
	return mhr.MarketClose
}

// GetTicks returns the ticks for the range.
// This is to override the default continous ticks that would be generated for the range.
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
	times := Sequence.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth := mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	return GenerateContinuousTicks(r, mhr, false, defaults, vf)

}

func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
	defaults.GetTextOptions().WriteToRenderer(r)
	var total int
	for index, t := range times {
		timeLabel := vf(t)

		labelBox := r.MeasureText(timeLabel)
		total += labelBox.Width()
		if index > 0 {
			total += DefaultMinimumTickHorizontalSpacing
		}
	}
	return total
}

func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []Tick {
	ticks := make([]Tick, len(times))
	for index, t := range times {
		ticks[index] = Tick{
			Value: Time.ToFloat64(t),
			Label: vf(t),
		}
	}
	return ticks
}

func (mhr MarketHoursRange) String() string {
	return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(time.RFC3339), mhr.Max.Format(time.RFC3339), mhr.Domain)
}

// Translate maps a given value into the ContinuousRange space.
func (mhr MarketHoursRange) Translate(value float64) int {
	valueTime := Time.FromFloat64(value)
	valueTimeEastern := valueTime.In(Date.Eastern())
	totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))
	return translated
}
<commit_msg>and for market hours as well.<commit_after>package chart

import (
	"fmt"
	"time"
)

// MarketHoursRange is a special type of range that compresses a time range into just the
// market (i.e. NYSE operating hours and days) range.
type MarketHoursRange struct {
	Min time.Time
	Max time.Time

	MarketOpen  time.Time
	MarketClose time.Time

	HolidayProvider HolidayProvider

	ValueFormatter ValueFormatter

	Descending bool
	Domain     int
}

// IsDescending returns if the range is descending.
func (mhr MarketHoursRange) IsDescending() bool {
	return mhr.Descending
}

// GetTimezone returns the timezone for the market hours range.
func (mhr MarketHoursRange) GetTimezone() *time.Location {
	return mhr.GetMarketOpen().Location()
}

// IsZero returns if the range is setup or not.
func (mhr MarketHoursRange) IsZero() bool {
	return mhr.Min.IsZero() && mhr.Max.IsZero()
}

// GetMin returns the min value.
func (mhr MarketHoursRange) GetMin() float64 {
	return Time.ToFloat64(mhr.Min)
}

// GetMax returns the max value.
func (mhr MarketHoursRange) GetMax() float64 {
	return Time.ToFloat64(mhr.GetEffectiveMax())
}

// GetEffectiveMax gets either the close on the max, or the max itself.
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
	maxClose := Date.On(mhr.MarketClose, mhr.Max)
	if maxClose.After(mhr.Max) {
		return maxClose
	}
	return mhr.Max
}

// SetMin sets the min value.
func (mhr *MarketHoursRange) SetMin(min float64) {
	mhr.Min = Time.FromFloat64(min)
	mhr.Min = mhr.Min.In(mhr.GetTimezone())
}

// SetMax sets the max value.
func (mhr *MarketHoursRange) SetMax(max float64) {
	mhr.Max = Time.FromFloat64(max)
	mhr.Max = mhr.Max.In(mhr.GetTimezone())
}

// GetDelta gets the delta.
func (mhr MarketHoursRange) GetDelta() float64 {
	min := mhr.GetMin()
	max := mhr.GetMax()
	return max - min
}

// GetDomain gets the domain.
func (mhr MarketHoursRange) GetDomain() int {
	return mhr.Domain
}

// SetDomain sets the domain.
func (mhr *MarketHoursRange) SetDomain(domain int) {
	mhr.Domain = domain
}

// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
	if mhr.HolidayProvider == nil {
		return defaultHolidayProvider
	}
	return mhr.HolidayProvider
}

// GetMarketOpen returns the market open time.
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
	if mhr.MarketOpen.IsZero() {
		return NYSEOpen
	}
	return mhr.MarketOpen
}

// GetMarketClose returns the market close time.
func (mhr MarketHoursRange) GetMarketClose() time.Time {
	if mhr.MarketClose.IsZero() {
		return NYSEClose
	}
	return mhr.MarketClose
}

// GetTicks returns the ticks for the range.
// This is to override the default continous ticks that would be generated for the range.
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
	times := Sequence.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth := mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	return GenerateContinuousTicks(r, mhr, false, defaults, vf)

}

func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
	defaults.GetTextOptions().WriteToRenderer(r)
	var total int
	for index, t := range times {
		timeLabel := vf(t)

		labelBox := r.MeasureText(timeLabel)
		total += labelBox.Width()
		if index > 0 {
			total += DefaultMinimumTickHorizontalSpacing
		}
	}
	return total
}

func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []Tick {
	ticks := make([]Tick, len(times))
	for index, t := range times {
		ticks[index] = Tick{
			Value: Time.ToFloat64(t),
			Label: vf(t),
		}
	}
	return ticks
}

func (mhr MarketHoursRange) String() string {
	return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(time.RFC3339), mhr.Max.Format(time.RFC3339), mhr.Domain)
}

// Translate maps a given value into the ContinuousRange space.
func (mhr MarketHoursRange) Translate(value float64) int {
	valueTime := Time.FromFloat64(value)
	valueTimeEastern := valueTime.In(Date.Eastern())
	totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))

	if r.IsDescending() {
		return r.Domain - translated
	}

	return translated
}
<|endoftext|>


<commit_before>package stack

import (
	"context"
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/appcelerator/amp/api/rpc/stack"
	"github.com/appcelerator/amp/cli"
	"github.com/spf13/cobra"
	"google.golang.org/grpc"
)

type listStackOptions struct {
	quiet bool
}

// NewListCommand returns a new instance of the stack command.
func NewListCommand(c cli.Interface) *cobra.Command {
	opts := listStackOptions{}
	cmd := &cobra.Command{
		Use:     "ls [OPTIONS]",
		Short:   "List deployed stacks",
		Aliases: []string{"list"},
		PreRunE: cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return list(c, opts)
		},
	}
	cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Only display stack ids")
	return cmd
}

func list(c cli.Interface, opts listStackOptions) error {
	req := &stack.ListRequest{}
	client := stack.NewStackClient(c.ClientConn())
	reply, err := client.List(context.Background(), req)
	if err != nil {
		return fmt.Errorf("%s", grpc.ErrorDesc(err))
	}
	if opts.quiet {
		for _, line := range reply.Entries {
			c.Console().Println(line.Stack.Id)
		}
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 0, cli.Padding, ' ', 0)
	fmt.Fprintln(w, "ID\tNAME\tSERVICES\tFAILED SERVICES\tSTATUS\tOWNER\tORGANIZATION")
	for _, entry := range reply.Entries {
		fmt.Fprintf(w, "%s\t%s\t%d/%d\t%d\t%s\t%s\t%s\n", entry.Stack.Id, entry.Stack.Name, entry.RunningServices, entry.TotalServices, entry.FailedServices, entry.Status, entry.Stack.Owner.User, entry.Stack.Owner.Organization)
	}
	w.Flush()
	return nil
}
<commit_msg>fix quiet flag for stack ls (#1436)<commit_after>package stack

import (
	"context"
	"fmt"
	"os"
	"text/tabwriter"

	"github.com/appcelerator/amp/api/rpc/stack"
	"github.com/appcelerator/amp/cli"
	"github.com/spf13/cobra"
	"google.golang.org/grpc"
)

type listStackOptions struct {
	quiet bool
}

// NewListCommand returns a new instance of the stack command.
func NewListCommand(c cli.Interface) *cobra.Command {
	opts := listStackOptions{}
	cmd := &cobra.Command{
		Use:     "ls [OPTIONS]",
		Short:   "List deployed stacks",
		Aliases: []string{"list"},
		PreRunE: cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return list(c, opts)
		},
	}
	cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Only display stack ids")
	return cmd
}

func list(c cli.Interface, opts listStackOptions) error {
	req := &stack.ListRequest{}
	client := stack.NewStackClient(c.ClientConn())
	reply, err := client.List(context.Background(), req)
	if err != nil {
		return fmt.Errorf("%s", grpc.ErrorDesc(err))
	}
	if opts.quiet {
		for _, line := range reply.Entries {
			c.Console().Println(line.Stack.Id)
		}
		return nil
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 0, cli.Padding, ' ', 0)
	fmt.Fprintln(w, "ID\tNAME\tSERVICES\tFAILED SERVICES\tSTATUS\tOWNER\tORGANIZATION")
	for _, entry := range reply.Entries {
		fmt.Fprintf(w, "%s\t%s\t%d/%d\t%d\t%s\t%s\t%s\n", entry.Stack.Id, entry.Stack.Name, entry.RunningServices, entry.TotalServices, entry.FailedServices, entry.Status, entry.Stack.Owner.User, entry.Stack.Owner.Organization)
	}
	w.Flush()
	return nil
}
<|endoftext|>


<commit_before>package stack

import (
	"context"
	"errors"
	"fmt"
	"os"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/appcelerator/amp/api/rpc/stack"
	"github.com/appcelerator/amp/cli"
	"github.com/spf13/cobra"
	"google.golang.org/grpc"
)

// NewListCommand returns a new instance of the stack command.
func NewListCommand(c cli.Interface) *cobra.Command {
	cmd := &cobra.Command{
		Use:     "list",
		Aliases: []string{"ls"},
		Short:   "List deployed stacks",
		PreRunE: cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return list(c)
		},
	}
	return cmd
}

func list(c cli.Interface) error {
	req := &stack.ListRequest{}
	client := stack.NewStackClient(c.ClientConn())
	reply, err := client.List(context.Background(), req)
	if err != nil {
		return errors.New(grpc.ErrorDesc(err))
	}
	lines := strings.Split(reply.Answer, "\n")
	if len(lines) > 1 {
		w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
		fmt.Fprintln(w, "ID\tNAME\tSERVICE")
		lines = lines[1:]
		sort.Strings(lines)
		for _, line := range lines {
			if line != "" {
				fmt.Fprintln(w, getOneStackListLine(line))
			}
		}
		w.Flush()
	}
	return nil
}

func getOneStackListLine(line string) string {
	cols := strings.Split(line, " ")
	name := cols[0]
	ll := strings.LastIndex(cols[0], "-")
	if ll >= 0 {
		name = name[0:ll]
	}
	ret := fmt.Sprintf("%s\t%s", cols[0], name)
	for _, col := range cols[1:] {
		if col != "" {
			ret = fmt.Sprintf("%s\t%s", ret, col)
		}
	}
	return ret
}
<commit_msg>add option quiet to amp stack ls<commit_after>package stack

import (
	"context"
	"errors"
	"fmt"
	"os"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/appcelerator/amp/api/rpc/stack"
	"github.com/appcelerator/amp/cli"
	"github.com/spf13/cobra"
	"google.golang.org/grpc"
)

type listOpts struct {
	quiet bool
}

var (
	lsopts = &listOpts{}
)

// NewListCommand returns a new instance of the stack command.
func NewListCommand(c cli.Interface) *cobra.Command {
	cmd := &cobra.Command{
		Use:     "list",
		Aliases: []string{"ls"},
		Short:   "List deployed stacks",
		PreRunE: cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return list(c)
		},
	}
	cmd.Flags().BoolVarP(&lsopts.quiet, "quiet", "q", false, "Only display the stack id")
	return cmd
}

func list(c cli.Interface) error {
	req := &stack.ListRequest{}
	client := stack.NewStackClient(c.ClientConn())
	reply, err := client.List(context.Background(), req)
	if err != nil {
		return errors.New(grpc.ErrorDesc(err))
	}
	lines := strings.Split(reply.Answer, "\n")
	if len(lines) > 1 {
		if !lsopts.quiet {
			w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
			fmt.Fprintln(w, "ID\tNAME\tSERVICE")
			lines = lines[1:]
			sort.Strings(lines)
			for _, line := range lines {
				if line != "" {
					fmt.Fprintln(w, getOneStackListLine(line))
				}
			}
			w.Flush()
		} else {
			for _, line := range lines[1:] {
				if line != "" {
					c.Console().Println(strings.Split(line, " ")[0])
				}
			}
		}

	}
	return nil
}

func getOneStackListLine(line string) string {
	cols := strings.Split(line, " ")
	name := cols[0]
	ll := strings.LastIndex(cols[0], "-")
	if ll >= 0 {
		name = name[0:ll]
	}
	ret := fmt.Sprintf("%s\t%s", cols[0], name)
	for _, col := range cols[1:] {
		if col != "" {
			ret = fmt.Sprintf("%s\t%s", ret, col)
		}
	}
	return ret
}
<|endoftext|>


<commit_before>package dnode

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
)

// Partial is the type of "arguments" field in dnode.Message.
type Partial struct {
	Raw           []byte
	CallbackSpecs []CallbackSpec
}

// MarshalJSON returns the raw bytes of the Partial.
func (p *Partial) MarshalJSON() ([]byte, error) {
	return p.Raw, nil
}

// UnmarshalJSON puts the data into Partial.Raw.
func (p *Partial) UnmarshalJSON(data []byte) error {
	// p.Raw = make([]byte, len(data))
	// copy(p.Raw, data)
	// return nil
	if p == nil {
		return errors.New("json.Partial: UnmarshalJSON on nil pointer")
	}
	p.Raw = append(p.Raw[0:0], data...)
	return nil
}

// Unmarshal unmarshals the raw data (p.Raw) into v and prepares callbacks.
// v must be a struct that is the type of expected arguments.
func (p *Partial) Unmarshal(v interface{}) error {
	l.Printf("Unmarshal Partial")

	if p == nil {
		return fmt.Errorf("Cannot unmarshal nil argument")
	}

	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		panic("v must be a pointer")
	}

	err := json.Unmarshal(p.Raw, &v)
	if err != nil {
		l.Println(err)
		return err
	}

	for _, spec := range p.CallbackSpecs {
		l.Printf("spec: %#v", spec)
		err := spec.Apply(value)
		if err != nil {
			l.Println(err)
			return err
		}
	}

	return nil
}

//-------------------------------------------
// Helper methods for unmarshaling JSON types
//-------------------------------------------

// Slice is a helper method to unmarshal a JSON Array.
func (p *Partial) Slice() (a []*Partial, err error) {
	err = p.Unmarshal(&a)
	return
}

// SliceOfLength is a helper method to unmarshal a JSON Array with specified length.
func (p *Partial) SliceOfLength(length int) (a []*Partial, err error) {
	err = p.Unmarshal(&a)
	if err != nil {
		return
	}

	if len(a) != length {
		err = errors.New("Invalid array length")
	}

	return
}

// Map is a helper method to unmarshal to a JSON Object.
func (p *Partial) Map() (m map[string]*Partial, err error) {
	err = p.Unmarshal(&m)
	return
}

// String is a helper to unmarshal a JSON String.
func (p *Partial) String() (s string, err error) {
	err = p.Unmarshal(&s)
	return
}

// Float64 is a helper to unmarshal a JSON Number.
func (p *Partial) Float64() (f float64, err error) {
	err = p.Unmarshal(&f)
	return
}

// Bool is a helper to unmarshal a JSON Boolean.
func (p *Partial) Bool() (b bool, err error) {
	err = p.Unmarshal(&b)
	return
}

// Function is a helper to unmarshal a callback function.
func (p *Partial) Function() (f Function, err error) {
	err = p.Unmarshal(&f)
	return
}

//----------------------------------------------------------------
// Helper methods for unmarshaling JSON types that panic on errors
//----------------------------------------------------------------

type ArgumentError struct {
	s string
}

func (err ArgumentError) Error() string {
	return err.s
}

func checkError(err error) {
	if err != nil {
		panic(&ArgumentError{err.Error()})
	}
}

func (p *Partial) MustSlice() []*Partial {
	a, err := p.Slice()
	checkError(err)
	return a
}

func (p *Partial) MustSliceOfLength(length int) []*Partial {
	a, err := p.SliceOfLength(length)
	checkError(err)
	return a
}

func (p *Partial) MustMap() map[string]*Partial {
	m, err := p.Map()
	checkError(err)
	return m
}

func (p *Partial) MustString() string {
	s, err := p.String()
	checkError(err)
	return s
}

func (p *Partial) MustFloat64() float64 {
	f, err := p.Float64()
	checkError(err)
	return f
}

func (p *Partial) MustBool() bool {
	b, err := p.Bool()
	checkError(err)
	return b
}

func (p *Partial) MustFunction() Function {
	f, err := p.Function()
	checkError(err)
	return f
}
<commit_msg>kite: copy is more readable than append<commit_after>package dnode

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
)

// Partial is the type of "arguments" field in dnode.Message.
type Partial struct {
	Raw           []byte
	CallbackSpecs []CallbackSpec
}

// MarshalJSON returns the raw bytes of the Partial.
func (p *Partial) MarshalJSON() ([]byte, error) {
	return p.Raw, nil
}

// UnmarshalJSON puts the data into Partial.Raw.
func (p *Partial) UnmarshalJSON(data []byte) error {
	if p == nil {
		return errors.New("json.Partial: UnmarshalJSON on nil pointer")
	}

	p.Raw = make([]byte, len(data))
	copy(p.Raw, data)
	return nil
}

// Unmarshal unmarshals the raw data (p.Raw) into v and prepares callbacks.
// v must be a struct that is the type of expected arguments.
func (p *Partial) Unmarshal(v interface{}) error {
	l.Printf("Unmarshal Partial")

	if p == nil {
		return fmt.Errorf("Cannot unmarshal nil argument")
	}

	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		panic("v must be a pointer")
	}

	err := json.Unmarshal(p.Raw, &v)
	if err != nil {
		l.Println(err)
		return err
	}

	for _, spec := range p.CallbackSpecs {
		l.Printf("spec: %#v", spec)
		err := spec.Apply(value)
		if err != nil {
			l.Println(err)
			return err
		}
	}

	return nil
}

//-------------------------------------------
// Helper methods for unmarshaling JSON types
//-------------------------------------------

// Slice is a helper method to unmarshal a JSON Array.
func (p *Partial) Slice() (a []*Partial, err error) {
	err = p.Unmarshal(&a)
	return
}

// SliceOfLength is a helper method to unmarshal a JSON Array with specified length.
func (p *Partial) SliceOfLength(length int) (a []*Partial, err error) {
	err = p.Unmarshal(&a)
	if err != nil {
		return
	}

	if len(a) != length {
		err = errors.New("Invalid array length")
	}

	return
}

// Map is a helper method to unmarshal to a JSON Object.
func (p *Partial) Map() (m map[string]*Partial, err error) {
	err = p.Unmarshal(&m)
	return
}

// String is a helper to unmarshal a JSON String.
func (p *Partial) String() (s string, err error) {
	err = p.Unmarshal(&s)
	return
}

// Float64 is a helper to unmarshal a JSON Number.
func (p *Partial) Float64() (f float64, err error) {
	err = p.Unmarshal(&f)
	return
}

// Bool is a helper to unmarshal a JSON Boolean.
func (p *Partial) Bool() (b bool, err error) {
	err = p.Unmarshal(&b)
	return
}

// Function is a helper to unmarshal a callback function.
func (p *Partial) Function() (f Function, err error) {
	err = p.Unmarshal(&f)
	return
}

//----------------------------------------------------------------
// Helper methods for unmarshaling JSON types that panic on errors
//----------------------------------------------------------------

type ArgumentError struct {
	s string
}

func (err ArgumentError) Error() string {
	return err.s
}

func checkError(err error) {
	if err != nil {
		panic(&ArgumentError{err.Error()})
	}
}

func (p *Partial) MustSlice() []*Partial {
	a, err := p.Slice()
	checkError(err)
	return a
}

func (p *Partial) MustSliceOfLength(length int) []*Partial {
	a, err := p.SliceOfLength(length)
	checkError(err)
	return a
}

func (p *Partial) MustMap() map[string]*Partial {
	m, err := p.Map()
	checkError(err)
	return m
}

func (p *Partial) MustString() string {
	s, err := p.String()
	checkError(err)
	return s
}

func (p *Partial) MustFloat64() float64 {
	f, err := p.Float64()
	checkError(err)
	return f
}

func (p *Partial) MustBool() bool {
	b, err := p.Bool()
	checkError(err)
	return b
}

func (p *Partial) MustFunction() Function {
	f, err := p.Function()
	checkError(err)
	return f
}
<|endoftext|>