<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"

	"github.com/tchaik/tchaik/index"
)

type LibraryAPI struct {
	index.Library

	collections map[string]index.Collection
	filters     map[string][]index.FilterItem
	recent      []index.Path
	searcher    index.Searcher
	players     *players
}

type libraryFileSystem struct {
	http.FileSystem
	index.Library
}

// Open implements http.FileSystem and rewrites TrackID values to their corresponding Location
// values using the index.Library
func (l *libraryFileSystem) Open(path string) (http.File, error) {
	t, ok := l.Library.Track(strings.Trim(path, "/")) // TrackIDs arrive with leading slash
	if !ok {
		return nil, fmt.Errorf("could not find track: %v", path)
	}

	loc := t.GetString("Location")
	if loc == "" {
		return nil, fmt.Errorf("invalid (empty) location for track: %v", path)
	}
	return l.FileSystem.Open(loc)
}

type group struct {
	Name        string
	Key         index.Key
	TotalTime   interface{} `json:",omitempty"`
	Artist      interface{} `json:",omitempty"`
	AlbumArtist interface{} `json:",omitempty"`
	Composer    interface{} `json:",omitempty"`
	BitRate     interface{} `json:",omitempty"`
	DiscNumber  interface{} `json:",omitempty"`
	ListStyle   interface{} `json:",omitempty"`
	TrackID     interface{} `json:",omitempty"`
	Year        interface{} `json:",omitempty"`
	Groups      []group     `json:",omitempty"`
	Tracks      []track     `json:",omitempty"`
}

type track struct {
	TrackID     string `json:",omitempty"`
	Name        string `json:",omitempty"`
	Album       string `json:",omitempty"`
	Artist      string `json:",omitempty"`
	AlbumArtist string `json:",omitempty"`
	Composer    string `json:",omitempty"`
	Year        int    `json:",omitempty"`
	DiscNumber  int    `json:",omitempty"`
	TotalTime   int    `json:",omitempty"`
	BitRate     int    `json:",omitempty"`
}

func buildCollection(h group, c index.Collection) group {
	getField := func(f string, g index.Group, c index.Collection) interface{} {
		if g.Field(f) == c.Field(f) {
			return nil
		}
		return g.Field(f)
	}

	for _, k := range c.Keys() {
		g := c.Get(k)
		g = index.FirstTrackAttr(index.StringAttr("AlbumArtist"), g)
		g = index.CommonGroupAttr([]index.Attr{index.StringAttr("Artist")}, g)
		h.Groups = append(h.Groups, group{
			Name:        g.Name(),
			Key:         k,
			AlbumArtist: getField("AlbumArtist", g, c),
			Artist:      getField("Artist", g, c),
		})
	}
	return h
}

func build(g index.Group, key index.Key) group {
	h := group{
		Name:        g.Name(),
		Key:         key,
		TotalTime:   g.Field("TotalTime"),
		Artist:      g.Field("Artist"),
		AlbumArtist: g.Field("AlbumArtist"),
		Composer:    g.Field("Composer"),
		Year:        g.Field("Year"),
		BitRate:     g.Field("BitRate"),
		DiscNumber:  g.Field("DiscNumber"),
		ListStyle:   g.Field("ListStyle"),
		TrackID:     g.Field("TrackID"),
	}

	if c, ok := g.(index.Collection); ok {
		return buildCollection(h, c)
	}

	getString := func(t index.Track, field string) string {
		if g.Field(field) != "" {
			return ""
		}
		return t.GetString(field)
	}

	getInt := func(t index.Track, field string) int {
		if g.Field(field) != 0 {
			return 0
		}
		return t.GetInt(field)
	}

	for _, t := range g.Tracks() {
		h.Tracks = append(h.Tracks, track{
			TrackID:   t.GetString("TrackID"),
			Name:      t.GetString("Name"),
			TotalTime: t.GetInt("TotalTime"),
			// Potentially common fields (don't want to re-transmit everything)
			Album:       getString(t, "Album"),
			Artist:      getString(t, "Artist"),
			AlbumArtist: getString(t, "AlbumArtist"),
			Composer:    getString(t, "Composer"),
			Year:        getInt(t, "Year"),
			DiscNumber:  getInt(t, "DiscNumber"),
			BitRate:     getInt(t, "BitRate"),
		})
	}
	return h
}

func (l *LibraryAPI) Fetch(c index.Collection, path []string) (group, error) {
	if len(path) == 0 {
		return build(c, index.Key("Root")), nil
	}

	var g index.Group = c
	k := index.Key(path[0])
	g = c.Get(k)

	if g == nil {
		return group{}, fmt.Errorf("invalid path: near '%v'", path[0])
	}

	index.Sort(g.Tracks(), index.MultiSort(index.SortByInt("DiscNumber"), index.SortByInt("TrackNumber")))
	c = index.Collect(g, index.ByPrefix("Name"))
	g = index.SubTransform(c, index.TrimEnumPrefix)
	g = index.SumGroupIntAttr("TotalTime", g)
	commonFields := []index.Attr{
		index.StringAttr("Album"),
		index.StringAttr("Artist"),
		index.StringAttr("AlbumArtist"),
		index.StringAttr("Composer"),
		index.IntAttr("Year"),
		index.IntAttr("BitRate"),
		index.IntAttr("DiscNumber"),
	}
	g = index.CommonGroupAttr(commonFields, g)
	g = index.RemoveEmptyCollections(g)

	for i, p := range path[1:] {
		var ok bool
		c, ok = g.(index.Collection)
		if !ok {
			return group{}, fmt.Errorf("retrieved Group is not a Collection")
		}
		k = index.Key(p)
		g = c.Get(k)

		if g == nil {
			return group{}, fmt.Errorf("invalid path near '%v'", path[1:][i])
		}

		if _, ok = g.(index.Collection); !ok {
			if i == len(path[1:])-1 {
				break
			}
			return group{}, fmt.Errorf("retrieved Group isn't a Collection: %v", p)
		}
	}
	if g == nil {
		return group{}, fmt.Errorf("could not find group")
	}
	g = index.FirstTrackAttr(index.StringAttr("TrackID"), g)

	return build(g, k), nil
}

func (l *LibraryAPI) FileSystem(fs http.FileSystem) http.FileSystem {
	return &libraryFileSystem{fs, l.Library}
}

func (l *LibraryAPI) ExpandPaths(paths []index.Path) group {
	return build(index.NewPathsCollection(l.collections["Root"], paths), index.Key("Root"))
}

type players struct {
	sync.RWMutex
	m map[string]Player
}

func newPlayers() *players {
	return &players{m: make(map[string]Player)}
}

func (s *players) add(p Player) {
	s.Lock()
	defer s.Unlock()

	s.m[p.Key()] = p
}

func (s *players) remove(key string) {
	s.Lock()
	defer s.Unlock()

	delete(s.m, key)
}

func (s *players) get(key string) Player {
	s.RLock()
	defer s.RUnlock()

	return s.m[key]
}

func (s *players) list() []string {
	s.RLock()
	defer s.RUnlock()

	keys := make([]string, 0, len(s.m))
	for k := range s.m {
		keys = append(keys, k)
	}
	return keys
}

func (s *players) MarshalJSON() ([]byte, error) {
	keys := s.list()
	return json.Marshal(struct {
		Keys []string `json:"keys"`
	}{
		Keys: keys,
	})
}

func playersGet(l LibraryAPI, w http.ResponseWriter, r *http.Request) {
	b, err := json.Marshal(l.players)
	if err != nil {
		http.Error(w, fmt.Sprintf("error encoding JSON: %v", err), http.StatusInternalServerError)
		return
	}

	w.Header().Add("Content-Type", "application/json")
	_, err = w.Write(b)
	if err != nil {
		log.Printf("error writing response: %v", err)
	}
}

func createPlayer(l LibraryAPI, w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	defer r.Body.Close()

	postData := struct {
		Key        string
		PlayerKeys []string
	}{}
	err := dec.Decode(&postData)
	if err != nil {
		http.Error(w, fmt.Sprintf("error parsing JSON: %v", err), http.StatusBadRequest)
		return
	}

	if p := l.players.get(postData.Key); p != nil {
		http.Error(w, "player key already exists", http.StatusBadRequest)
		return
	}

	if postData.PlayerKeys == nil || len(postData.PlayerKeys) == 0 {
		http.Error(w, "no player keys specified", http.StatusBadRequest)
		return
	}

	var players []Player
	for _, pk := range postData.PlayerKeys {
		p := l.players.get(pk)
		if p == nil {
			http.Error(w, fmt.Sprintf("invalid player key: %v", pk), http.StatusBadRequest)
			return
		}
		players = append(players, p)
	}
	l.players.add(MultiPlayer(postData.Key, players...))
	w.WriteHeader(http.StatusCreated)
}

func playerAction(p Player, w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	defer r.Body.Close()

	putData := struct {
		Action string
		Value  interface{}
	}{}
	err := dec.Decode(&putData)
	if err != nil {
		http.Error(w, fmt.Sprintf("error parsing JSON: %v", err), http.StatusBadRequest)
		return
	}

	switch putData.Action {
	case "play":
		err = p.Play()

	case "pause":
		err = p.Pause()

	case "next":
		err = p.NextTrack()

	case "prev":
		err = p.PreviousTrack()

	case "togglePlayPause":
		err = p.TogglePlayPause()

	case "toggleMute":
		err = p.ToggleMute()

	case "setVolume":
		f, ok := putData.Value.(float64)
		if !ok {
			err = InvalidValueError("invalid volume value: expected float")
			break
		}
		err = p.SetVolume(f)

	case "setMute":
		b, ok := putData.Value.(bool)
		if !ok {
			err = InvalidValueError("invalid mute value: expected boolean")
			break
		}
		err = p.SetMute(b)

	case "setTime":
		f, ok := putData.Value.(float64)
		if !ok {
			err = InvalidValueError("invalid time value: expected float")
			break
		}
		err = p.SetTime(f)

	default:
		err = InvalidValueError("invalid action")
		return
	}

	if err != nil {
		if err, ok := err.(InvalidValueError); ok {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		http.Error(w, fmt.Sprintf("error sending player command: %v", err), http.StatusInternalServerError)
	}
}

func playerView(p Player, w http.ResponseWriter, t *http.Request) {
	enc := json.NewEncoder(w)
	err := enc.Encode(p)
	if err != nil {
		log.Printf("error encoding player data: %v", err)
		return
	}
}

func playersHandler(l LibraryAPI) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "" {
			switch r.Method {
			case "GET":
				playersGet(l, w, r)
			case "POST":
				createPlayer(l, w, r)
			}
			return
		}

		paths := strings.Split(r.URL.Path, "/")
		if len(paths) != 1 {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}

		p := l.players.get(paths[0])
		if p == nil {
			http.Error(w, "invalid player key", http.StatusBadRequest)
			return
		}

		switch r.Method {
		case "DELETE":
			l.players.remove(paths[0])
			w.WriteHeader(http.StatusNoContent)
			return

		case "PUT":
			playerAction(p, w, r)

		case "GET":
			playerView(p, w, r)
		}
	})
}
<commit_msg>Fix: non-common fields were not correctly transmitted.<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"

	"github.com/tchaik/tchaik/index"
)

type LibraryAPI struct {
	index.Library

	collections map[string]index.Collection
	filters     map[string][]index.FilterItem
	recent      []index.Path
	searcher    index.Searcher
	players     *players
}

type libraryFileSystem struct {
	http.FileSystem
	index.Library
}

// Open implements http.FileSystem and rewrites TrackID values to their corresponding Location
// values using the index.Library
func (l *libraryFileSystem) Open(path string) (http.File, error) {
	t, ok := l.Library.Track(strings.Trim(path, "/")) // TrackIDs arrive with leading slash
	if !ok {
		return nil, fmt.Errorf("could not find track: %v", path)
	}

	loc := t.GetString("Location")
	if loc == "" {
		return nil, fmt.Errorf("invalid (empty) location for track: %v", path)
	}
	return l.FileSystem.Open(loc)
}

type group struct {
	Name        string
	Key         index.Key
	TotalTime   interface{} `json:",omitempty"`
	Artist      interface{} `json:",omitempty"`
	AlbumArtist interface{} `json:",omitempty"`
	Composer    interface{} `json:",omitempty"`
	BitRate     interface{} `json:",omitempty"`
	DiscNumber  interface{} `json:",omitempty"`
	ListStyle   interface{} `json:",omitempty"`
	TrackID     interface{} `json:",omitempty"`
	Year        interface{} `json:",omitempty"`
	Groups      []group     `json:",omitempty"`
	Tracks      []track     `json:",omitempty"`
}

type track struct {
	TrackID     string `json:",omitempty"`
	Name        string `json:",omitempty"`
	Album       string `json:",omitempty"`
	Artist      string `json:",omitempty"`
	AlbumArtist string `json:",omitempty"`
	Composer    string `json:",omitempty"`
	Year        int    `json:",omitempty"`
	DiscNumber  int    `json:",omitempty"`
	TotalTime   int    `json:",omitempty"`
	BitRate     int    `json:",omitempty"`
}

func buildCollection(h group, c index.Collection) group {
	getField := func(f string, g index.Group, c index.Collection) interface{} {
		if g.Field(f) == c.Field(f) {
			return nil
		}
		return g.Field(f)
	}

	for _, k := range c.Keys() {
		g := c.Get(k)
		g = index.FirstTrackAttr(index.StringAttr("AlbumArtist"), g)
		g = index.CommonGroupAttr([]index.Attr{index.StringAttr("Artist")}, g)
		h.Groups = append(h.Groups, group{
			Name:        g.Name(),
			Key:         k,
			AlbumArtist: getField("AlbumArtist", g, c),
			Artist:      getField("Artist", g, c),
		})
	}
	return h
}

func build(g index.Group, key index.Key) group {
	h := group{
		Name:        g.Name(),
		Key:         key,
		TotalTime:   g.Field("TotalTime"),
		Artist:      g.Field("Artist"),
		AlbumArtist: g.Field("AlbumArtist"),
		Composer:    g.Field("Composer"),
		Year:        g.Field("Year"),
		BitRate:     g.Field("BitRate"),
		DiscNumber:  g.Field("DiscNumber"),
		ListStyle:   g.Field("ListStyle"),
		TrackID:     g.Field("TrackID"),
	}

	if c, ok := g.(index.Collection); ok {
		return buildCollection(h, c)
	}

	getString := func(t index.Track, field string) string {
		if g.Field(field) != nil {
			return ""
		}
		return t.GetString(field)
	}

	getInt := func(t index.Track, field string) int {
		if g.Field(field) != nil {
			return 0
		}
		return t.GetInt(field)
	}

	for _, t := range g.Tracks() {
		h.Tracks = append(h.Tracks, track{
			TrackID:   t.GetString("TrackID"),
			Name:      t.GetString("Name"),
			TotalTime: t.GetInt("TotalTime"),
			// Potentially common fields (don't want to re-transmit everything)
			Album:       getString(t, "Album"),
			Artist:      getString(t, "Artist"),
			AlbumArtist: getString(t, "AlbumArtist"),
			Composer:    getString(t, "Composer"),
			Year:        getInt(t, "Year"),
			DiscNumber:  getInt(t, "DiscNumber"),
			BitRate:     getInt(t, "BitRate"),
		})
	}
	return h
}

func (l *LibraryAPI) Fetch(c index.Collection, path []string) (group, error) {
	if len(path) == 0 {
		return build(c, index.Key("Root")), nil
	}

	var g index.Group = c
	k := index.Key(path[0])
	g = c.Get(k)

	if g == nil {
		return group{}, fmt.Errorf("invalid path: near '%v'", path[0])
	}

	index.Sort(g.Tracks(), index.MultiSort(index.SortByInt("DiscNumber"), index.SortByInt("TrackNumber")))
	c = index.Collect(g, index.ByPrefix("Name"))
	g = index.SubTransform(c, index.TrimEnumPrefix)
	g = index.SumGroupIntAttr("TotalTime", g)
	commonFields := []index.Attr{
		index.StringAttr("Album"),
		index.StringAttr("Artist"),
		index.StringAttr("AlbumArtist"),
		index.StringAttr("Composer"),
		index.IntAttr("Year"),
		index.IntAttr("BitRate"),
		index.IntAttr("DiscNumber"),
	}
	g = index.CommonGroupAttr(commonFields, g)
	g = index.RemoveEmptyCollections(g)

	for i, p := range path[1:] {
		var ok bool
		c, ok = g.(index.Collection)
		if !ok {
			return group{}, fmt.Errorf("retrieved Group is not a Collection")
		}
		k = index.Key(p)
		g = c.Get(k)

		if g == nil {
			return group{}, fmt.Errorf("invalid path near '%v'", path[1:][i])
		}

		if _, ok = g.(index.Collection); !ok {
			if i == len(path[1:])-1 {
				break
			}
			return group{}, fmt.Errorf("retrieved Group isn't a Collection: %v", p)
		}
	}
	if g == nil {
		return group{}, fmt.Errorf("could not find group")
	}
	g = index.FirstTrackAttr(index.StringAttr("TrackID"), g)

	return build(g, k), nil
}

func (l *LibraryAPI) FileSystem(fs http.FileSystem) http.FileSystem {
	return &libraryFileSystem{fs, l.Library}
}

func (l *LibraryAPI) ExpandPaths(paths []index.Path) group {
	return build(index.NewPathsCollection(l.collections["Root"], paths), index.Key("Root"))
}

type players struct {
	sync.RWMutex
	m map[string]Player
}

func newPlayers() *players {
	return &players{m: make(map[string]Player)}
}

func (s *players) add(p Player) {
	s.Lock()
	defer s.Unlock()

	s.m[p.Key()] = p
}

func (s *players) remove(key string) {
	s.Lock()
	defer s.Unlock()

	delete(s.m, key)
}

func (s *players) get(key string) Player {
	s.RLock()
	defer s.RUnlock()

	return s.m[key]
}

func (s *players) list() []string {
	s.RLock()
	defer s.RUnlock()

	keys := make([]string, 0, len(s.m))
	for k := range s.m {
		keys = append(keys, k)
	}
	return keys
}

func (s *players) MarshalJSON() ([]byte, error) {
	keys := s.list()
	return json.Marshal(struct {
		Keys []string `json:"keys"`
	}{
		Keys: keys,
	})
}

func playersGet(l LibraryAPI, w http.ResponseWriter, r *http.Request) {
	b, err := json.Marshal(l.players)
	if err != nil {
		http.Error(w, fmt.Sprintf("error encoding JSON: %v", err), http.StatusInternalServerError)
		return
	}

	w.Header().Add("Content-Type", "application/json")
	_, err = w.Write(b)
	if err != nil {
		log.Printf("error writing response: %v", err)
	}
}

func createPlayer(l LibraryAPI, w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	defer r.Body.Close()

	postData := struct {
		Key        string
		PlayerKeys []string
	}{}
	err := dec.Decode(&postData)
	if err != nil {
		http.Error(w, fmt.Sprintf("error parsing JSON: %v", err), http.StatusBadRequest)
		return
	}

	if p := l.players.get(postData.Key); p != nil {
		http.Error(w, "player key already exists", http.StatusBadRequest)
		return
	}

	if postData.PlayerKeys == nil || len(postData.PlayerKeys) == 0 {
		http.Error(w, "no player keys specified", http.StatusBadRequest)
		return
	}

	var players []Player
	for _, pk := range postData.PlayerKeys {
		p := l.players.get(pk)
		if p == nil {
			http.Error(w, fmt.Sprintf("invalid player key: %v", pk), http.StatusBadRequest)
			return
		}
		players = append(players, p)
	}
	l.players.add(MultiPlayer(postData.Key, players...))
	w.WriteHeader(http.StatusCreated)
}

func playerAction(p Player, w http.ResponseWriter, r *http.Request) {
	dec := json.NewDecoder(r.Body)
	defer r.Body.Close()

	putData := struct {
		Action string
		Value  interface{}
	}{}
	err := dec.Decode(&putData)
	if err != nil {
		http.Error(w, fmt.Sprintf("error parsing JSON: %v", err), http.StatusBadRequest)
		return
	}

	switch putData.Action {
	case "play":
		err = p.Play()

	case "pause":
		err = p.Pause()

	case "next":
		err = p.NextTrack()

	case "prev":
		err = p.PreviousTrack()

	case "togglePlayPause":
		err = p.TogglePlayPause()

	case "toggleMute":
		err = p.ToggleMute()

	case "setVolume":
		f, ok := putData.Value.(float64)
		if !ok {
			err = InvalidValueError("invalid volume value: expected float")
			break
		}
		err = p.SetVolume(f)

	case "setMute":
		b, ok := putData.Value.(bool)
		if !ok {
			err = InvalidValueError("invalid mute value: expected boolean")
			break
		}
		err = p.SetMute(b)

	case "setTime":
		f, ok := putData.Value.(float64)
		if !ok {
			err = InvalidValueError("invalid time value: expected float")
			break
		}
		err = p.SetTime(f)

	default:
		err = InvalidValueError("invalid action")
		return
	}

	if err != nil {
		if err, ok := err.(InvalidValueError); ok {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		http.Error(w, fmt.Sprintf("error sending player command: %v", err), http.StatusInternalServerError)
	}
}

func playerView(p Player, w http.ResponseWriter, t *http.Request) {
	enc := json.NewEncoder(w)
	err := enc.Encode(p)
	if err != nil {
		log.Printf("error encoding player data: %v", err)
		return
	}
}

func playersHandler(l LibraryAPI) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "" {
			switch r.Method {
			case "GET":
				playersGet(l, w, r)
			case "POST":
				createPlayer(l, w, r)
			}
			return
		}

		paths := strings.Split(r.URL.Path, "/")
		if len(paths) != 1 {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}

		p := l.players.get(paths[0])
		if p == nil {
			http.Error(w, "invalid player key", http.StatusBadRequest)
			return
		}

		switch r.Method {
		case "DELETE":
			l.players.remove(paths[0])
			w.WriteHeader(http.StatusNoContent)
			return

		case "PUT":
			playerAction(p, w, r)

		case "GET":
			playerView(p, w, r)
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/globocom/tsuru/cmd"
	"github.com/globocom/tsuru/cmd/tsuru-base"
	"io/ioutil"
	"launchpad.net/gnuflag"
	"net/http"
)

type AppCreate struct{}

func (AppCreate) Run(context *cmd.Context, client *cmd.Client) error {
	appName := context.Args[0]
	platform := context.Args[1]
	memory := context.Args[2]
	// TODO: Read default memory from config and ensure that user can use the choosen amount of memory
	if memory == "" {
	   memory = 128 // 128
	}
	memory = memory*1024*1024 // Convert in MB
	b := bytes.NewBufferString(fmt.Sprintf(`{"name":"%s","platform":"%s","memory":"%s"}`, appName, platform, memory))
	url, err := cmd.GetURL("/apps")
	if err != nil {
		return err
	}
	request, err := http.NewRequest("POST", url, b)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	out := make(map[string]string)
	err = json.Unmarshal(result, &out)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, "App %q is being created!\n", appName)
	fmt.Fprintln(context.Stdout, "Use app-info to check the status of the app and its units.")
	fmt.Fprintf(context.Stdout, "Your repository for %q project is %q\n", appName, out["repository_url"])
	return nil
}

func (AppCreate) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "app-create",
		Usage:   "app-create <appname> <platform> <memory MB>",
		Desc:    "create a new app.",
		MinArgs: 2,
	}
}

type AppRemove struct {
	tsuru.GuessingCommand
	yes bool
	fs  *gnuflag.FlagSet
}

func (c *AppRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "app-remove",
		Usage: "app-remove [--app appname] [--assume-yes]",
		Desc: `removes an app.

If you don't provide the app name, tsuru will try to guess it.`,
		MinArgs: 0,
	}
}

func (c *AppRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	var answer string
	if !c.yes {
		fmt.Fprintf(context.Stdout, `Are you sure you want to remove app "%s"? (y/n) `, appName)
		fmt.Fscanf(context.Stdin, "%s", &answer)
		if answer != "y" {
			fmt.Fprintln(context.Stdout, "Abort.")
			return nil
		}
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, `App "%s" successfully removed!`+"\n", appName)
	return nil
}

func (c *AppRemove) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.GuessingCommand.Flags()
		c.fs.BoolVar(&c.yes, "assume-yes", false, "Don't ask for confirmation, just remove the app.")
		c.fs.BoolVar(&c.yes, "y", false, "Don't ask for confirmation, just remove the app.")
	}
	return c.fs
}

type UnitAdd struct {
	tsuru.GuessingCommand
}

func (c *UnitAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-add",
		Usage:   "unit-add <# of units> [--app appname]",
		Desc:    "add new units to an app.",
		MinArgs: 1,
	}
}

func (c *UnitAdd) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("PUT", url, bytes.NewBufferString(context.Args[0]))
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully added!")
	return nil
}

type UnitRemove struct {
	tsuru.GuessingCommand
}

func (c *UnitRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-remove",
		Usage:   "unit-remove <# of units> [--app appname]",
		Desc:    "remove units from an app.",
		MinArgs: 1,
	}
}

func (c *UnitRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	body := bytes.NewBufferString(context.Args[0])
	request, err := http.NewRequest("DELETE", url, body)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully removed!")
	return nil
}
<commit_msg>Some casting<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/globocom/tsuru/cmd"
	"github.com/globocom/tsuru/cmd/tsuru-base"
	"io/ioutil"
	"launchpad.net/gnuflag"
	"net/http"
	"strconv"
)

type AppCreate struct{}

func (AppCreate) Run(context *cmd.Context, client *cmd.Client) error {
	appName := context.Args[0]
	platform := context.Args[1]
	memory, _ := strconv.Atoi(context.Args[2])
	/* TODO:
	 * - Read default memory from config
	 * - Ensure that user can use the choosen amount of memory
	 * - Ensure that choosen memory is between mix and max in config
	 * - Ensure that choosen memory is a multiple of slot size in config
	 */
	if memory == 0 {
	   memory = 128 // 128
	}
	memory = memory*1024*1024 // Convert in MB
	b := bytes.NewBufferString(fmt.Sprintf(`{"name":"%s","platform":"%s","memory":"%d"}`, appName, platform, memory))
	url, err := cmd.GetURL("/apps")
	if err != nil {
		return err
	}
	request, err := http.NewRequest("POST", url, b)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}
	out := make(map[string]string)
	err = json.Unmarshal(result, &out)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, "App %q is being created!\n", appName)
	fmt.Fprintln(context.Stdout, "Use app-info to check the status of the app and its units.")
	fmt.Fprintf(context.Stdout, "Your repository for %q project is %q\n", appName, out["repository_url"])
	return nil
}

func (AppCreate) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "app-create",
		Usage:   "app-create <appname> <platform> <memory MB>",
		Desc:    "create a new app.",
		MinArgs: 2,
	}
}

type AppRemove struct {
	tsuru.GuessingCommand
	yes bool
	fs  *gnuflag.FlagSet
}

func (c *AppRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "app-remove",
		Usage: "app-remove [--app appname] [--assume-yes]",
		Desc: `removes an app.

If you don't provide the app name, tsuru will try to guess it.`,
		MinArgs: 0,
	}
}

func (c *AppRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	var answer string
	if !c.yes {
		fmt.Fprintf(context.Stdout, `Are you sure you want to remove app "%s"? (y/n) `, appName)
		fmt.Fscanf(context.Stdin, "%s", &answer)
		if answer != "y" {
			fmt.Fprintln(context.Stdout, "Abort.")
			return nil
		}
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintf(context.Stdout, `App "%s" successfully removed!`+"\n", appName)
	return nil
}

func (c *AppRemove) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.GuessingCommand.Flags()
		c.fs.BoolVar(&c.yes, "assume-yes", false, "Don't ask for confirmation, just remove the app.")
		c.fs.BoolVar(&c.yes, "y", false, "Don't ask for confirmation, just remove the app.")
	}
	return c.fs
}

type UnitAdd struct {
	tsuru.GuessingCommand
}

func (c *UnitAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-add",
		Usage:   "unit-add <# of units> [--app appname]",
		Desc:    "add new units to an app.",
		MinArgs: 1,
	}
}

func (c *UnitAdd) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	request, err := http.NewRequest("PUT", url, bytes.NewBufferString(context.Args[0]))
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully added!")
	return nil
}

type UnitRemove struct {
	tsuru.GuessingCommand
}

func (c *UnitRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unit-remove",
		Usage:   "unit-remove <# of units> [--app appname]",
		Desc:    "remove units from an app.",
		MinArgs: 1,
	}
}

func (c *UnitRemove) Run(context *cmd.Context, client *cmd.Client) error {
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
	if err != nil {
		return err
	}
	body := bytes.NewBufferString(context.Args[0])
	request, err := http.NewRequest("DELETE", url, body)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	fmt.Fprintln(context.Stdout, "Units successfully removed!")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/moov-io/ach"
	"github.com/moov-io/base/admin"
	moovhttp "github.com/moov-io/base/http"
	"github.com/moov-io/base/http/bind"
	"github.com/moov-io/paygate/pkg/util"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"
)

var (
	httpAddr  = flag.String("http.addr", bind.HTTP("ACH"), "HTTP listen address")
	adminAddr = flag.String("admin.addr", bind.Admin("ACH"), "Admin HTTP listen address")

	flagLogFormat = flag.String("log.format", "", "Format for log lines (Options: json, plain")
)

func main() {
	flag.Parse()

	log.Printf("Starting moov-io/ach webui version %s", ach.Version)

	// Channel for errors
	errs := make(chan error)

	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
		errs <- fmt.Errorf("%s", <-c)
	}()

	// Start Admin server (with Prometheus metrics)
	adminServer := admin.NewServer(*adminAddr)
	adminServer.AddVersionHandler(ach.Version) // Setup 'GET /version'
	go func() {
		log.Printf("listening on %s", adminServer.BindAddr())
		if err := adminServer.Listen(); err != nil {
			err = fmt.Errorf("problem starting admin http: %v", err)
			log.Print(err)
			errs <- err
		}
	}()
	defer adminServer.Shutdown()

	// Setup business HTTP routes
	router := mux.NewRouter()
	addPingRoute(router)

	// Register our assets route
	assetsPath := util.Or(os.Getenv("ASSETS_PATH"), filepath.Join("cmd", "webui", "assets"))
	log.Printf("serving assets from %s", assetsPath)
	addAssetsPath(router, http.FileServer(http.Dir(assetsPath)))

	serve := &http.Server{
		Addr:    *httpAddr,
		Handler: router,
		TLSConfig: &tls.Config{
			InsecureSkipVerify:       false,
			PreferServerCipherSuites: true,
			MinVersion:               tls.VersionTLS12,
		},
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  10 * time.Second,
	}
	shutdownServer := func() {
		if err := serve.Shutdown(context.TODO()); err != nil {
			log.Print(err)
		}
	}

	// Start business logic HTTP server
	go func() {
		if certFile, keyFile := os.Getenv("HTTPS_CERT_FILE"), os.Getenv("HTTPS_KEY_FILE"); certFile != "" && keyFile != "" {
			log.Printf("binding to %s for secure HTTP server", *httpAddr)
			if err := serve.ListenAndServeTLS(certFile, keyFile); err != nil {
				log.Print(err)
			}
		} else {
			log.Printf("binding to %s for HTTP server", *httpAddr)
			if err := serve.ListenAndServe(); err != nil {
				log.Print(err)
			}
		}
	}()

	// Block/Wait for an error
	if err := <-errs; err != nil {
		shutdownServer()
		log.Print(err)
	}
}

func addPingRoute(r *mux.Router) {
	r.Methods("GET").Path("/ping").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		moovhttp.SetAccessControlAllowHeaders(w, r.Header.Get("Origin"))
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("PONG"))
	})
}

func addAssetsPath(r *mux.Router, handler http.Handler) {
	r.Methods("GET").PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handler.ServeHTTP(w, r)
	})
}
<commit_msg>cmd/webui: remove unused -log.format flag<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/moov-io/ach"
	"github.com/moov-io/base/admin"
	moovhttp "github.com/moov-io/base/http"
	"github.com/moov-io/base/http/bind"
	"github.com/moov-io/paygate/pkg/util"

	"github.com/gorilla/mux"
)

var (
	httpAddr  = flag.String("http.addr", bind.HTTP("ACH"), "HTTP listen address")
	adminAddr = flag.String("admin.addr", bind.Admin("ACH"), "Admin HTTP listen address")
)

func main() {
	flag.Parse()

	log.Printf("Starting moov-io/ach webui version %s", ach.Version)

	// Channel for errors
	errs := make(chan error)

	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
		errs <- fmt.Errorf("%s", <-c)
	}()

	// Start Admin server (with Prometheus metrics)
	adminServer := admin.NewServer(*adminAddr)
	adminServer.AddVersionHandler(ach.Version) // Setup 'GET /version'
	go func() {
		log.Printf("listening on %s", adminServer.BindAddr())
		if err := adminServer.Listen(); err != nil {
			err = fmt.Errorf("problem starting admin http: %v", err)
			log.Print(err)
			errs <- err
		}
	}()
	defer adminServer.Shutdown()

	// Setup business HTTP routes
	router := mux.NewRouter()
	addPingRoute(router)

	// Register our assets route
	assetsPath := util.Or(os.Getenv("ASSETS_PATH"), filepath.Join("cmd", "webui", "assets"))
	log.Printf("serving assets from %s", assetsPath)
	addAssetsPath(router, http.FileServer(http.Dir(assetsPath)))

	serve := &http.Server{
		Addr:    *httpAddr,
		Handler: router,
		TLSConfig: &tls.Config{
			InsecureSkipVerify:       false,
			PreferServerCipherSuites: true,
			MinVersion:               tls.VersionTLS12,
		},
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  10 * time.Second,
	}
	shutdownServer := func() {
		if err := serve.Shutdown(context.TODO()); err != nil {
			log.Print(err)
		}
	}

	// Start business logic HTTP server
	go func() {
		if certFile, keyFile := os.Getenv("HTTPS_CERT_FILE"), os.Getenv("HTTPS_KEY_FILE"); certFile != "" && keyFile != "" {
			log.Printf("binding to %s for secure HTTP server", *httpAddr)
			if err := serve.ListenAndServeTLS(certFile, keyFile); err != nil {
				log.Print(err)
			}
		} else {
			log.Printf("binding to %s for HTTP server", *httpAddr)
			if err := serve.ListenAndServe(); err != nil {
				log.Print(err)
			}
		}
	}()

	// Block/Wait for an error
	if err := <-errs; err != nil {
		shutdownServer()
		log.Print(err)
	}
}

func addPingRoute(r *mux.Router) {
	r.Methods("GET").Path("/ping").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		moovhttp.SetAccessControlAllowHeaders(w, r.Header.Get("Origin"))
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("PONG"))
	})
}

func addAssetsPath(r *mux.Router, handler http.Handler) {
	r.Methods("GET").PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handler.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package strlit

import (
	"github.com/reiver/go-buffers"
	"github.com/reiver/go-oi"
	"github.com/reiver/go-utf8s"
	"github.com/reiver/go-whitespace"

	"bytes"
	"fmt"
	"io"
	"strings"
	"unicode/utf8"
)

// Bare provides methods to decode, and encode Bare String Literals
type Bare struct {}

// Decode decodes a Bare String Literal.
//
// ‘dst’ can be a []byte, or an io.Writer.
//
// ‘src’ can be a string, or a []byte, or an io.ReaderAt, or an io.ReadSeeker, or an io.RuneScanner.
func (receiver Bare) Decode(dst interface{}, src interface{}) (bytesWritten int, bytesRead int, err error) {

	if nil == dst {
		return 0, 0, errNilDestination
	}

	var writer io.Writer
	{
		switch casted := dst.(type) {
		case io.Writer:
			writer = casted
		case []byte:
			writer = buffers.NewWriter(casted)
		default:
			return 0, 0, fmt.Errorf("strlit: Unsupported Destination Type: %T", dst)
		}
	}

	if nil == src {
		return 0, 0, errNilSource
	}

	var runeScanner io.RuneScanner
	{
		switch casted := src.(type) {
		case io.RuneScanner:
			runeScanner = casted
		case io.ReadSeeker:
			runeScanner = utf8s.NewRuneScanner(casted)
		case io.ReaderAt:
			runeScanner = utf8s.NewRuneScanner(oi.ReadSeeker(casted))
		case []byte:
			runeScanner = bytes.NewReader(casted)
		case string:
			runeScanner = strings.NewReader(casted)
		default:
			return 0, 0, fmt.Errorf("strlit: Unsupported Source Type: %T", src)
		}
	}

	return receiver.decode(writer, runeScanner)
}

func (receiver Bare) decode(writer io.Writer, runeScanner io.RuneScanner) (bytesWritten int, bytesRead int, err error) {

	if nil == writer {
		return 0, 0, errNilDestination
	}

	if nil == runeScanner {
		return 0, 0, errNilSource
	}

	Loop: for {
		r, size, err := runeScanner.ReadRune()
		bytesRead += size
		if nil != err && io.EOF == err {
			if 0 == bytesRead {
				return bytesWritten, bytesRead, errEmptySource
			}
			break Loop
		}
		if nil != err {
			return bytesWritten, bytesRead, err
		}
		if utf8.RuneError == r && 0 == size {
			break Loop
		}
		if utf8.RuneError == r {
			return bytesWritten, bytesRead, errUTF8RuneError
		}

		switch {
		case whitespace.IsWhitespace(r):
			err := runeScanner.UnreadRune()
			if nil != err {
				return bytesWritten, bytesRead, err
			}
			bytesRead -= size

			if 0 == bytesWritten {
				return bytesWritten, bytesRead, errNotBareLiteral(r)
			}

			break Loop
		}

		n, err := utf8s.WriteRune(writer, r)
		bytesWritten += size
		if nil != err {
			return bytesWritten, bytesRead, err
		}

		if expected, actual := size, n; expected != actual {
			return bytesWritten, bytesRead, fmt.Errorf("strlit: Internal Error: wrong number of bytes copied; expected=%d actual=%d", expected, actual)
		}
	}

	return
}
<commit_msg>strlit.Bare, made it ‘src’ can be rune, and updated docs<commit_after>package strlit

import (
	"github.com/reiver/go-buffers"
	"github.com/reiver/go-oi"
	"github.com/reiver/go-utf8s"
	"github.com/reiver/go-whitespace"

	"bytes"
	"fmt"
	"io"
	"strings"
	"unicode/utf8"
)

// Bare provides methods to decode, and encode Bare String Literals
type Bare struct {}

// Decode decodes a Bare String Literal.
//
// ‘dst’ can be a []byte, or an io.Writer.
//
// ‘src’ can be a rune, or a string, or a []byte, or an io.ReaderAt, or an io.ReadSeeker, or an io.RuneScanner.
func (receiver Bare) Decode(dst interface{}, src interface{}) (bytesWritten int, bytesRead int, err error) {

	if nil == dst {
		return 0, 0, errNilDestination
	}

	var writer io.Writer
	{
		switch casted := dst.(type) {
		case io.Writer:
			writer = casted
		case []byte:
			writer = buffers.NewWriter(casted)
		default:
			return 0, 0, fmt.Errorf("strlit: Unsupported Destination Type: %T", dst)
		}
	}

	if nil == src {
		return 0, 0, errNilSource
	}

	var runeScanner io.RuneScanner
	{
		switch casted := src.(type) {
		case io.RuneScanner:
			runeScanner = casted
		case io.ReadSeeker:
			runeScanner = utf8s.NewRuneScanner(casted)
		case io.ReaderAt:
			runeScanner = utf8s.NewRuneScanner(oi.ReadSeeker(casted))
		case []byte:
			runeScanner = bytes.NewReader(casted)
		case string:
			runeScanner = strings.NewReader(casted)
		case rune:
			runeScanner = strings.NewReader(string(casted))
		default:
			return 0, 0, fmt.Errorf("strlit: Unsupported Source Type: %T", src)
		}
	}

	return receiver.decode(writer, runeScanner)
}

func (receiver Bare) decode(writer io.Writer, runeScanner io.RuneScanner) (bytesWritten int, bytesRead int, err error) {

	if nil == writer {
		return 0, 0, errNilDestination
	}

	if nil == runeScanner {
		return 0, 0, errNilSource
	}

	Loop: for {
		r, size, err := runeScanner.ReadRune()
		bytesRead += size
		if nil != err && io.EOF == err {
			if 0 == bytesRead {
				return bytesWritten, bytesRead, errEmptySource
			}
			break Loop
		}
		if nil != err {
			return bytesWritten, bytesRead, err
		}
		if utf8.RuneError == r && 0 == size {
			break Loop
		}
		if utf8.RuneError == r {
			return bytesWritten, bytesRead, errUTF8RuneError
		}

		switch {
		case whitespace.IsWhitespace(r):
			err := runeScanner.UnreadRune()
			if nil != err {
				return bytesWritten, bytesRead, err
			}
			bytesRead -= size

			if 0 == bytesWritten {
				return bytesWritten, bytesRead, errNotBareLiteral(r)
			}

			break Loop
		}

		n, err := utf8s.WriteRune(writer, r)
		bytesWritten += size
		if nil != err {
			return bytesWritten, bytesRead, err
		}

		if expected, actual := size, n; expected != actual {
			return bytesWritten, bytesRead, fmt.Errorf("strlit: Internal Error: wrong number of bytes copied; expected=%d actual=%d", expected, actual)
		}
	}

	return
}
<|endoftext|>


<commit_before>// Go wrapper for the BitX API.
// The API is documented here: https://bitx.co/api
package bitx

import "time"
import "net/http"
import "net/url"
import "errors"
import "encoding/json"
import "strconv"
import "fmt"
import "bytes"
import "io/ioutil"
import _ "crypto/sha512"

const userAgent = "bitx-go/0.0.2"

var base = url.URL{Scheme: "https", Host: "api.mybitx.com"}

type Client struct {
	api_key_id, api_key_secret string
}

// Pass an empty string for the api_key_id if you will only access the public
// API.
func NewClient(api_key_id, api_key_secret string) *Client {
	return &Client{api_key_id, api_key_secret}
}

func (c *Client) call(method, path string, params url.Values,
	result interface{}) error {
	u := base
	u.Path = path

	var body *bytes.Reader
	if method == "GET" {
		u.RawQuery = params.Encode()
		body = bytes.NewReader(nil)
	} else if method == "POST" {
		body = bytes.NewReader([]byte(params.Encode()))
	} else {
		return errors.New("Unsupported method")
	}

	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return err
	}
	if c.api_key_id != "" {
		req.SetBasicAuth(c.api_key_id, c.api_key_secret)
	}
	req.Header.Add("User-Agent", userAgent)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r, err := (&http.Client{}).Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	if r.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(r.Body)
		return errors.New(fmt.Sprintf(
			"BitX error %d: %s: %s",
			r.StatusCode, r.Status, string(body)))
	}

	if err := json.NewDecoder(r.Body).Decode(result); err != nil {
		return err
	}

	return nil
}

type ticker struct {
	Error     string `json:"error"`
	Timestamp int64  `json:"timestamp"`
	Bid       string `json:"bid"`
	Ask       string `json:"ask"`
	Last      string `json:"last_trade"`
	Volume24H string `json:"rolling_24_hour_volume"`
}

type Ticker struct {
	Timestamp                 time.Time
	Bid, Ask, Last, Volume24H float64
}

// Returns the latest ticker indicators for the given currency pair..
func (c *Client) Ticker(pair string) (Ticker, error) {
	var r ticker
	err := c.call("GET", "/api/1/ticker", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return Ticker{}, err
	}
	if r.Error != "" {
		return Ticker{}, errors.New("BitX error: " + r.Error)
	}

	t := time.Unix(r.Timestamp/1000, 0)

	bid, err := strconv.ParseFloat(r.Bid, 64)
	if err != nil {
		return Ticker{}, err
	}

	ask, err := strconv.ParseFloat(r.Ask, 64)
	if err != nil {
		return Ticker{}, err
	}

	last, err := strconv.ParseFloat(r.Last, 64)
	if err != nil {
		return Ticker{}, err
	}

	volume24h, err := strconv.ParseFloat(r.Volume24H, 64)
	if err != nil {
		return Ticker{}, err
	}

	return Ticker{t, bid, ask, last, volume24h}, nil
}

type orderbookEntry struct {
	Price  string `json:"price"`
	Volume string `json:"volume"`
}

type orderbook struct {
	Error string           `json:"error"`
	Asks  []orderbookEntry `json:"asks"`
	Bids  []orderbookEntry `json:"bids"`
}

type OrderBookEntry struct {
	Price, Volume float64
}

func convert(entries []orderbookEntry) (r []OrderBookEntry) {
	r = make([]OrderBookEntry, len(entries))
	for i, e := range entries {
		price, _ := strconv.ParseFloat(e.Price, 64)
		volume, _ := strconv.ParseFloat(e.Volume, 64)
		r[i].Price = price
		r[i].Volume = volume
	}
	return r
}

// Returns a list of bids and asks in the order book for the given currency
// pair.
func (c *Client) OrderBook(pair string) (
	bids, asks []OrderBookEntry, err error) {

	var r orderbook
	err = c.call("GET", "/api/1/orderbook", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return nil, nil, err
	}
	if r.Error != "" {
		return nil, nil, errors.New("BitX error: " + r.Error)
	}

	return convert(r.Bids), convert(r.Asks), nil
}

type trade struct {
	Timestamp int64  `json:"timestamp"`
	Price     string `json:"price"`
	Volume    string `json:"volume"`
}

type trades struct {
	Error  string  `json:"error"`
	Trades []trade `json:"trades"`
}

type Trade struct {
	Timestamp     time.Time
	Price, Volume float64
}

// Returns a list of the most recent trades for the given currency pair.
func (c *Client) Trades(pair string) ([]Trade, error) {
	var r trades
	err := c.call("GET", "/api/1/trades", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return nil, err
	}
	if r.Error != "" {
		return nil, errors.New("BitX error: " + r.Error)
	}

	tr := make([]Trade, len(r.Trades))
	for i, t := range r.Trades {
		tr[i].Timestamp = time.Unix(t.Timestamp/1000, 0)
		price, _ := strconv.ParseFloat(t.Price, 64)
		volume, _ := strconv.ParseFloat(t.Volume, 64)
		tr[i].Price = price
		tr[i].Volume = volume
	}
	return tr, nil
}

type postorder struct {
	OrderId string `json:"order_id"`
	Error   string `json:"error"`
}

type OrderType string

const BID = OrderType("BID")
const ASK = OrderType("ASK")

// Create a new trade order.
func (c *Client) PostOrder(pair string, order_type OrderType,
	volume, price float64) (string, error) {
	form := make(url.Values)
	form.Add("volume", fmt.Sprintf("%f", volume))
	form.Add("price", fmt.Sprintf("%f", price))
	form.Add("pair", pair)
	form.Add("type", string(order_type))

	var r postorder
	err := c.call("POST", "/api/1/postorder", form, &r)
	if err != nil {
		return "", err
	}
	if r.Error != "" {
		return "", errors.New("BitX error: " + r.Error)
	}

	return r.OrderId, nil
}

type order struct {
	OrderId           string `json:"order_id"`
	CreationTimestamp int64  `json:"creation_timestamp"`
	Type              string `json:"type"`
	State             string `json:"state"`
	LimitPrice        string `json:"limit_price"`
	LimitVolume       string `json:"limit_volume"`
	Base              string `json:"base"`
	Counter           string `json:"counter"`
	FeeBase           string `json:"fee_base"`
	FeeCounter        string `json:"fee_counter"`
}

type orders struct {
	Error  string  `json:"error"`
	Orders []order `json:"orders"`
}

type OrderState string

const Pending = OrderState("PENDING")
const Complete = OrderState("COMPLETE")

type Order struct {
	Id                  string
	CreatedAt           time.Time
	Type                OrderType
	State               OrderState
	LimitPrice          float64
	LimitVolume         float64
	Base, Counter       float64
	FeeBase, FeeCounter float64
}

func atofloat64(s string) float64 {
	f, _ := strconv.ParseFloat(s, 64)
	return f
}

// Returns a list of the most recently placed orders.
// The list is truncated after 100 items.
func (c *Client) ListOrders(pair string) ([]Order, error) {
	var r orders
	err := c.call("GET", "/api/1/listorders", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return nil, err
	}
	if r.Error != "" {
		return nil, errors.New("BitX error: " + r.Error)
	}

	orders := make([]Order, len(r.Orders))
	for i, bo := range r.Orders {
		o := &orders[i]
		o.Id = bo.OrderId
		o.Type = OrderType(bo.Type)
		o.State = OrderState(bo.State)
		o.CreatedAt = time.Unix(bo.CreationTimestamp/1000, 0)
		o.LimitPrice = atofloat64(bo.LimitPrice)
		o.LimitVolume = atofloat64(bo.LimitVolume)
		o.Base = atofloat64(bo.Base)
		o.Counter = atofloat64(bo.Counter)
		o.FeeBase = atofloat64(bo.FeeBase)
		o.FeeCounter = atofloat64(bo.FeeCounter)
	}
	return orders, nil
}

type stoporder struct {
	Success bool   `json:"success"`
	Error   string `json:"error"`
}

// Request to stop an order.
func (c *Client) StopOrder(id string) error {
	form := make(url.Values)
	form.Add("order_id", id)
	var r stoporder
	err := c.call("POST", "/api/1/stoporder", form, &r)
	if err != nil {
		return err
	}
	if r.Error != "" {
		return errors.New("BitX error: " + r.Error)
	}
	return nil
}

type balance struct {
	Asset    string `json:"asset"`
	Balance  string `json:"balance"`
	Reserved string `json:"reserved"`
}

type balances struct {
	Error   string    `json:"error"`
	Balance []balance `json:"balance"`
}

// Returns the trading account balance and reserved funds.
func (c *Client) Balance(asset string) (
	balance float64, reserved float64, err error) {
	var r balances
	err = c.call("GET", "/api/1/balance", url.Values{"asset": {asset}}, &r)
	if err != nil {
		return 0, 0, err
	}
	if r.Error != "" {
		return 0, 0, errors.New("BitX error: " + r.Error)
	}
	if len(r.Balance) == 0 {
		return 0, 0, errors.New("Balance not returned")
	}

	balance = atofloat64(r.Balance[0].Balance)
	reserved = atofloat64(r.Balance[0].Reserved)
	return balance, reserved, nil
}

func (c *Client) Send(amount, currency, address, desc, message string) error {
	form := make(url.Values)
	form.Add("amount", amount)
	form.Add("currency", currency)
	form.Add("address", address)
	form.Add("description", desc)
	form.Add("message", message)

	var r stoporder
	err := c.call("POST", "/api/1/send", form, &r)
	if err != nil {
		return err
	}
	if r.Error != "" {
		return errors.New("BitX error: " + r.Error)
	}

	return nil
}
<commit_msg>Implement GetOrder to fetch details of a specific order.<commit_after>// Go wrapper for the BitX API.
// The API is documented here: https://bitx.co/api
package bitx

import (
	"bytes"
	_ "crypto/sha512"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"time"
)

const userAgent = "bitx-go/0.0.3"

var base = url.URL{Scheme: "https", Host: "api.mybitx.com"}

type Client struct {
	api_key_id, api_key_secret string
}

// Pass an empty string for the api_key_id if you will only access the public
// API.
func NewClient(api_key_id, api_key_secret string) *Client {
	return &Client{api_key_id, api_key_secret}
}

func (c *Client) call(method, path string, params url.Values,
	result interface{}) error {
	u := base
	u.Path = path

	var body *bytes.Reader
	if method == "GET" {
		u.RawQuery = params.Encode()
		body = bytes.NewReader(nil)
	} else if method == "POST" {
		body = bytes.NewReader([]byte(params.Encode()))
	} else {
		return errors.New("Unsupported method")
	}

	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return err
	}
	if c.api_key_id != "" {
		req.SetBasicAuth(c.api_key_id, c.api_key_secret)
	}
	req.Header.Add("User-Agent", userAgent)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r, err := (&http.Client{}).Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	if r.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(r.Body)
		return errors.New(fmt.Sprintf(
			"BitX error %d: %s: %s",
			r.StatusCode, r.Status, string(body)))
	}

	if err := json.NewDecoder(r.Body).Decode(result); err != nil {
		return err
	}

	return nil
}

type ticker struct {
	Error     string `json:"error"`
	Timestamp int64  `json:"timestamp"`
	Bid       string `json:"bid"`
	Ask       string `json:"ask"`
	Last      string `json:"last_trade"`
	Volume24H string `json:"rolling_24_hour_volume"`
}

type Ticker struct {
	Timestamp                 time.Time
	Bid, Ask, Last, Volume24H float64
}

// Returns the latest ticker indicators for the given currency pair..
func (c *Client) Ticker(pair string) (Ticker, error) {
	var r ticker
	err := c.call("GET", "/api/1/ticker", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return Ticker{}, err
	}
	if r.Error != "" {
		return Ticker{}, errors.New("BitX error: " + r.Error)
	}

	t := time.Unix(r.Timestamp/1000, 0)

	bid, err := strconv.ParseFloat(r.Bid, 64)
	if err != nil {
		return Ticker{}, err
	}

	ask, err := strconv.ParseFloat(r.Ask, 64)
	if err != nil {
		return Ticker{}, err
	}

	last, err := strconv.ParseFloat(r.Last, 64)
	if err != nil {
		return Ticker{}, err
	}

	volume24h, err := strconv.ParseFloat(r.Volume24H, 64)
	if err != nil {
		return Ticker{}, err
	}

	return Ticker{t, bid, ask, last, volume24h}, nil
}

type orderbookEntry struct {
	Price  string `json:"price"`
	Volume string `json:"volume"`
}

type orderbook struct {
	Error string           `json:"error"`
	Asks  []orderbookEntry `json:"asks"`
	Bids  []orderbookEntry `json:"bids"`
}

type OrderBookEntry struct {
	Price, Volume float64
}

func convert(entries []orderbookEntry) (r []OrderBookEntry) {
	r = make([]OrderBookEntry, len(entries))
	for i, e := range entries {
		price, _ := strconv.ParseFloat(e.Price, 64)
		volume, _ := strconv.ParseFloat(e.Volume, 64)
		r[i].Price = price
		r[i].Volume = volume
	}
	return r
}

// Returns a list of bids and asks in the order book for the given currency
// pair.
func (c *Client) OrderBook(pair string) (
	bids, asks []OrderBookEntry, err error) {

	var r orderbook
	err = c.call("GET", "/api/1/orderbook", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return nil, nil, err
	}
	if r.Error != "" {
		return nil, nil, errors.New("BitX error: " + r.Error)
	}

	return convert(r.Bids), convert(r.Asks), nil
}

type trade struct {
	Timestamp int64  `json:"timestamp"`
	Price     string `json:"price"`
	Volume    string `json:"volume"`
}

type trades struct {
	Error  string  `json:"error"`
	Trades []trade `json:"trades"`
}

type Trade struct {
	Timestamp     time.Time
	Price, Volume float64
}

// Returns a list of the most recent trades for the given currency pair.
func (c *Client) Trades(pair string) ([]Trade, error) {
	var r trades
	err := c.call("GET", "/api/1/trades", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return nil, err
	}
	if r.Error != "" {
		return nil, errors.New("BitX error: " + r.Error)
	}

	tr := make([]Trade, len(r.Trades))
	for i, t := range r.Trades {
		tr[i].Timestamp = time.Unix(t.Timestamp/1000, 0)
		price, _ := strconv.ParseFloat(t.Price, 64)
		volume, _ := strconv.ParseFloat(t.Volume, 64)
		tr[i].Price = price
		tr[i].Volume = volume
	}
	return tr, nil
}

type postorder struct {
	OrderId string `json:"order_id"`
	Error   string `json:"error"`
}

type OrderType string

const BID = OrderType("BID")
const ASK = OrderType("ASK")

// Create a new trade order.
func (c *Client) PostOrder(pair string, order_type OrderType,
	volume, price float64) (string, error) {
	form := make(url.Values)
	form.Add("volume", fmt.Sprintf("%f", volume))
	form.Add("price", fmt.Sprintf("%f", price))
	form.Add("pair", pair)
	form.Add("type", string(order_type))

	var r postorder
	err := c.call("POST", "/api/1/postorder", form, &r)
	if err != nil {
		return "", err
	}
	if r.Error != "" {
		return "", errors.New("BitX error: " + r.Error)
	}

	return r.OrderId, nil
}

type order struct {
	Error             string `json:"error"`
	OrderId           string `json:"order_id"`
	CreationTimestamp int64  `json:"creation_timestamp"`
	Type              string `json:"type"`
	State             string `json:"state"`
	LimitPrice        string `json:"limit_price"`
	LimitVolume       string `json:"limit_volume"`
	Base              string `json:"base"`
	Counter           string `json:"counter"`
	FeeBase           string `json:"fee_base"`
	FeeCounter        string `json:"fee_counter"`
}

type orders struct {
	Error  string  `json:"error"`
	Orders []order `json:"orders"`
}

type OrderState string

const Pending = OrderState("PENDING")
const Complete = OrderState("COMPLETE")

type Order struct {
	Id                  string
	CreatedAt           time.Time
	Type                OrderType
	State               OrderState
	LimitPrice          float64
	LimitVolume         float64
	Base, Counter       float64
	FeeBase, FeeCounter float64
}

func atofloat64(s string) float64 {
	f, _ := strconv.ParseFloat(s, 64)
	return f
}

func parseOrder(bo order) Order {
	var o Order
	o.Id = bo.OrderId
	o.Type = OrderType(bo.Type)
	o.State = OrderState(bo.State)
	o.CreatedAt = time.Unix(bo.CreationTimestamp/1000, 0)
	o.LimitPrice = atofloat64(bo.LimitPrice)
	o.LimitVolume = atofloat64(bo.LimitVolume)
	o.Base = atofloat64(bo.Base)
	o.Counter = atofloat64(bo.Counter)
	o.FeeBase = atofloat64(bo.FeeBase)
	o.FeeCounter = atofloat64(bo.FeeCounter)
	return o
}

// Returns a list of the most recently placed orders.
// The list is truncated after 100 items.
func (c *Client) ListOrders(pair string) ([]Order, error) {
	var r orders
	err := c.call("GET", "/api/1/listorders", url.Values{"pair": {pair}}, &r)
	if err != nil {
		return nil, err
	}
	if r.Error != "" {
		return nil, errors.New("BitX error: " + r.Error)
	}

	orders := make([]Order, len(r.Orders))
	for i, bo := range r.Orders {
		orders[i] = parseOrder(bo)
	}
	return orders, nil
}

var pathIDRegex = regexp.MustCompile("^[[:alnum:]]+$")

func isValidPathID(id string) bool {
	if len(id) == 0 || len(id) > 255 {
		return false
	}
	return pathIDRegex.MatchString(id)
}

// Get an order by its id.
func (c *Client) GetOrder(id string) (*Order, error) {
	if !isValidPathID(id) {
		return nil, errors.New("invalid order id")
	}
	var bo order
	err := c.call("GET", "/api/1/orders/"+id, nil, &bo)
	if err != nil {
		return nil, err
	}
	if bo.Error != "" {
		return nil, errors.New("BitX error: " + bo.Error)
	}
	o := parseOrder(bo)
	return &o, nil
}

type stoporder struct {
	Success bool   `json:"success"`
	Error   string `json:"error"`
}

// Request to stop an order.
func (c *Client) StopOrder(id string) error {
	form := make(url.Values)
	form.Add("order_id", id)
	var r stoporder
	err := c.call("POST", "/api/1/stoporder", form, &r)
	if err != nil {
		return err
	}
	if r.Error != "" {
		return errors.New("BitX error: " + r.Error)
	}
	return nil
}

type balance struct {
	Asset    string `json:"asset"`
	Balance  string `json:"balance"`
	Reserved string `json:"reserved"`
}

type balances struct {
	Error   string    `json:"error"`
	Balance []balance `json:"balance"`
}

// Returns the trading account balance and reserved funds.
func (c *Client) Balance(asset string) (
	balance float64, reserved float64, err error) {
	var r balances
	err = c.call("GET", "/api/1/balance", url.Values{"asset": {asset}}, &r)
	if err != nil {
		return 0, 0, err
	}
	if r.Error != "" {
		return 0, 0, errors.New("BitX error: " + r.Error)
	}
	if len(r.Balance) == 0 {
		return 0, 0, errors.New("Balance not returned")
	}

	balance = atofloat64(r.Balance[0].Balance)
	reserved = atofloat64(r.Balance[0].Reserved)
	return balance, reserved, nil
}

func (c *Client) Send(amount, currency, address, desc, message string) error {
	form := make(url.Values)
	form.Add("amount", amount)
	form.Add("currency", currency)
	form.Add("address", address)
	form.Add("description", desc)
	form.Add("message", message)

	var r stoporder
	err := c.call("POST", "/api/1/send", form, &r)
	if err != nil {
		return err
	}
	if r.Error != "" {
		return errors.New("BitX error: " + r.Error)
	}

	return nil
}
<|endoftext|>


<commit_before>package director

import (
	"errors"
	"fmt"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func Test_TimedLooper(t *testing.T) {
	Convey("TimedLooper", t, func() {
		looper := NewTimedLooper(1, 1*time.Nanosecond, make(chan error))

		Convey("Sends a nil on the DoneChan when everything was kosher", func() {
			go looper.Done(nil)

			result := looper.Wait()
			So(result, ShouldBeNil)
		})

		Convey("Sends the error on the DoneChan when everything exploded", func() {
			err := errors.New("Borked!")
			go looper.Done(err)

			result := looper.Wait()
			So(result, ShouldEqual, err)
		})

		Convey("The loop executes the function", func() {
			run := false
			go looper.Loop(func() error { run = true; return nil })
			<-looper.DoneChan

			So(run, ShouldBeTrue)
		})

		Convey("The loop executes the correct number of times", func() {
			count := 0
			looper.Count = 5
			go looper.Loop(func() error { count++; return nil })
			looper.Wait()

			So(count, ShouldEqual, 5)
		})

		Convey("The loop returns an error on the DoneChan", func() {
			err := errors.New("Borked!")
			go looper.Loop(func() error { return err })
			So(looper.Wait(), ShouldEqual, err)
		})

		Convey("The loop exits when told to quit", func() {
			looper.Count = FOREVER
			count := 0

			go looper.Loop(func() error { count++; time.Sleep(2 * time.Nanosecond); return nil })
			looper.Quit()

			So(looper.Wait(), ShouldBeNil)
			So(count, ShouldBeLessThan, 2)
		})
	})
}

func Test_NewImmediateTimedLooper(t *testing.T) {
	Convey("ImmediateTimedLooper", t, func() {
		looper := NewImmediateTimedLooper(10, 1*time.Nanosecond, make(chan error))

		Convey("Immediate looper must have immediate set to true", func() {
			So(looper.Immediate, ShouldBeTrue)
		})
	})
}

func Test_FreeLooper(t *testing.T) {
	Convey("FreeLooper", t, func() {
		looper := NewFreeLooper(1, make(chan error))

		Convey("Sends a nil on the DoneChan when everything was kosher", func() {
			go looper.Done(nil)

			result := looper.Wait()
			So(result, ShouldBeNil)
		})

		Convey("Sends the error on the DoneChan when everything exploded", func() {
			err := errors.New("Borked!")
			go looper.Done(err)

			result := looper.Wait()
			So(result, ShouldEqual, err)
		})

		Convey("The loop executes the function", func() {
			run := false
			go looper.Loop(func() error { run = true; return nil })
			looper.Wait()

			So(run, ShouldBeTrue)
		})

		Convey("The loop executes the correct number of times", func() {
			count := 0
			looper.Count = 5
			go looper.Loop(func() error { count++; return nil })
			looper.Wait()

			So(count, ShouldEqual, 5)
		})

		Convey("The loop returns an error on the DoneChan", func() {
			err := errors.New("Borked!")
			go looper.Loop(func() error { return err })
			So(looper.Wait(), ShouldEqual, err)
		})

		Convey("The loop exits when told to quit", func() {
			looper.Count = FOREVER
			count := 0

			go looper.Loop(func() error { count++; time.Sleep(2 * time.Nanosecond); return nil })
			looper.Quit()

			So(looper.Wait(), ShouldBeNil)
			So(count, ShouldBeLessThan, 2)
		})
	})
}

// In this example, we run a really fast TimedLooper for a
// fixed number of runs.
func ExampleTimedLooper() {
	looper := NewTimedLooper(5, 1*time.Nanosecond, make(chan error))

	runner := func(looper Looper) {
		x := 0
		looper.Loop(func() error {
			fmt.Println(x)
			x++
			return nil
		})
	}

	go runner(looper)
	<-looper.DoneChan

	// Output:
	// 0
	// 1
	// 2
	// 3
	// 4
}

// In this example we run a really fast TimedLooper for a fixed
// number of runs, but we interrupt it with a Quit() call so
// it only completes one run.
func ExampleTimedLooper_Quit() {
	looper := NewTimedLooper(5, 50*time.Millisecond, make(chan error))

	runner := func(looper Looper) {
		x := 0
		looper.Loop(func() error {
			fmt.Println(x)
			x++
			return nil
		})
	}

	go runner(looper)
	looper.Quit()
	<-looper.DoneChan

	// Output:
	// 0
}

// In this example, we are going to run a FreeLooper with 5 iterations.
// In the course of running, an error is generated, which the parent
// function captures and outputs. As a result of the error only 3
// of the 5 iterations are completed and the output reflects this.
func Example() {
	looper := NewFreeLooper(5, make(chan error))

	runner := func(looper Looper) {
		x := 0
		looper.Loop(func() error {
			fmt.Println(x)
			x++
			if x == 3 {
				return errors.New("Uh oh")
			}
			return nil
		})
	}

	go runner(looper)
	err := <-looper.DoneChan

	if err != nil {
		fmt.Printf("I got an error: %s\n", err.Error())
	}

	// Output:
	// 0
	// 1
	// 2
	// I got an error: Uh oh
}
<commit_msg>Use the Wait() call instead of channel<commit_after>package director

import (
	"errors"
	"fmt"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func Test_TimedLooper(t *testing.T) {
	Convey("TimedLooper", t, func() {
		looper := NewTimedLooper(1, 1*time.Nanosecond, make(chan error))

		Convey("Sends a nil on the DoneChan when everything was kosher", func() {
			go looper.Done(nil)

			result := looper.Wait()
			So(result, ShouldBeNil)
		})

		Convey("Sends the error on the DoneChan when everything exploded", func() {
			err := errors.New("Borked!")
			go looper.Done(err)

			result := looper.Wait()
			So(result, ShouldEqual, err)
		})

		Convey("The loop executes the function", func() {
			run := false
			go looper.Loop(func() error { run = true; return nil })
			<-looper.DoneChan

			So(run, ShouldBeTrue)
		})

		Convey("The loop executes the correct number of times", func() {
			count := 0
			looper.Count = 5
			go looper.Loop(func() error { count++; return nil })
			looper.Wait()

			So(count, ShouldEqual, 5)
		})

		Convey("The loop returns an error on the DoneChan", func() {
			err := errors.New("Borked!")
			go looper.Loop(func() error { return err })
			So(looper.Wait(), ShouldEqual, err)
		})

		Convey("The loop exits when told to quit", func() {
			looper.Count = FOREVER
			count := 0

			go looper.Loop(func() error { count++; time.Sleep(2 * time.Nanosecond); return nil })
			looper.Quit()

			So(looper.Wait(), ShouldBeNil)
			So(count, ShouldBeLessThan, 2)
		})
	})
}

func Test_NewImmediateTimedLooper(t *testing.T) {
	Convey("ImmediateTimedLooper", t, func() {
		looper := NewImmediateTimedLooper(10, 1*time.Nanosecond, make(chan error))

		Convey("Immediate looper must have immediate set to true", func() {
			So(looper.Immediate, ShouldBeTrue)
		})
	})
}

func Test_FreeLooper(t *testing.T) {
	Convey("FreeLooper", t, func() {
		looper := NewFreeLooper(1, make(chan error))

		Convey("Sends a nil on the DoneChan when everything was kosher", func() {
			go looper.Done(nil)

			result := looper.Wait()
			So(result, ShouldBeNil)
		})

		Convey("Sends the error on the DoneChan when everything exploded", func() {
			err := errors.New("Borked!")
			go looper.Done(err)

			result := looper.Wait()
			So(result, ShouldEqual, err)
		})

		Convey("The loop executes the function", func() {
			run := false
			go looper.Loop(func() error { run = true; return nil })
			looper.Wait()

			So(run, ShouldBeTrue)
		})

		Convey("The loop executes the correct number of times", func() {
			count := 0
			looper.Count = 5
			go looper.Loop(func() error { count++; return nil })
			looper.Wait()

			So(count, ShouldEqual, 5)
		})

		Convey("The loop returns an error on the DoneChan", func() {
			err := errors.New("Borked!")
			go looper.Loop(func() error { return err })
			So(looper.Wait(), ShouldEqual, err)
		})

		Convey("The loop exits when told to quit", func() {
			looper.Count = FOREVER
			count := 0

			go looper.Loop(func() error { count++; time.Sleep(2 * time.Nanosecond); return nil })
			looper.Quit()

			So(looper.Wait(), ShouldBeNil)
			So(count, ShouldBeLessThan, 2)
		})
	})
}

// In this example, we run a really fast TimedLooper for a
// fixed number of runs.
func ExampleTimedLooper() {
	looper := NewTimedLooper(5, 1*time.Nanosecond, make(chan error))

	runner := func(looper Looper) {
		x := 0
		looper.Loop(func() error {
			fmt.Println(x)
			x++
			return nil
		})
	}

	go runner(looper)
	looper.Wait()

	// Output:
	// 0
	// 1
	// 2
	// 3
	// 4
}

// In this example we run a really fast TimedLooper for a fixed
// number of runs, but we interrupt it with a Quit() call so
// it only completes one run.
func ExampleTimedLooper_Quit() {
	looper := NewTimedLooper(5, 50*time.Millisecond, make(chan error))

	runner := func(looper Looper) {
		x := 0
		looper.Loop(func() error {
			fmt.Println(x)
			x++
			return nil
		})
	}

	go runner(looper)
	looper.Quit()
	looper.Wait()

	// Output:
	// 0
}

// In this example, we are going to run a FreeLooper with 5 iterations.
// In the course of running, an error is generated, which the parent
// function captures and outputs. As a result of the error only 3
// of the 5 iterations are completed and the output reflects this.
func Example() {
	looper := NewFreeLooper(5, make(chan error))

	runner := func(looper Looper) {
		x := 0
		looper.Loop(func() error {
			fmt.Println(x)
			x++
			if x == 3 {
				return errors.New("Uh oh")
			}
			return nil
		})
	}

	go runner(looper)
	err := looper.Wait()

	if err != nil {
		fmt.Printf("I got an error: %s\n", err.Error())
	}

	// Output:
	// 0
	// 1
	// 2
	// I got an error: Uh oh
}
<|endoftext|>


<commit_before>// Dirmap written in go.  (C) 2017.  All rights reserved
// dirmap.go
package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"timlibg"
)

const LastAltered = " 15 Sep 2018"

/*
  REVISION HISTORY
  -------- -------
   5 Nov 2017 -- First version, based on code dirwalk.
   8 Nov 2017 -- My first use of sort.Slice, which uses a closure as the less procedure.
  14 Sep 2018 -- Added map data structure to sort out why the subtotals are wrong, but the GrandTotal is right.
                   I think subdirectories are being entered more than once.  I need to sort the list by name and subtotal to find this.
				   I will remove the old way.  Then use the slices to sort and display results.
				   And either display the output or write to a file.
*/

type directory struct {
	name     string
	subtotal int64
}

type dirslice []directory

func (ds dirslice) Less(i, j int) bool {
	return ds[i].subtotal > ds[j].subtotal // I want a reverse sort, largest first
}

func (ds dirslice) Swap(i, j int) {
	ds[i], ds[j] = ds[j], ds[i]
}

func (ds dirslice) Len() int {
	return len(ds)
}

func main() {
	var GrandTotalSize, TotalOfFiles int64 // this used to be a uint64.  I think making it an int64 is better as of 09/14/2018 2:46:12 PM
	var startDirectory string
	var dirList dirslice

	fmt.Println()
	fmt.Println(" dirmap sums the directories it walks.  Written in Go.  Last altered ", LastAltered)

	if len(os.Args) < 2 {
		startDirectory, _ = os.Getwd()
	} else {
		startDirectory = os.Args[1]
	}
	start, err := os.Stat(startDirectory)
	if err != nil || !start.IsDir() {
		fmt.Println(" usage: diskwalk <directoryname>")
		os.Exit(1)
	}

	dirList = make(dirslice, 0, 500)
	DirMap := make(map[string]int64, 500)
	filepathwalkfunc := func(fpath string, fi os.FileInfo, err error) error { // this is a closure
		if err != nil {
			return err
		}

		if !fi.Mode().IsRegular() { // not a reg file, maybe a directory or symlink
			return nil
		}
		//  Now have a regular file.
		TotalOfFiles++
		GrandTotalSize += fi.Size()
		DirMap[filepath.Dir(fpath)] += fi.Size() // using a map so order of walk is not important

		return nil
	}

	filepath.Walk(startDirectory, filepathwalkfunc)

	// Prepare for output.

	GrandTotalString := strconv.FormatInt(GrandTotalSize, 10)
	GrandTotalString = AddCommas(GrandTotalString)
	fmt.Print(" start dir is ", startDirectory, "; found ", TotalOfFiles, " files in this tree. ")
	fmt.Println(" Total Size of walked tree is", GrandTotalString, ", and len of DirMap is", len(DirMap))

	fmt.Println()
	// Output map
	for n, m := range DirMap { // n is name as a string, m is map as a directory subtotal
		d := directory{} // this is a structured constant
		d.name = n
		d.subtotal = m
		dirList = append(dirList, d)
	}
	fmt.Println(" Length if dirList is", len(dirList))
	sort.Sort(dirList)

	datestr := MakeDateStr()
	outfilename := filepath.Base(startDirectory) + "_" + datestr
	outfile, err := os.Create(outfilename)
	defer outfile.Close()
	outputfile := bufio.NewWriter(outfile)
	defer outputfile.Flush()

	if err != nil {
		fmt.Println(" Cannot open outputfile ", outfilename, " with error ", err)
		// I'm going to assume this branch does not occur in the code below.  Else I would need a
		// stop flag of some kind to write to screen.
	}

	if len(dirList) < 30 {
		for _, d := range dirList {
			str := strconv.FormatInt(d.subtotal, 10)
			str = AddCommas(str)
			s := fmt.Sprintf("%s size is %s", d.name, str)
			fmt.Println(s)
		}
		fmt.Println()
	} else { // write output to a file.  First, build filename
		for _, d := range dirList {
			str := strconv.FormatInt(d.subtotal, 10)
			str = AddCommas(str)
			s := fmt.Sprintf("%s size is %s\n", d.name, str)
			outputfile.WriteString(s)
		}
		outputfile.WriteString("\n")
		outputfile.WriteString("\n")
		outputfile.Flush()
		outfile.Close()
	}
	fmt.Println()
} // main

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas

func min(i, j int) int {
	if i < j {
		return i
	} else {
		return j
	}
} // min

// ------------------------------------------- MakeDateStr ---------------------------------------------
func MakeDateStr() (datestr string) {

	const DateSepChar = "-"

	m, d, y := timlibg.TIME2MDY()
	timenow := timlibg.GetDateTime()

	MSTR := strconv.Itoa(m)
	DSTR := strconv.Itoa(d)
	YSTR := strconv.Itoa(y)
	Hr := strconv.Itoa(timenow.Hours)
	Min := strconv.Itoa(timenow.Minutes)
	Sec := strconv.Itoa(timenow.Seconds)

	datestr = "_" + MSTR + DateSepChar + DSTR + DateSepChar + YSTR + "_" + Hr + DateSepChar + Min + DateSepChar +
		Sec + "__" + timenow.DayOfWeekStr
	return datestr
} // MakeDateStr
<commit_msg>modified:   dirmap/dirmap.go -- and it finally work. 09/15/2018 07:45:58 PM<commit_after>// Dirmap written in go.  (C) 2017.  All rights reserved
// dirmap.go
package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"timlibg"
)

const LastAltered = " 15 Sep 2018"

/*
  REVISION HISTORY
  -------- -------
   5 Nov 2017 -- First version, based on code dirwalk.
   8 Nov 2017 -- My first use of sort.Slice, which uses a closure as the less procedure.
  14 Sep 2018 -- Added map data structure to sort out why the subtotals are wrong, but the GrandTotal is right.
                   I think subdirectories are being entered more than once.  I need to sort the list by name and subtotal to find this.
				   I will remove the old way.  Then use the slices to sort and display results.
				   And either display the output or write to a file.
*/

type directory struct {
	name     string
	subtotal int64
}

type dirslice []directory

func (ds dirslice) Less(i, j int) bool {
	return ds[i].subtotal > ds[j].subtotal // I want a reverse sort, largest first
}

func (ds dirslice) Swap(i, j int) {
	ds[i], ds[j] = ds[j], ds[i]
}

func (ds dirslice) Len() int {
	return len(ds)
}

func main() {
	var GrandTotalSize, TotalOfFiles int64 // this used to be a uint64.  I think making it an int64 is better as of 09/14/2018 2:46:12 PM
	var startDirectory string
	var dirList dirslice

	fmt.Println()
	fmt.Println(" dirmap sums the directories it walks.  Written in Go.  Last altered ", LastAltered)

	if len(os.Args) < 2 {
		startDirectory, _ = os.Getwd()
	} else {
		startDirectory = os.Args[1]
	}
	start, err := os.Stat(startDirectory)
	if err != nil || !start.IsDir() {
		fmt.Println(" usage: diskwalk <directoryname>")
		os.Exit(1)
	}

	dirList = make(dirslice, 0, 500)
	DirMap := make(map[string]int64, 500)
	filepathwalkfunc := func(fpath string, fi os.FileInfo, err error) error { // this is a closure
		if err != nil {
			return err
		}

		if !fi.Mode().IsRegular() { // not a reg file, maybe a directory or symlink
			return nil
		}
		//  Now have a regular file.
		TotalOfFiles++
		GrandTotalSize += fi.Size()
		DirMap[filepath.Dir(fpath)] += fi.Size() // using a map so order of walk is not important

		return nil
	}

	filepath.Walk(startDirectory, filepathwalkfunc)

	// Prepare for output.

	GrandTotalString := strconv.FormatInt(GrandTotalSize, 10)
	GrandTotalString = AddCommas(GrandTotalString)
	fmt.Print(" start dir is ", startDirectory, "; found ", TotalOfFiles, " files in this tree. ")
	fmt.Println(" Total Size of walked tree is", GrandTotalString, ", and len of DirMap is", len(DirMap))

	fmt.Println()
	// Output map
	for n, m := range DirMap { // n is name as a string, m is map as a directory subtotal
		d := directory{} // this is a structured constant
		d.name = n
		d.subtotal = m
		dirList = append(dirList, d)
	}
	fmt.Println(" Length of dirList is", len(dirList))
	sort.Sort(dirList)

	datestr := MakeDateStr()
	outfilename := filepath.Base(startDirectory) + "_" + datestr
	outfile, err := os.Create(outfilename)
	defer outfile.Close()
	outputfile := bufio.NewWriter(outfile)
	defer outputfile.Flush()

	if err != nil {
		fmt.Println(" Cannot open outputfile ", outfilename, " with error ", err)
		// I'm going to assume this branch does not occur in the code below.  Else I would need a
		// stop flag of some kind to write to screen.
	}

	if len(dirList) < 30 {
		for _, d := range dirList {
			str := strconv.FormatInt(d.subtotal, 10)
			str = AddCommas(str)
			s := fmt.Sprintf("%s size is %s", d.name, str)
			fmt.Println(s)
		}
		fmt.Println()
	} else { // write output to a file.  First, build filename
		s0 := fmt.Sprintf("start dir is %s, found %d files in this tree.  GrandTotal is %s, and number of directories is %d\n", startDirectory, TotalOfFiles, GrandTotalString, len(DirMap))
		outputfile.WriteString(s0)
		outputfile.WriteString("\n")
		for _, d := range dirList {
			str := strconv.FormatInt(d.subtotal, 10)
			str = AddCommas(str)
			s1 := fmt.Sprintf("%s size is %s\n", d.name, str)
			outputfile.WriteString(s1)
		}
		outputfile.WriteString("\n")
		outputfile.WriteString("\n")
		outputfile.Flush()
		outfile.Close()
	}
	fmt.Println()
} // main

//-------------------------------------------------------------------- InsertByteSlice
func InsertIntoByteSlice(slice, insertion []byte, index int) []byte {
	return append(slice[:index], append(insertion, slice[index:]...)...)
}

//---------------------------------------------------------------------- AddCommas
func AddCommas(instr string) string {
	var Comma []byte = []byte{','}

	BS := make([]byte, 0, 15)
	BS = append(BS, instr...)

	i := len(BS)

	for NumberOfCommas := i / 3; (NumberOfCommas > 0) && (i > 3); NumberOfCommas-- {
		i -= 3
		BS = InsertIntoByteSlice(BS, Comma, i)
	}
	return string(BS)
} // AddCommas

func min(i, j int) int {
	if i < j {
		return i
	} else {
		return j
	}
} // min

// ------------------------------------------- MakeDateStr ---------------------------------------------
func MakeDateStr() (datestr string) {

	const DateSepChar = "-"

	m, d, y := timlibg.TIME2MDY()
	timenow := timlibg.GetDateTime()

	MSTR := strconv.Itoa(m)
	DSTR := strconv.Itoa(d)
	YSTR := strconv.Itoa(y)
	Hr := strconv.Itoa(timenow.Hours)
	Min := strconv.Itoa(timenow.Minutes)
	Sec := strconv.Itoa(timenow.Seconds)

	datestr = "_" + MSTR + DateSepChar + DSTR + DateSepChar + YSTR + "_" + Hr + DateSepChar + Min + DateSepChar +
		Sec + "__" + timenow.DayOfWeekStr
	return datestr
} // MakeDateStr
<|endoftext|>


<commit_before>package azure

import (
	"encoding/xml"
	"fmt"
	"github.com/loldesign/azure/core"
	"io"
	"mime"
	"net/http"
	"path"
	"strings"
	"time"
)

var client = &http.Client{}

type Azure struct {
	Account string
	Key     string
}

type Blobs struct {
	XMLName xml.Name `xml:"EnumerationResults"`
	Itens   []Blob   `xml:"Blobs>Blob"`
}

type Blob struct {
	Name     string   `xml:"Name"`
	Property Property `xml:"Properties"`
}

type Property struct {
	LastModified  string `xml:"Last-Modified"`
	Etag          string `xml:"Etag"`
	ContentLength string `xml:"Content-Length"`
	ContentType   string `xml:"Content-Type"`
	BlobType      string `xml:"BlobType"`
	LeaseStatus   string `xml:"LeaseStatus"`
}

func (a Azure) doRequest(azureRequest core.AzureRequest) (*http.Response, error) {
	client, req := a.clientAndRequest(azureRequest)
	return client.Do(req)
}

func (a Azure) clientAndRequest(azureRequest core.AzureRequest) (*http.Client, *http.Request) {
	req := a.prepareRequest(azureRequest)

	return client, req
}

func (a Azure) prepareRequest(azureRequest core.AzureRequest) *http.Request {
	credentials := core.Credentials{
		Account:   a.Account,
		AccessKey: a.Key}

	return core.New(credentials, azureRequest).PrepareRequest()
}

func prepareMetadata(keys map[string]string) map[string]string {
	header := make(map[string]string)

	for k, v := range keys {
		key := fmt.Sprintf("x-ms-meta-%s", k)
		header[key] = v
	}

	return header
}

func New(account, accessKey string) Azure {
	return Azure{account, accessKey}
}

func (a Azure) CreateContainer(container string, meta map[string]string) (*http.Response, error) {
	azureRequest := core.AzureRequest{
		Method:      "put",
		Container:   container,
		Resource:    "?restype=container",
		Header:      prepareMetadata(meta),
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}

func (a Azure) DeleteContainer(container string) (*http.Response, error) {
	azureRequest := core.AzureRequest{
		Method:      "delete",
		Container:   container,
		Resource:    "?restype=container",
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}

func (a Azure) FileUpload(container, name string, body io.Reader) (*http.Response, error) {
	extension := strings.ToLower(path.Ext(name))
	contentType := mime.TypeByExtension(extension)

	azureRequest := core.AzureRequest{
		Method:      "put",
		Container:   container,
		Blob:        name,
		Body:        body,
		Header:      map[string]string{"x-ms-blob-type": "BlockBlob", "Accept-Charset": "UTF-8", "Content-Type": contentType},
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}

func (a Azure) ListBlobs(container string) (Blobs, error) {
	var blobs Blobs

	azureRequest := core.AzureRequest{
		Method:      "get",
		Container:   container,
		Resource:    "?restype=container&comp=list",
		RequestTime: time.Now().UTC()}

	res, err := a.doRequest(azureRequest)

	if err != nil {
		return blobs, err
	}

	decoder := xml.NewDecoder(res.Body)
	decoder.Decode(&blobs)

	return blobs, nil
}

func (a Azure) DeleteBlob(container, name string) (bool, error) {
	azureRequest := core.AzureRequest{
		Method:      "delete",
		Container:   container,
		Blob:        name,
		RequestTime: time.Now().UTC()}

	res, err := a.doRequest(azureRequest)

	if err != nil {
		return false, err
	}

	if res.StatusCode != 202 {
		return false, fmt.Errorf("deleteBlob: %s", res.Status)
	}

	return true, nil
}
<commit_msg>creating method to download file<commit_after>package azure

import (
	"encoding/xml"
	"fmt"
	"github.com/loldesign/azure/core"
	"io"
	"mime"
	"net/http"
	"path"
	"strings"
	"time"
)

var client = &http.Client{}

type Azure struct {
	Account string
	Key     string
}

type Blobs struct {
	XMLName xml.Name `xml:"EnumerationResults"`
	Itens   []Blob   `xml:"Blobs>Blob"`
}

type Blob struct {
	Name     string   `xml:"Name"`
	Property Property `xml:"Properties"`
}

type Property struct {
	LastModified  string `xml:"Last-Modified"`
	Etag          string `xml:"Etag"`
	ContentLength string `xml:"Content-Length"`
	ContentType   string `xml:"Content-Type"`
	BlobType      string `xml:"BlobType"`
	LeaseStatus   string `xml:"LeaseStatus"`
}

func (a Azure) doRequest(azureRequest core.AzureRequest) (*http.Response, error) {
	client, req := a.clientAndRequest(azureRequest)
	return client.Do(req)
}

func (a Azure) clientAndRequest(azureRequest core.AzureRequest) (*http.Client, *http.Request) {
	req := a.prepareRequest(azureRequest)

	return client, req
}

func (a Azure) prepareRequest(azureRequest core.AzureRequest) *http.Request {
	credentials := core.Credentials{
		Account:   a.Account,
		AccessKey: a.Key}

	return core.New(credentials, azureRequest).PrepareRequest()
}

func prepareMetadata(keys map[string]string) map[string]string {
	header := make(map[string]string)

	for k, v := range keys {
		key := fmt.Sprintf("x-ms-meta-%s", k)
		header[key] = v
	}

	return header
}

func New(account, accessKey string) Azure {
	return Azure{account, accessKey}
}

func (a Azure) CreateContainer(container string, meta map[string]string) (*http.Response, error) {
	azureRequest := core.AzureRequest{
		Method:      "put",
		Container:   container,
		Resource:    "?restype=container",
		Header:      prepareMetadata(meta),
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}

func (a Azure) DeleteContainer(container string) (*http.Response, error) {
	azureRequest := core.AzureRequest{
		Method:      "delete",
		Container:   container,
		Resource:    "?restype=container",
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}

func (a Azure) FileUpload(container, name string, body io.Reader) (*http.Response, error) {
	extension := strings.ToLower(path.Ext(name))
	contentType := mime.TypeByExtension(extension)

	azureRequest := core.AzureRequest{
		Method:      "put",
		Container:   container,
		Blob:        name,
		Body:        body,
		Header:      map[string]string{"x-ms-blob-type": "BlockBlob", "Accept-Charset": "UTF-8", "Content-Type": contentType},
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}

func (a Azure) ListBlobs(container string) (Blobs, error) {
	var blobs Blobs

	azureRequest := core.AzureRequest{
		Method:      "get",
		Container:   container,
		Resource:    "?restype=container&comp=list",
		RequestTime: time.Now().UTC()}

	res, err := a.doRequest(azureRequest)

	if err != nil {
		return blobs, err
	}

	decoder := xml.NewDecoder(res.Body)
	decoder.Decode(&blobs)

	return blobs, nil
}

func (a Azure) DeleteBlob(container, name string) (bool, error) {
	azureRequest := core.AzureRequest{
		Method:      "delete",
		Container:   container,
		Blob:        name,
		RequestTime: time.Now().UTC()}

	res, err := a.doRequest(azureRequest)

	if err != nil {
		return false, err
	}

	if res.StatusCode != 202 {
		return false, fmt.Errorf("deleteBlob: %s", res.Status)
	}

	return true, nil
}

func (a Azure) FileDownload(container, name string) (*http.Response, error) {
	azureRequest := core.AzureRequest{
		Method:      "get",
		Container:   container,
		Blob:        name,
		RequestTime: time.Now().UTC()}

	return a.doRequest(azureRequest)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strconv"
	"time"
  "sync"
)

func LoopRequests(wg *sync.WaitGroup, m *sync.Mutex, finishTime time.Time) {
	for {
		StartScenario(wg, m, finishTime)
	}
}

func StartStressTest(worker int, cPath string, sPath string) {
	LoadHttpHeader(cPath)
  LoadScenario(sPath)
	ShowLog("Stress Test Start!  Number of Workers: " + strconv.Itoa(worker))
	finishTime := time.Now().Add(1 * time.Minute)

	wg := new(sync.WaitGroup)
	m := new(sync.Mutex)
	for i := 0; i < worker; i++ {
		wg.Add(1)
		go LoopRequests(wg, m, finishTime)
	}
	wg.Wait()

  ShowResult()
}

func ShowLog(str string) {
	fmt.Println(time.Now().Format("15:04:05") + "  " + str)
}

func main() {
	flag.Usage = func() {
		fmt.Println(`Usage: ./hakari [option]
Options:
  -w N	           Run with N workers
  -c FILE          Config file
  -s FILE          Scenario file`)
	}

	var (
		worker = flag.Int("w", 2, "Run with N workers")
    cPath = flag.String("c", "config.yaml", "Config file")
    sPath = flag.String("s", "scenario.yaml", "Scenario file")
	)
	flag.Parse()

	StartStressTest(*worker, *cPath, *sPath)
}
<commit_msg>add minutes option<commit_after>package main

import (
	"flag"
	"fmt"
	"strconv"
	"time"
  "sync"
)

func LoopRequests(wg *sync.WaitGroup, m *sync.Mutex, finishTime time.Time) {
	for {
		StartScenario(wg, m, finishTime)
	}
}

func StartStressTest(worker int, cPath string, sPath string, duration int) {
	LoadHttpHeader(cPath)
  LoadScenario(sPath)
	ShowLog("Stress Test Start!  Number of Workers: " + strconv.Itoa(worker))
	finishTime := time.Now().Add(time.Duration(duration) * time.Minute)

	wg := new(sync.WaitGroup)
	m := new(sync.Mutex)
	for i := 0; i < worker; i++ {
		wg.Add(1)
		go LoopRequests(wg, m, finishTime)
	}
	wg.Wait()

  ShowResult()
}

func ShowLog(str string) {
	fmt.Println(time.Now().Format("15:04:05") + "  " + str)
}

func main() {
	flag.Usage = func() {
		fmt.Println(`Usage: ./hakari [option]
Options:
  -w N	           Run with N workers.   default: 2
  -c FILE          Config file.          default: ./config.yaml
  -s FILE          Scenario file.        default: ./scenario.yaml
  -m N             Run for N minutes.    default: 1`)
	}

	var (
		worker = flag.Int("w", 2, "Run with N workers")
    cPath = flag.String("c", "config.yaml", "Config file")
    sPath = flag.String("s", "scenario.yaml", "Scenario file")
    duration = flag.Int("m", 1, "Run for N minutes")
	)
	flag.Parse()

	StartStressTest(*worker, *cPath, *sPath, *duration)
}
<|endoftext|>