<commit_before>package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type Round struct {
	Players         []Player
	State           int
	LastStateChange time.Time
	Bonus           Point
	Bombs           map[Point]bool
	FrameBuffer     Symbols
	Lock            sync.Mutex
}

func (round *Round) generateMap() {
	// http://www.theasciicode.com.ar
	for row := 0; row < mapHeight; row++ {
		for column := 0; column < mapWidth; column++ {
			var char []byte
			if (row == 0 || row == mapHeight-1) && column < mapWidth-2 {
				char = []byte("─")
			} else if column == 0 || column == mapWidth-3 || column == mapWidth-nameTableWidth {
				char = []byte("│")
			} else if column == mapWidth-2 {
				char = []byte("\r")
			} else if column == mapWidth-1 {
				char = []byte("\n")
			} else {
				char = []byte(" ")
			}
			round.FrameBuffer[row*mapWidth+column] = Symbol{0, char}
		}
	}
}

func (round *Round) generateBot() Player {
	// Get data of player and return the structure

	for {
		p := Player{Name: fmt.Sprintf("Bot %d", rand.Intn(10)+1), Health: 100, Bot: true, Car: Car{Speed: 1}}
		if !p.searchDuplicateName(round) {
			return p
		}
	}
}

func (round *Round) getPlayersExcept(exceptions []Player) []*Player {
	var rest []*Player
	for i := range round.Players {
		exception := false
		for k := range exceptions {
			if round.Players[i].Name == exceptions[k].Name {
				exception = true
			}
		}
		if !exception {
			rest = append(rest, &round.Players[i])
		}
	}
	return rest
}

func (round *Round) getRandomNonBotPlayerId() int {
	for {
		randId := rand.Intn(len(round.Players))
		targetPlayer := round.Players[randId]
		if targetPlayer.Health > 0 && !targetPlayer.Bot {
			return randId
		}
	}
}

func (round *Round) gameLogic() {
	for i := range round.Players {
		if round.Players[i].Bot {
			go round.Players[i].moveBot(round)
		} else {
			go round.Players[i].readDirection(round)
		}
		go round.Players[i].checkPosition(round)
		go round.Players[i].checkSpeed(round)
		go round.Players[i].checkHealth(round)
		go round.Players[i].checkBomb(round)
	}
}

func (round *Round) checkGameOver(activeFrameBuffer Symbols) {
	humans := 0
	deadHumans := 0
	deadPlayers := 0
	winnersName := ""

	for _, p := range round.Players {
		if p.Health <= 0 {
			deadPlayers++
		}

		if !p.Bot {
			humans++
			if p.Health <= 0 {
				deadHumans++
			} else {
				winnersName = p.Name
			}
		}
	}

	secondsLeft := round.LastStateChange.Unix() + maxRoundRunningTimeSec - time.Now().Unix()
	if humans == deadHumans || maxPlayersPerRound-deadPlayers == 1 || secondsLeft <= 0 {
		round.State = FINISHED
		fmt.Println("Round has changed to the state FINISHED")
		if maxPlayersPerRound-deadPlayers == 1 {
			winnerStr := "THE WINNER IS " + winnersName + "!!!"
			for i, char := range []byte(winnerStr) {
				activeFrameBuffer[mapWidth*(mapHeight/2-2)+mapWidth/2-len(winnerStr)/2+i] = Symbol{GREEN, []byte{char}}
			}
			round.writeToAllPlayers(activeFrameBuffer.symbolsToByte(), false)
			time.Sleep(5 * time.Second)
		}
	}
}

func (round *Round) over() {
	round.writeToAllPlayers([]byte("Time is out\n"), false)
	for _, player := range round.Players {
		if player.Bot {
			continue
		}
		player.Conn.Close()
	}
}

func (round *Round) writeToAllPlayers(message []byte, clean bool) {
	for _, player := range round.Players {
		if player.Bot {
			continue
		}

		go func(message []byte, player Player, clean bool) {
			if clean {
				player.Conn.Write(clear)
			}
			player.Conn.Write(home)
			player.Conn.Write(message)
		}(message, player, clean)
	}
}

func (round *Round) applyNames(lineBetweenPlayersInBar int) {
	for line, player := range round.Players {
		for i, char := range []byte(player.Name) {
			round.FrameBuffer[(line*lineBetweenPlayersInBar+1)*mapWidth+(mapWidth-nameTableWidth+1)+i] = Symbol{player.Color, []byte{char}}
		}
		round.FrameBuffer[(line*lineBetweenPlayersInBar+1)*mapWidth+(mapWidth-nameTableWidth+1)+len(player.Name)] = Symbol{RESET, []byte{':'}}
	}
}

func (round *Round) applyBonus(activeFrameBuffer []Symbol) {
	if round.State == STARTING {
		return
	}
	if round.Bonus.X == -1 && round.Bonus.Y == -1 {
		if rand.Int()%lowFactor == 0 {
			round.Bonus = Point{rand.Intn(mapWidth-nameTableWidth-2) + 1, rand.Intn(mapHeight-2) + 1}
		}
	} else {
		activeFrameBuffer[round.Bonus.Y*mapWidth+round.Bonus.X] = Symbol{RED, []byte(bonus)}
	}
}

func (round *Round) applyBombs(activeFrameBuffer []Symbol, lineBetweenPlayersInBar int) {
	if round.State == STARTING {
		return
	}

	round.Lock.Lock()
	for b, _ := range round.Bombs {
		activeFrameBuffer[b.Y*mapWidth+b.X] = Symbol{BOLD, []byte(bomb)}
	}
	round.Lock.Unlock()
}

func (round *Round) applyUserData(activeFrameBuffer []Symbol, lineBetweenPlayersInBar int) {
	for num, player := range round.Players {
		// Apply health
		health := []byte(fmt.Sprintf("Health: %3d", player.Health))
		for i, char := range health {
			// +1 because health is next line after the name
			activeFrameBuffer[((num*lineBetweenPlayersInBar+1)+1)*mapWidth+(mapWidth-3)-len(health)+i] = Symbol{player.Color, []byte{char}}
		}

		// Apply the amount of bombs to the bar
		bombs := []byte(fmt.Sprintf("Bombs: %4d", round.Players[num].Bombs))
		for i, char := range bombs {
			// +2 because "bombs" is next line after the name
			activeFrameBuffer[((num*lineBetweenPlayersInBar+2)+1)*mapWidth+(mapWidth-3)-len(bombs)+i] = Symbol{player.Color, []byte{char}}
		}
	}
}

func (round *Round) applyGetReady(activeFrameBuffer []Symbol, getReadyCounter *int) {
	if round.State == STARTING {
		getReady := "GET READY!"
		if *getReadyCounter == 0 {
			fmt.Println("Round has changed to the state RUNNING")
			round.State = RUNNING
		} else if *getReadyCounter <= framesPerSecond*1 {
			getReady += " 1"
		} else if *getReadyCounter <= framesPerSecond*2 {
			getReady += " 2"
		} else if *getReadyCounter <= framesPerSecond*3 {
			getReady += " 3"
		}

		for i, char := range []byte(getReady) {
			activeFrameBuffer[mapWidth*(mapHeight/2-2)+mapWidth/2-len(getReady)/2+i] = Symbol{GREEN, []byte{char}}
		}
		*getReadyCounter--
	}
}

func (round *Round) applyCars(activeMap []Symbol) {
	for _, player := range round.Players {
		charPosX, charPosY := 0, 0
		for i := 0; i < len(cars[player.Car.Direction]); i++ {
			var chars []byte
			if cars[player.Car.Direction][i] == byte('\n') {
				charPosY++
				charPosX = 0
				continue
			} else if cars[player.Car.Direction][i] == 226 {
				/*
				 This means extended ASCII is used. After 226 2 bytes must follow
				*/
				chars = []byte{cars[player.Car.Direction][i], cars[player.Car.Direction][i+1], cars[player.Car.Direction][i+2]}
				i += 2
			} else if cars[player.Car.Direction][i] == 194 {
				/*
				 This means extended ASCII is used. After 194 1 bytes must follow
				*/
				chars = []byte{cars[player.Car.Direction][i], cars[player.Car.Direction][i+1]}
				i++
			} else if player.Health <= 0 && cars[player.Car.Direction][i] == 'o' {
				chars = []byte{'x'}
			} else {
				chars = []byte{cars[player.Car.Direction][i]}
			}
			activeMap[(player.Car.Borders.Points[LEFTUP].Y+charPosY)*mapWidth+player.Car.Borders.Points[LEFTUP].X+charPosX] = Symbol{player.Color, chars}
			charPosX++
		}
	}
}

func (round Round) start() {
	lineBetweenPlayersInBar := mapHeight / len(round.Players)
	getReadyCounter := getReadyPause / framesPerSecond

	round.gameLogic()
	round.generateMap()
	round.applyNames(lineBetweenPlayersInBar)

	for {
		activeFrameBuffer := make(Symbols, len(round.FrameBuffer))
		copy(activeFrameBuffer, round.FrameBuffer)

		round.applyUserData(activeFrameBuffer, lineBetweenPlayersInBar)
		round.applyBonus(activeFrameBuffer)
		round.applyBombs(activeFrameBuffer, lineBetweenPlayersInBar)
		round.applyCars(activeFrameBuffer)
		round.applyGetReady(activeFrameBuffer, &getReadyCounter)

		round.writeToAllPlayers(activeFrameBuffer.symbolsToByte(), false)

		round.checkGameOver(activeFrameBuffer)
		if round.State == FINISHED {
			round.over()
			return
		}
		time.Sleep(1 % framesPerSecond * 100 * time.Millisecond)
	}
}
<commit_msg>Do not crash if cars going out of borders<commit_after>package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type Round struct {
	Players         []Player
	State           int
	LastStateChange time.Time
	Bonus           Point
	Bombs           map[Point]bool
	FrameBuffer     Symbols
	Lock            sync.Mutex
}

func (round *Round) generateMap() {
	// http://www.theasciicode.com.ar
	for row := 0; row < mapHeight; row++ {
		for column := 0; column < mapWidth; column++ {
			var char []byte
			if (row == 0 || row == mapHeight-1) && column < mapWidth-2 {
				char = []byte("─")
			} else if column == 0 || column == mapWidth-3 || column == mapWidth-nameTableWidth {
				char = []byte("│")
			} else if column == mapWidth-2 {
				char = []byte("\r")
			} else if column == mapWidth-1 {
				char = []byte("\n")
			} else {
				char = []byte(" ")
			}
			round.FrameBuffer[row*mapWidth+column] = Symbol{0, char}
		}
	}
}

func (round *Round) generateBot() Player {
	// Get data of player and return the structure

	for {
		p := Player{Name: fmt.Sprintf("Bot %d", rand.Intn(10)+1), Health: 100, Bot: true, Car: Car{Speed: 1}}
		if !p.searchDuplicateName(round) {
			return p
		}
	}
}

func (round *Round) getPlayersExcept(exceptions []Player) []*Player {
	var rest []*Player
	for i := range round.Players {
		exception := false
		for k := range exceptions {
			if round.Players[i].Name == exceptions[k].Name {
				exception = true
			}
		}
		if !exception {
			rest = append(rest, &round.Players[i])
		}
	}
	return rest
}

func (round *Round) getRandomNonBotPlayerId() int {
	for {
		randId := rand.Intn(len(round.Players))
		targetPlayer := round.Players[randId]
		if targetPlayer.Health > 0 && !targetPlayer.Bot {
			return randId
		}
	}
}

func (round *Round) gameLogic() {
	for i := range round.Players {
		if round.Players[i].Bot {
			go round.Players[i].moveBot(round)
		} else {
			go round.Players[i].readDirection(round)
		}
		go round.Players[i].checkPosition(round)
		go round.Players[i].checkSpeed(round)
		go round.Players[i].checkHealth(round)
		go round.Players[i].checkBomb(round)
	}
}

func (round *Round) checkGameOver(activeFrameBuffer Symbols) {
	humans := 0
	deadHumans := 0
	deadPlayers := 0
	winnersName := ""

	for _, p := range round.Players {
		if p.Health <= 0 {
			deadPlayers++
		}

		if !p.Bot {
			humans++
			if p.Health <= 0 {
				deadHumans++
			} else {
				winnersName = p.Name
			}
		}
	}

	secondsLeft := round.LastStateChange.Unix() + maxRoundRunningTimeSec - time.Now().Unix()
	if humans == deadHumans || maxPlayersPerRound-deadPlayers == 1 || secondsLeft <= 0 {
		round.State = FINISHED
		fmt.Println("Round has changed to the state FINISHED")
		if maxPlayersPerRound-deadPlayers == 1 {
			winnerStr := "THE WINNER IS " + winnersName + "!!!"
			for i, char := range []byte(winnerStr) {
				activeFrameBuffer[mapWidth*(mapHeight/2-2)+mapWidth/2-len(winnerStr)/2+i] = Symbol{GREEN, []byte{char}}
			}
			round.writeToAllPlayers(activeFrameBuffer.symbolsToByte(), false)
			time.Sleep(5 * time.Second)
		}
	}
}

func (round *Round) over() {
	round.writeToAllPlayers([]byte("Time is out\n"), false)
	for _, player := range round.Players {
		if player.Bot {
			continue
		}
		player.Conn.Close()
	}
}

func (round *Round) writeToAllPlayers(message []byte, clean bool) {
	for _, player := range round.Players {
		if player.Bot {
			continue
		}

		go func(message []byte, player Player, clean bool) {
			if clean {
				player.Conn.Write(clear)
			}
			player.Conn.Write(home)
			player.Conn.Write(message)
		}(message, player, clean)
	}
}

func (round *Round) applyNames(lineBetweenPlayersInBar int) {
	for line, player := range round.Players {
		for i, char := range []byte(player.Name) {
			round.FrameBuffer[(line*lineBetweenPlayersInBar+1)*mapWidth+(mapWidth-nameTableWidth+1)+i] = Symbol{player.Color, []byte{char}}
		}
		round.FrameBuffer[(line*lineBetweenPlayersInBar+1)*mapWidth+(mapWidth-nameTableWidth+1)+len(player.Name)] = Symbol{RESET, []byte{':'}}
	}
}

func (round *Round) applyBonus(activeFrameBuffer []Symbol) {
	if round.State == STARTING {
		return
	}
	if round.Bonus.X == -1 && round.Bonus.Y == -1 {
		if rand.Int()%lowFactor == 0 {
			round.Bonus = Point{rand.Intn(mapWidth-nameTableWidth-2) + 1, rand.Intn(mapHeight-2) + 1}
		}
	} else {
		activeFrameBuffer[round.Bonus.Y*mapWidth+round.Bonus.X] = Symbol{RED, []byte(bonus)}
	}
}

func (round *Round) applyBombs(activeFrameBuffer []Symbol, lineBetweenPlayersInBar int) {
	if round.State == STARTING {
		return
	}

	round.Lock.Lock()
	for b := range round.Bombs {
		activeFrameBuffer[b.Y*mapWidth+b.X] = Symbol{BOLD, []byte(bomb)}
	}
	round.Lock.Unlock()
}

func (round *Round) applyUserData(activeFrameBuffer []Symbol, lineBetweenPlayersInBar int) {
	for num, player := range round.Players {
		// Apply health
		health := []byte(fmt.Sprintf("Health: %3d", player.Health))
		for i, char := range health {
			// +1 because health is next line after the name
			activeFrameBuffer[((num*lineBetweenPlayersInBar+1)+1)*mapWidth+(mapWidth-3)-len(health)+i] = Symbol{player.Color, []byte{char}}
		}

		// Apply the amount of bombs to the bar
		bombs := []byte(fmt.Sprintf("Bombs: %4d", round.Players[num].Bombs))
		for i, char := range bombs {
			// +2 because "bombs" is next line after the name
			activeFrameBuffer[((num*lineBetweenPlayersInBar+2)+1)*mapWidth+(mapWidth-3)-len(bombs)+i] = Symbol{player.Color, []byte{char}}
		}
	}
}

func (round *Round) applyGetReady(activeFrameBuffer []Symbol, getReadyCounter *int) {
	if round.State == STARTING {
		getReady := "GET READY!"
		if *getReadyCounter == 0 {
			fmt.Println("Round has changed to the state RUNNING")
			round.State = RUNNING
		} else if *getReadyCounter <= framesPerSecond*1 {
			getReady += " 1"
		} else if *getReadyCounter <= framesPerSecond*2 {
			getReady += " 2"
		} else if *getReadyCounter <= framesPerSecond*3 {
			getReady += " 3"
		}

		for i, char := range []byte(getReady) {
			activeFrameBuffer[mapWidth*(mapHeight/2-2)+mapWidth/2-len(getReady)/2+i] = Symbol{GREEN, []byte{char}}
		}
		*getReadyCounter--
	}
}

func (round *Round) applyCars(activeMap []Symbol) {
	// Do not care about errors with moving away from slice
	defer func() {
		recover()
	}()

	for _, player := range round.Players {
		charPosX, charPosY := 0, 0
		for i := 0; i < len(cars[player.Car.Direction]); i++ {
			var chars []byte
			if cars[player.Car.Direction][i] == byte('\n') {
				charPosY++
				charPosX = 0
				continue
			} else if cars[player.Car.Direction][i] == 226 {
				/*
				 This means extended ASCII is used. After 226 2 bytes must follow
				*/
				chars = []byte{cars[player.Car.Direction][i], cars[player.Car.Direction][i+1], cars[player.Car.Direction][i+2]}
				i += 2
			} else if cars[player.Car.Direction][i] == 194 {
				/*
				 This means extended ASCII is used. After 194 1 bytes must follow
				*/
				chars = []byte{cars[player.Car.Direction][i], cars[player.Car.Direction][i+1]}
				i++
			} else if player.Health <= 0 && cars[player.Car.Direction][i] == 'o' {
				chars = []byte{'x'}
			} else {
				chars = []byte{cars[player.Car.Direction][i]}
			}
			activeMap[(player.Car.Borders.Points[LEFTUP].Y+charPosY)*mapWidth+player.Car.Borders.Points[LEFTUP].X+charPosX] = Symbol{player.Color, chars}
			charPosX++
		}
	}
}

func (round Round) start() {
	lineBetweenPlayersInBar := mapHeight / len(round.Players)
	getReadyCounter := getReadyPause / framesPerSecond

	round.gameLogic()
	round.generateMap()
	round.applyNames(lineBetweenPlayersInBar)

	for {
		activeFrameBuffer := make(Symbols, len(round.FrameBuffer))
		copy(activeFrameBuffer, round.FrameBuffer)

		round.applyUserData(activeFrameBuffer, lineBetweenPlayersInBar)
		round.applyBonus(activeFrameBuffer)
		round.applyBombs(activeFrameBuffer, lineBetweenPlayersInBar)
		round.applyCars(activeFrameBuffer)
		round.applyGetReady(activeFrameBuffer, &getReadyCounter)

		round.writeToAllPlayers(activeFrameBuffer.symbolsToByte(), false)

		round.checkGameOver(activeFrameBuffer)
		if round.State == FINISHED {
			round.over()
			return
		}
		time.Sleep(1 % framesPerSecond * 100 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>/********************************
*** Multiplexer for Go        ***
*** Bone is under MIT license ***
*** Code by CodingFerret      ***
*** github.com/go-zoo         ***
*********************************/

package bone

import (
	"net/http"
	"net/url"
	"sort"
	"strings"
)

// Route content the required information for a valid route
// Path: is the Route URL
// Size: is the length of the path
// Token: is the value of each part of the path, split by /
// Pattern: is content information about the route, if it's have a route variable
// handler: is the handler who handle this route
// Method: define HTTP method on the route
type Route struct {
	Path    string
	Size    int
	Token   Token
	Params  bool
	Pattern map[int]Pattern
	Handler http.Handler
	Method  string
}

// Token content all value of a spliting route path
// Tokens: string value of each token
// size: number of token
type Token struct {
	Tokens []string
	Size   int
}

type byLength []*Route

func (b byLength) Len() int {
	return len(b)
}

func (b byLength) Swap(i int, j int) {
	b[i], b[j] = b[j], b[i]
}

func (b byLength) Less(i int, j int) bool {
	return b[i].Token.Size < b[j].Token.Size
}

func (b byLength) Sort() {
	sort.Sort(b)
}

// Pattern content the required information for the route Pattern
// Exist: check if a variable was declare on the route
// ID: the name of the variable
// Pos: postition of var in the route path
// Value: is the value of the request parameters
type Pattern struct {
	ID  string
	Pos int
}

// NewRoute return a pointer to a Route instance and call save() on it
func NewRoute(url string, h http.Handler) *Route {
	r := &Route{Path: url, Handler: h}
	r.save()
	return r
}

// Save, set automaticly the the Route.Size and Route.Pattern value
func (r *Route) save() {
	r.Size = len(r.Path)
	r.Token.Tokens = strings.Split(r.Path, "/")
	r.Pattern = make(map[int]Pattern)

	for i, s := range r.Token.Tokens {
		if len(s) >= 1 {
			if s[:1] == ":" {
				r.Pattern[i] = Pattern{
					ID:  s[1:],
					Pos: i,
				}
				r.Params = true
				r.Handler = clean(r.Handler)
			}
		}
		r.Token.Size++
	}
}

// Clean request value from the vars stack
func clean(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		next.ServeHTTP(rw, req)
		delete(vars, req)
	})
}

// Match check if the request match the route Pattern
func (r *Route) Match(path string) (url.Values, bool) {
	ss := strings.Split(path, "/")
	uV := url.Values{}
	exists := false

	if len(ss) == r.Token.Size && ss[r.Token.Size-1] != "" {
		for k, _ := range r.Pattern {
			if r.Path[:r.Pattern[k].Pos] == path[:r.Pattern[k].Pos] {
				uV.Add(r.Pattern[k].ID, ss[r.Pattern[k].Pos])
				exists = true
			}
		}
	}

	return uV, exists
}

// Get set the route method to Get
func (r *Route) Get() *Route {
	r.Method = "GET"
	return r
}

// Post set the route method to Post
func (r *Route) Post() *Route {
	r.Method = "POST"
	return r
}

// Put set the route method to Put
func (r *Route) Put() *Route {
	r.Method = "PUT"
	return r
}

// Delete set the route method to Delete
func (r *Route) Delete() *Route {
	r.Method = "DELETE"
	return r
}

// Head set the route method to Head
func (r *Route) Head() *Route {
	r.Method = "HEAD"
	return r
}

// Patch set the route method to Patch
func (r *Route) Patch() *Route {
	r.Method = "PATCH"
	return r
}

// Options set the route method to Options
func (r *Route) Options() *Route {
	r.Method = "OPTIONS"
	return r
}
<commit_msg>Add ServeHttp method to bone.Route<commit_after>/********************************
*** Multiplexer for Go        ***
*** Bone is under MIT license ***
*** Code by CodingFerret      ***
*** github.com/go-zoo         ***
*********************************/

package bone

import (
	"net/http"
	"net/url"
	"sort"
	"strings"
)

// Route content the required information for a valid route
// Path: is the Route URL
// Size: is the length of the path
// Token: is the value of each part of the path, split by /
// Pattern: is content information about the route, if it's have a route variable
// handler: is the handler who handle this route
// Method: define HTTP method on the route
type Route struct {
	Path    string
	Size    int
	Token   Token
	Params  bool
	Pattern map[int]Pattern
	Handler http.Handler
	Method  string
}

// Token content all value of a spliting route path
// Tokens: string value of each token
// size: number of token
type Token struct {
	Tokens []string
	Size   int
}

type byLength []*Route

func (b byLength) Len() int {
	return len(b)
}

func (b byLength) Swap(i int, j int) {
	b[i], b[j] = b[j], b[i]
}

func (b byLength) Less(i int, j int) bool {
	return b[i].Token.Size < b[j].Token.Size
}

func (b byLength) Sort() {
	sort.Sort(b)
}

// Pattern content the required information for the route Pattern
// Exist: check if a variable was declare on the route
// ID: the name of the variable
// Pos: postition of var in the route path
// Value: is the value of the request parameters
type Pattern struct {
	ID  string
	Pos int
}

// NewRoute return a pointer to a Route instance and call save() on it
func NewRoute(url string, h http.Handler) *Route {
	r := &Route{Path: url, Handler: h}
	r.save()
	return r
}

// Save, set automaticly the the Route.Size and Route.Pattern value
func (r *Route) save() {
	r.Size = len(r.Path)
	r.Token.Tokens = strings.Split(r.Path, "/")
	r.Pattern = make(map[int]Pattern)

	for i, s := range r.Token.Tokens {
		if len(s) >= 1 {
			if s[:1] == ":" {
				r.Pattern[i] = Pattern{
					ID:  s[1:],
					Pos: i,
				}
				r.Params = true
				r.Handler = clean(r.Handler)
			}
		}
		r.Token.Size++
	}
}

// Clean request value from the vars stack
func clean(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		next.ServeHTTP(rw, req)
		delete(vars, req)
	})
}

// Match check if the request match the route Pattern
func (r *Route) Match(path string) (url.Values, bool) {
	ss := strings.Split(path, "/")
	uV := url.Values{}
	exists := false

	if len(ss) == r.Token.Size && ss[r.Token.Size-1] != "" {
		for k, _ := range r.Pattern {
			if r.Path[:r.Pattern[k].Pos] == path[:r.Pattern[k].Pos] {
				uV.Add(r.Pattern[k].ID, ss[r.Pattern[k].Pos])
				exists = true
			}
		}
	}

	return uV, exists
}

// Get set the route method to Get
func (r *Route) Get() *Route {
	r.Method = "GET"
	return r
}

// Post set the route method to Post
func (r *Route) Post() *Route {
	r.Method = "POST"
	return r
}

// Put set the route method to Put
func (r *Route) Put() *Route {
	r.Method = "PUT"
	return r
}

// Delete set the route method to Delete
func (r *Route) Delete() *Route {
	r.Method = "DELETE"
	return r
}

// Head set the route method to Head
func (r *Route) Head() *Route {
	r.Method = "HEAD"
	return r
}

// Patch set the route method to Patch
func (r *Route) Patch() *Route {
	r.Method = "PATCH"
	return r
}

// Options set the route method to Options
func (r *Route) Options() *Route {
	r.Method = "OPTIONS"
	return r
}

func (r *Route) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if r.Method != "" {
		if req.Method == r.Method {
			r.Handler.ServeHTTP(rw, req)
			return
		}
		http.NotFound(rw, req)
		return
	}
	r.Handler.ServeHTTP(rw, req)
}
<|endoftext|>


<commit_before>package jsonschema2go

import (
	"encoding/json"
	"fmt"
	"go/format"
	"net/http"
	"path/filepath"
	"reflect"
	"sort"
	"strconv"
	"strings"
)

type (
	// Note that all members are backed by pointers, so that nil value can signify non-existence.
	// Otherwise we could not differentiate whether a zero value is non-existence or actually the
	// zero value. For example, if a bool is false, we don't know if it was explictly set to false
	// in the json we read, or whether it was not given. Unmarshaling into a pointer means pointer
	// will be nil pointer if it wasn't read, or a pointer to true/false if it was read from json.
	JsonSubSchema struct {
		AdditionalItems      *bool                 `json:"additionalItems"`
		AdditionalProperties *AdditionalProperties `json:"additionalProperties"`
		AllOf                Items                 `json:"allOf"`
		AnyOf                Items                 `json:"anyOf"`
		Default              interface{}           `json:"default"`
		Description          *string               `json:"description"`
		Enum                 interface{}           `json:"enum"`
		Format               *string               `json:"format"`
		ID                   *string               `json:"id"`
		Items                *JsonSubSchema        `json:"items"`
		Maximum              *int                  `json:"maximum"`
		MaxLength            *int                  `json:"maxLength"`
		Minimum              *int                  `json:"minimum"`
		MinLength            *int                  `json:"minLength"`
		OneOf                Items                 `json:"oneOf"`
		Pattern              *string               `json:"pattern"`
		Properties           *Properties           `json:"properties"`
		Ref                  *string               `json:"$ref"`
		Required             []string              `json:"required"`
		Schema               *string               `json:"$schema"`
		Title                *string               `json:"title"`
		Type                 *string               `json:"type"`

		// non-json fields used for sorting/tracking
		TypeName     string
		SourceURL    string
		RefSubSchema *JsonSubSchema
	}

	Items []JsonSubSchema

	Properties struct {
		Properties          map[string]*JsonSubSchema
		SortedPropertyNames []string
	}

	AdditionalProperties struct {
		Boolean    *bool
		Properties *JsonSubSchema
	}
)

func (subSchema JsonSubSchema) String() string {
	result := ""
	result += describe("Additional Items", subSchema.AdditionalItems)
	result += describe("Additional Properties", subSchema.AdditionalProperties)
	result += describe("All Of", subSchema.AllOf)
	result += describe("Any Of", subSchema.AnyOf)
	result += describe("Default", subSchema.Default)
	result += describe("Description", subSchema.Description)
	result += describe("Enum", subSchema.Enum)
	result += describe("Format", subSchema.Format)
	result += describe("ID", subSchema.ID)
	result += describeList("Items", subSchema.Items)
	result += describe("Maximum", subSchema.Maximum)
	result += describe("MaxLength", subSchema.MaxLength)
	result += describe("Minimum", subSchema.Minimum)
	result += describe("MinLength", subSchema.MinLength)
	result += describeList("OneOf", subSchema.OneOf)
	result += describe("Pattern", subSchema.Pattern)
	result += describeList("Properties", subSchema.Properties)
	result += describe("Ref", subSchema.Ref)
	result += describe("Required", subSchema.Required)
	result += describe("Schema", subSchema.Schema)
	result += describe("Title", subSchema.Title)
	result += describe("Type", subSchema.Type)
	result += describe("TypeName", &subSchema.TypeName)
	result += describe("SourceURL", &subSchema.SourceURL)
	return result
}

func (jsonSubSchema *JsonSubSchema) TypeDefinition(withComments bool, extraPackages map[string]bool) (string, map[string]bool) {
	content := ""
	comment := ""
	if withComments {
		content += "\n"
		if d := jsonSubSchema.Description; d != nil {
			if desc := *d; desc != "" {
				comment = Indent(desc, "// ")
			}
			if len(comment) >= 1 && comment[len(comment)-1:] != "\n" {
				comment += "\n"
			}
		}
		if url := jsonSubSchema.SourceURL; url != "" {
			comment += "//\n// See " + url + "\n"
		}
		content += comment
		content += jsonSubSchema.TypeName + " "
	}
	typ := "json.RawMessage"
	if p := jsonSubSchema.Type; p != nil {
		typ = *p
	}
	if p := jsonSubSchema.RefSubSchema; p != nil {
		typ = p.TypeName
	}
	switch typ {
	case "array":
		if jsonType := jsonSubSchema.Items.Type; jsonType != nil {
			var newType string
			newType, extraPackages = jsonSubSchema.Items.TypeDefinition(false, extraPackages)
			typ = "[]" + newType
		}
	case "object":
		if s := jsonSubSchema.Properties; s != nil {
			typ = fmt.Sprintf("struct {\n")
			members := make(map[string]bool, len(s.SortedPropertyNames))
			for _, j := range s.SortedPropertyNames {
				memberName := Normalise(j, members)
				// recursive call to build structs inside structs
				var subType string
				subType, extraPackages = s.Properties[j].TypeDefinition(false, extraPackages)
				// comment the struct member with the description from the json
				comment = ""
				if d := s.Properties[j].Description; d != nil {
					comment = Indent(*d, "\t// ")
				}
				if len(comment) >= 1 && comment[len(comment)-1:] != "\n" {
					comment += "\n"
				}
				typ += comment
				// struct member name and type, as part of struct definition
				typ += fmt.Sprintf("\t%v %v `json:\"%v\"`\n", memberName, subType, j)
			}
			typ += "}"
		} else {
			typ = "map[string]json.RawMessage"
		}
	case "number":
		typ = "int"
	case "integer":
		typ = "int"
	case "boolean":
		typ = "bool"
	// json type string maps to go type string, so only need to test case of when
	// string is a json date-time, so we can convert to go type time.Time...
	case "string":
		if f := jsonSubSchema.Format; f != nil {
			if *f == "date-time" {
				typ = "time.Time"
			}
		}
	}
	switch typ {
	case "time.Time":
		extraPackages["time"] = true
	case "json.RawMessage":
		extraPackages["encoding/json"] = true
	case "map[string]json.RawMessage":
		extraPackages["encoding/json"] = true
	}
	content += typ
	if withComments {
		content += "\n"
	}
	return content, extraPackages
}

func (p Properties) String() string {
	result := ""
	for _, i := range p.SortedPropertyNames {
		result += "Property '" + i + "' =\n" + Indent(p.Properties[i].String(), "  ")
	}
	return result
}

func (p *Properties) postPopulate(schemaSet map[string]*JsonSubSchema) {
	// now all data should be loaded, let's sort the p.Properties
	if p.Properties != nil {
		p.SortedPropertyNames = make([]string, 0, len(p.Properties))
		for propertyName := range p.Properties {
			p.SortedPropertyNames = append(p.SortedPropertyNames, propertyName)
			// subschemas also need to be triggered to postPopulate...
			p.Properties[propertyName].postPopulate(schemaSet)
		}
		sort.Strings(p.SortedPropertyNames)
	}
}

func (p *Properties) UnmarshalJSON(bytes []byte) (err error) {
	errX := json.Unmarshal(bytes, &p.Properties)
	return errX
}

func (aP *AdditionalProperties) UnmarshalJSON(bytes []byte) (err error) {
	b, p := new(bool), new(JsonSubSchema)
	if err = json.Unmarshal(bytes, b); err == nil {
		aP.Boolean = b
		return
	}
	if err = json.Unmarshal(bytes, p); err == nil {
		aP.Properties = p
	}
	return
}

func (aP AdditionalProperties) String() string {
	if aP.Boolean != nil {
		return strconv.FormatBool(*aP.Boolean)
	}
	return aP.Properties.String()
}

func (items Items) String() string {
	result := ""
	for i, j := range items {
		result += fmt.Sprintf("Item '%v' =\n", i) + Indent(j.String(), "  ")
	}
	return result
}

func (items Items) postPopulate(schemaSet map[string]*JsonSubSchema) {
	for i := range items {
		items[i].postPopulate(schemaSet)
	}
}

func describeList(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%v\n", name) + Indent(fmt.Sprintf("%v", reflect.Indirect(reflect.ValueOf(value)).Interface()), "  ")
		}
	}
	return ""
}

// If item is not null, then return a description of it. If it is a pointer, dereference it first.
func describe(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%-22v = '%v'\n", name, reflect.Indirect(reflect.ValueOf(value)).Interface())
		}
	}
	return ""
}

type CanPopulate interface {
	postPopulate(map[string]*JsonSubSchema)
}

func postPopulateIfNotNil(canPopulate CanPopulate, schemaSet map[string]*JsonSubSchema) {
	if reflect.ValueOf(canPopulate).IsValid() {
		if !reflect.ValueOf(canPopulate).IsNil() {
			canPopulate.postPopulate(schemaSet)
		}
	}
}

func (subSchema *JsonSubSchema) postPopulate(schemaSet map[string]*JsonSubSchema) {
	postPopulateIfNotNil(subSchema.AllOf, schemaSet)
	postPopulateIfNotNil(subSchema.AnyOf, schemaSet)
	postPopulateIfNotNil(subSchema.OneOf, schemaSet)
	postPopulateIfNotNil(subSchema.Items, schemaSet)
	postPopulateIfNotNil(subSchema.Properties, schemaSet)
	// If we have a $ref pointing to another schema, keep a reference so we can
	// discover TypeName later when we generate the type definition
	if subSchema.Ref != nil {
		subSchema.RefSubSchema = cacheJsonSchema(schemaSet, *subSchema.Ref)
	}
}

func loadJsonSchema(schemaSet map[string]*JsonSubSchema, url string) *JsonSubSchema {
	var resp *http.Response
	resp, err := http.Get(url)
	ExitOnFail(err)
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	m := new(JsonSubSchema)
	err = decoder.Decode(m)
	ExitOnFail(err)
	m.postPopulate(schemaSet)
	return m
}

func cacheJsonSchema(schemaSet map[string]*JsonSubSchema, url string) *JsonSubSchema {
	// workaround for problem where some urls don't end with a #
	if (url)[len(url)-1:] != "#" {
		url += "#"
	}
	// only fetch if we haven't fetched already...
	if _, ok := schemaSet[url]; !ok {
		schemaSet[url] = loadJsonSchema(schemaSet, url)
		schemaSet[url].SourceURL = url
	}
	return schemaSet[url]
}

// This is where we generate nested and compoound types in go to represent json payloads
// which are used as inputs and outputs for the REST API endpoints, and also for Pulse
// message bodies for the Exchange APIs.
// Returns the generated code content, and a map of keys of extra packages to import, e.g.
// a generated type might use time.Time, so if not imported, this would have to be added.
// using a map of strings -> bool to simulate a set - true => include
func generateGoTypes(schemaSet map[string]*JsonSubSchema) (string, map[string]bool) {
	extraPackages := make(map[string]bool)
	content := "type (" // intentionally no \n here since each type starts with one already
	// Loop through all json schemas that were found referenced inside the API json schemas...
	for _, i := range schemaSet {
		var newContent string
		newContent, extraPackages = i.TypeDefinition(true, extraPackages)
		content += Indent(newContent, "\t")
	}
	return content + ")\n\n", extraPackages
}

func URLsToFile(filename string, urls ...string) (string, error) {
	// calculate parent dir name of target file, since this will be the package
	// name of the generated code...
	absPath, err := filepath.Abs(filename)
	if err != nil {
		return "", err
	}
	parentDirName := filepath.Base(filepath.Dir(absPath))
	packageName := parentDirName
	if strings.ContainsRune(packageName, '-') {
		packageName = "main"
	}

	// Generate normalised names for schemas. Keep a record of generated type
	// names, so that we don't reuse old names. map[string]bool acts like a set
	// of strings.
	TypeName := make(map[string]bool)

	allSchemas := make(map[string]*JsonSubSchema)
	for _, url := range urls {
		schema := cacheJsonSchema(allSchemas, url)
		if schema.Title != nil {
			schema.TypeName = Normalise(*schema.Title, TypeName)
		} else {
			schema.TypeName = Normalise("var", TypeName)
		}
	}
	types, extraPackages := generateGoTypes(allSchemas)
	content := `// The following code is AUTO-GENERATED. Please DO NOT edit.

package ` + packageName + `

`
	extraPackagesContent := ""
	for j, k := range extraPackages {
		if k {
			extraPackagesContent += Indent("\""+j+"\"", "\t")
		}
	}

	if extraPackagesContent != "" {
		content += `import (
` + extraPackagesContent + `
)

`
	}
	content += types
	// format it
	bytes, err := format.Source([]byte(content))
	if err != nil {
		return "", err
	}
	WriteStringToFile(bytes, filename)
	return absPath, nil
}
<commit_msg>Bug fix where extraPackages would all appear on a single line - now one per line in the import block<commit_after>package jsonschema2go

import (
	"encoding/json"
	"fmt"
	"go/format"
	"net/http"
	"path/filepath"
	"reflect"
	"sort"
	"strconv"
	"strings"
)

type (
	// Note that all members are backed by pointers, so that nil value can signify non-existence.
	// Otherwise we could not differentiate whether a zero value is non-existence or actually the
	// zero value. For example, if a bool is false, we don't know if it was explictly set to false
	// in the json we read, or whether it was not given. Unmarshaling into a pointer means pointer
	// will be nil pointer if it wasn't read, or a pointer to true/false if it was read from json.
	JsonSubSchema struct {
		AdditionalItems      *bool                 `json:"additionalItems"`
		AdditionalProperties *AdditionalProperties `json:"additionalProperties"`
		AllOf                Items                 `json:"allOf"`
		AnyOf                Items                 `json:"anyOf"`
		Default              interface{}           `json:"default"`
		Description          *string               `json:"description"`
		Enum                 interface{}           `json:"enum"`
		Format               *string               `json:"format"`
		ID                   *string               `json:"id"`
		Items                *JsonSubSchema        `json:"items"`
		Maximum              *int                  `json:"maximum"`
		MaxLength            *int                  `json:"maxLength"`
		Minimum              *int                  `json:"minimum"`
		MinLength            *int                  `json:"minLength"`
		OneOf                Items                 `json:"oneOf"`
		Pattern              *string               `json:"pattern"`
		Properties           *Properties           `json:"properties"`
		Ref                  *string               `json:"$ref"`
		Required             []string              `json:"required"`
		Schema               *string               `json:"$schema"`
		Title                *string               `json:"title"`
		Type                 *string               `json:"type"`

		// non-json fields used for sorting/tracking
		TypeName     string
		SourceURL    string
		RefSubSchema *JsonSubSchema
	}

	Items []JsonSubSchema

	Properties struct {
		Properties          map[string]*JsonSubSchema
		SortedPropertyNames []string
	}

	AdditionalProperties struct {
		Boolean    *bool
		Properties *JsonSubSchema
	}
)

func (subSchema JsonSubSchema) String() string {
	result := ""
	result += describe("Additional Items", subSchema.AdditionalItems)
	result += describe("Additional Properties", subSchema.AdditionalProperties)
	result += describe("All Of", subSchema.AllOf)
	result += describe("Any Of", subSchema.AnyOf)
	result += describe("Default", subSchema.Default)
	result += describe("Description", subSchema.Description)
	result += describe("Enum", subSchema.Enum)
	result += describe("Format", subSchema.Format)
	result += describe("ID", subSchema.ID)
	result += describeList("Items", subSchema.Items)
	result += describe("Maximum", subSchema.Maximum)
	result += describe("MaxLength", subSchema.MaxLength)
	result += describe("Minimum", subSchema.Minimum)
	result += describe("MinLength", subSchema.MinLength)
	result += describeList("OneOf", subSchema.OneOf)
	result += describe("Pattern", subSchema.Pattern)
	result += describeList("Properties", subSchema.Properties)
	result += describe("Ref", subSchema.Ref)
	result += describe("Required", subSchema.Required)
	result += describe("Schema", subSchema.Schema)
	result += describe("Title", subSchema.Title)
	result += describe("Type", subSchema.Type)
	result += describe("TypeName", &subSchema.TypeName)
	result += describe("SourceURL", &subSchema.SourceURL)
	return result
}

func (jsonSubSchema *JsonSubSchema) TypeDefinition(withComments bool, extraPackages map[string]bool) (string, map[string]bool) {
	content := ""
	comment := ""
	if withComments {
		content += "\n"
		if d := jsonSubSchema.Description; d != nil {
			if desc := *d; desc != "" {
				comment = Indent(desc, "// ")
			}
			if len(comment) >= 1 && comment[len(comment)-1:] != "\n" {
				comment += "\n"
			}
		}
		if url := jsonSubSchema.SourceURL; url != "" {
			comment += "//\n// See " + url + "\n"
		}
		content += comment
		content += jsonSubSchema.TypeName + " "
	}
	typ := "json.RawMessage"
	if p := jsonSubSchema.Type; p != nil {
		typ = *p
	}
	if p := jsonSubSchema.RefSubSchema; p != nil {
		typ = p.TypeName
	}
	switch typ {
	case "array":
		if jsonType := jsonSubSchema.Items.Type; jsonType != nil {
			var newType string
			newType, extraPackages = jsonSubSchema.Items.TypeDefinition(false, extraPackages)
			typ = "[]" + newType
		}
	case "object":
		if s := jsonSubSchema.Properties; s != nil {
			typ = fmt.Sprintf("struct {\n")
			members := make(map[string]bool, len(s.SortedPropertyNames))
			for _, j := range s.SortedPropertyNames {
				memberName := Normalise(j, members)
				// recursive call to build structs inside structs
				var subType string
				subType, extraPackages = s.Properties[j].TypeDefinition(false, extraPackages)
				// comment the struct member with the description from the json
				comment = ""
				if d := s.Properties[j].Description; d != nil {
					comment = Indent(*d, "\t// ")
				}
				if len(comment) >= 1 && comment[len(comment)-1:] != "\n" {
					comment += "\n"
				}
				typ += comment
				// struct member name and type, as part of struct definition
				typ += fmt.Sprintf("\t%v %v `json:\"%v\"`\n", memberName, subType, j)
			}
			typ += "}"
		} else {
			typ = "map[string]json.RawMessage"
		}
	case "number":
		typ = "int"
	case "integer":
		typ = "int"
	case "boolean":
		typ = "bool"
	// json type string maps to go type string, so only need to test case of when
	// string is a json date-time, so we can convert to go type time.Time...
	case "string":
		if f := jsonSubSchema.Format; f != nil {
			if *f == "date-time" {
				typ = "time.Time"
			}
		}
	}
	switch typ {
	case "time.Time":
		extraPackages["time"] = true
	case "json.RawMessage":
		extraPackages["encoding/json"] = true
	case "map[string]json.RawMessage":
		extraPackages["encoding/json"] = true
	}
	content += typ
	if withComments {
		content += "\n"
	}
	return content, extraPackages
}

func (p Properties) String() string {
	result := ""
	for _, i := range p.SortedPropertyNames {
		result += "Property '" + i + "' =\n" + Indent(p.Properties[i].String(), "  ")
	}
	return result
}

func (p *Properties) postPopulate(schemaSet map[string]*JsonSubSchema) {
	// now all data should be loaded, let's sort the p.Properties
	if p.Properties != nil {
		p.SortedPropertyNames = make([]string, 0, len(p.Properties))
		for propertyName := range p.Properties {
			p.SortedPropertyNames = append(p.SortedPropertyNames, propertyName)
			// subschemas also need to be triggered to postPopulate...
			p.Properties[propertyName].postPopulate(schemaSet)
		}
		sort.Strings(p.SortedPropertyNames)
	}
}

func (p *Properties) UnmarshalJSON(bytes []byte) (err error) {
	errX := json.Unmarshal(bytes, &p.Properties)
	return errX
}

func (aP *AdditionalProperties) UnmarshalJSON(bytes []byte) (err error) {
	b, p := new(bool), new(JsonSubSchema)
	if err = json.Unmarshal(bytes, b); err == nil {
		aP.Boolean = b
		return
	}
	if err = json.Unmarshal(bytes, p); err == nil {
		aP.Properties = p
	}
	return
}

func (aP AdditionalProperties) String() string {
	if aP.Boolean != nil {
		return strconv.FormatBool(*aP.Boolean)
	}
	return aP.Properties.String()
}

func (items Items) String() string {
	result := ""
	for i, j := range items {
		result += fmt.Sprintf("Item '%v' =\n", i) + Indent(j.String(), "  ")
	}
	return result
}

func (items Items) postPopulate(schemaSet map[string]*JsonSubSchema) {
	for i := range items {
		items[i].postPopulate(schemaSet)
	}
}

func describeList(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%v\n", name) + Indent(fmt.Sprintf("%v", reflect.Indirect(reflect.ValueOf(value)).Interface()), "  ")
		}
	}
	return ""
}

// If item is not null, then return a description of it. If it is a pointer, dereference it first.
func describe(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%-22v = '%v'\n", name, reflect.Indirect(reflect.ValueOf(value)).Interface())
		}
	}
	return ""
}

type CanPopulate interface {
	postPopulate(map[string]*JsonSubSchema)
}

func postPopulateIfNotNil(canPopulate CanPopulate, schemaSet map[string]*JsonSubSchema) {
	if reflect.ValueOf(canPopulate).IsValid() {
		if !reflect.ValueOf(canPopulate).IsNil() {
			canPopulate.postPopulate(schemaSet)
		}
	}
}

func (subSchema *JsonSubSchema) postPopulate(schemaSet map[string]*JsonSubSchema) {
	postPopulateIfNotNil(subSchema.AllOf, schemaSet)
	postPopulateIfNotNil(subSchema.AnyOf, schemaSet)
	postPopulateIfNotNil(subSchema.OneOf, schemaSet)
	postPopulateIfNotNil(subSchema.Items, schemaSet)
	postPopulateIfNotNil(subSchema.Properties, schemaSet)
	// If we have a $ref pointing to another schema, keep a reference so we can
	// discover TypeName later when we generate the type definition
	if subSchema.Ref != nil {
		subSchema.RefSubSchema = cacheJsonSchema(schemaSet, *subSchema.Ref)
	}
}

func loadJsonSchema(schemaSet map[string]*JsonSubSchema, url string) *JsonSubSchema {
	var resp *http.Response
	resp, err := http.Get(url)
	ExitOnFail(err)
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	m := new(JsonSubSchema)
	err = decoder.Decode(m)
	ExitOnFail(err)
	m.postPopulate(schemaSet)
	return m
}

func cacheJsonSchema(schemaSet map[string]*JsonSubSchema, url string) *JsonSubSchema {
	// workaround for problem where some urls don't end with a #
	if (url)[len(url)-1:] != "#" {
		url += "#"
	}
	// only fetch if we haven't fetched already...
	if _, ok := schemaSet[url]; !ok {
		schemaSet[url] = loadJsonSchema(schemaSet, url)
		schemaSet[url].SourceURL = url
	}
	return schemaSet[url]
}

// This is where we generate nested and compoound types in go to represent json payloads
// which are used as inputs and outputs for the REST API endpoints, and also for Pulse
// message bodies for the Exchange APIs.
// Returns the generated code content, and a map of keys of extra packages to import, e.g.
// a generated type might use time.Time, so if not imported, this would have to be added.
// using a map of strings -> bool to simulate a set - true => include
func generateGoTypes(schemaSet map[string]*JsonSubSchema) (string, map[string]bool) {
	extraPackages := make(map[string]bool)
	content := "type (" // intentionally no \n here since each type starts with one already
	// Loop through all json schemas that were found referenced inside the API json schemas...
	for _, i := range schemaSet {
		var newContent string
		newContent, extraPackages = i.TypeDefinition(true, extraPackages)
		content += Indent(newContent, "\t")
	}
	return content + ")\n\n", extraPackages
}

func URLsToFile(filename string, urls ...string) (string, error) {
	// calculate parent dir name of target file, since this will be the package
	// name of the generated code...
	absPath, err := filepath.Abs(filename)
	if err != nil {
		return "", err
	}
	parentDirName := filepath.Base(filepath.Dir(absPath))
	packageName := parentDirName
	if strings.ContainsRune(packageName, '-') {
		packageName = "main"
	}

	// Generate normalised names for schemas. Keep a record of generated type
	// names, so that we don't reuse old names. map[string]bool acts like a set
	// of strings.
	TypeName := make(map[string]bool)

	allSchemas := make(map[string]*JsonSubSchema)
	for _, url := range urls {
		schema := cacheJsonSchema(allSchemas, url)
		if schema.Title != nil {
			schema.TypeName = Normalise(*schema.Title, TypeName)
		} else {
			schema.TypeName = Normalise("var", TypeName)
		}
	}
	types, extraPackages := generateGoTypes(allSchemas)
	content := `// The following code is AUTO-GENERATED. Please DO NOT edit.

package ` + packageName + `

`
	extraPackagesContent := ""
	for j, k := range extraPackages {
		if k {
			extraPackagesContent += Indent("\""+j+"\"\n", "\t")
		}
	}

	if extraPackagesContent != "" {
		content += `import (
` + extraPackagesContent + `)

`
	}
	content += types
	// format it
	bytes, err := format.Source([]byte(content))
	if err != nil {
		return "", err
	}
	WriteStringToFile(bytes, filename)
	return absPath, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"path"
	"strings"

	"github.com/daaku/go.httpgzip"
)

type Region struct {
	Situation   string `json:"situation"`
	Observation string `json:"observation"`
	WindAndSea  string `json:"ventEtMer"`
	Waves       string `json:"houle"`
	Visibility  string `json:"visi"`
	Confidence  string `json:"indice"`
}

type Echeance struct {
	Title string    `json:"titreEcheance"`
	Areas []*Region `json:"region"`
}

type Bulletin struct {
	Title     string      `json:"titreBulletin"`
	Special   string      `json:"bulletinSpecial"`
	Echeances []*Echeance `json:"echeance"`
}

func formatReport(b *Bulletin) string {
	buf := &bytes.Buffer{}
	w := func(format string, args ...interface{}) {
		fmt.Fprintf(buf, format, args...)
	}
	wif := func(s string) {
		if s != "" {
			s = strings.Replace(s, "
", "\n", -1)
			w("%s\n", s)
		}
	}
	w("%s\n", b.Title)
	w("Bulletin spécial: %s\n", b.Special)
	w("\n")
	for _, e := range b.Echeances {
		w("%s\n", e.Title)
		for _, r := range e.Areas {
			wif(r.Situation)
			wif(r.Observation)
			parts := strings.Split(r.WindAndSea, "MER :")
			if len(parts) == 2 {
				wif(parts[0])
				wif("MER :" + parts[1])
			} else {
				wif(r.WindAndSea)
			}
			wif(r.Waves)
			wif(r.Visibility)
			wif(r.Confidence)
		}
		w("\n")
	}
	return buf.String()
}

func hashReport(report string) string {
	h := sha256.Sum256([]byte(report))
	return hex.EncodeToString(h[:])
}

func getJson(url string, output interface{}) error {
	rsp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("could not get %s: %s", url, err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		return fmt.Errorf("failed to get %s: %s", url, rsp.Status)
	}
	err = json.NewDecoder(rsp.Body).Decode(output)
	if err != nil {
		return fmt.Errorf("could not decode json response for %s: %s", url, err)
	}
	return nil
}

func fetchWeather(id string) (string, error) {
	url := "http://www.meteofrance.com/mf3-rpc-portlet/rest/bulletins/cote/" + id +
		"/bulletinsMarineMetropole"
	reports := []*Bulletin{}
	err := getJson(url, &reports)
	if err != nil {
		return "", err
	}
	if len(reports) <= 0 {
		return "", fmt.Errorf("no report retrieved")
	}
	return formatReport(reports[0]), nil
}

func formatJsonWeather(w http.ResponseWriter, req *http.Request) {
	id := path.Base(req.URL.Path)
	report, err := fetchWeather(id)
	w.Header().Set("Content-Type", "text/plain;charset=utf-8")
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "error: %s\n", err)
		return
	}
	h := hashReport(report)
	w.Header().Set("ETag", h)
	etag := req.Header.Get("If-None-Match")
	if etag == h {
		w.WriteHeader(304)
		return
	}
	fmt.Fprintf(w, "%s", report)
}

type CoastalArea struct {
	Id  string
	URL string
}

func fetchAreas() ([]CoastalArea, error) {
	type Area struct {
		Id  string `json:"id"`
		URL string `json:"url"`
	}

	type AreaGroup struct {
		Name  string `json:"name"`
		Areas []Area `json:"zones"`
	}

	url := "http://www.meteofrance.com/mf3-rpc-portlet/js/datas/bulletins_marine.json"
	groups := map[string][]AreaGroup{}
	err := getJson(url, &groups)
	if err != nil {
		return nil, err
	}
	group, ok := groups["bulletinsMarineMetropole"]
	if !ok {
		return nil, fmt.Errorf("cannot extract metropole areas")
	}
	areas := []CoastalArea{}
	for _, g := range group {
		if g.Name != "cote" {
			continue
		}
		for _, a := range g.Areas {
			areas = append(areas, CoastalArea{
				Id:  a.Id,
				URL: a.URL,
			})
		}
	}
	return areas, nil
}

const (
	html = `

	{{range .}}
		{{.Name}}

	{{end}}

`
)

func formatAreas(t *template.Template, areas []CoastalArea) (string, error) {
	type Area struct {
		URL  string
		Name string
	}
	data := []Area{}
	for _, area := range areas {
		data = append(data, Area{
			URL:  "areas/" + area.Id,
			Name: area.URL,
		})
	}
	w := &bytes.Buffer{}
	err := t.Execute(w, data)
	if err != nil {
		return "", err
	}
	return w.String(), nil
}

func renderAreas(t *template.Template) (string, error) {
	areas, err := fetchAreas()
	if err != nil {
		return "", err
	}
	return formatAreas(t, areas)
}

func formatJsonAreas(t *template.Template, w http.ResponseWriter, req *http.Request) {
	areas, err := renderAreas(t)
	if err != nil {
		w.Header().Set("Content-Type", "text/plain;charset=utf-8")
		w.WriteHeader(500)
		fmt.Fprintf(w, "error: %s\n", err)
		return
	}
	w.Header().Set("Content-Type", "text/html;charset=utf-8")
	h := hashReport(areas)
	w.Header().Set("ETag", h)
	etag := req.Header.Get("If-None-Match")
	if etag == h {
		w.WriteHeader(304)
		return
	}
	fmt.Fprintf(w, "%s", areas)
}

var (
	serveCmd    = app.Command("serve", "reformat forecasts and serve them over HTTP")
	servePrefix = serveCmd.Flag("prefix", "public URL prefix").String()
	serveHttp   = serveCmd.Flag("http", "HTTP host:port").Default(":5000").String()
)

func serveFn() error {
	prefix := *servePrefix
	addr := *serveHttp
	t, err := template.New("areas").Parse(html)
	if err != nil {
		return err
	}
	mux := http.NewServeMux()
	mux.HandleFunc(prefix+"/", func(w http.ResponseWriter, req *http.Request) {
		formatJsonAreas(t, w, req)
	})
	mux.HandleFunc(prefix+"/areas/", formatJsonWeather)
	return http.ListenAndServe(addr, httpgzip.NewHandler(mux))
}
<commit_msg>serve: remove spurious trailing blanks from section fields<commit_after>package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"path"
	"strings"

	"github.com/daaku/go.httpgzip"
)

type Region struct {
	Situation   string `json:"situation"`
	Observation string `json:"observation"`
	WindAndSea  string `json:"ventEtMer"`
	Waves       string `json:"houle"`
	Visibility  string `json:"visi"`
	Confidence  string `json:"indice"`
}

type Echeance struct {
	Title string    `json:"titreEcheance"`
	Areas []*Region `json:"region"`
}

type Bulletin struct {
	Title     string      `json:"titreBulletin"`
	Special   string      `json:"bulletinSpecial"`
	Echeances []*Echeance `json:"echeance"`
}

func formatReport(b *Bulletin) string {
	buf := &bytes.Buffer{}
	w := func(format string, args ...interface{}) {
		fmt.Fprintf(buf, format, args...)
	}
	wif := func(s string) {
		s = strings.TrimSpace(s)
		if s != "" {
			s = strings.Replace(s, "
", "\n", -1)
			w("%s\n", s)
		}
	}
	w("%s\n", b.Title)
	w("Bulletin spécial: %s\n", b.Special)
	w("\n")
	for _, e := range b.Echeances {
		w("%s\n", e.Title)
		for _, r := range e.Areas {
			wif(r.Situation)
			wif(r.Observation)
			parts := strings.Split(r.WindAndSea, "MER :")
			if len(parts) == 2 {
				wif(parts[0])
				wif("MER :" + parts[1])
			} else {
				wif(r.WindAndSea)
			}
			wif(r.Waves)
			wif(r.Visibility)
			wif(r.Confidence)
		}
		w("\n")
	}
	return buf.String()
}

func hashReport(report string) string {
	h := sha256.Sum256([]byte(report))
	return hex.EncodeToString(h[:])
}

func getJson(url string, output interface{}) error {
	rsp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("could not get %s: %s", url, err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		return fmt.Errorf("failed to get %s: %s", url, rsp.Status)
	}
	err = json.NewDecoder(rsp.Body).Decode(output)
	if err != nil {
		return fmt.Errorf("could not decode json response for %s: %s", url, err)
	}
	return nil
}

func fetchWeather(id string) (string, error) {
	url := "http://www.meteofrance.com/mf3-rpc-portlet/rest/bulletins/cote/" + id +
		"/bulletinsMarineMetropole"
	reports := []*Bulletin{}
	err := getJson(url, &reports)
	if err != nil {
		return "", err
	}
	if len(reports) <= 0 {
		return "", fmt.Errorf("no report retrieved")
	}
	return formatReport(reports[0]), nil
}

func formatJsonWeather(w http.ResponseWriter, req *http.Request) {
	id := path.Base(req.URL.Path)
	report, err := fetchWeather(id)
	w.Header().Set("Content-Type", "text/plain;charset=utf-8")
	if err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, "error: %s\n", err)
		return
	}
	h := hashReport(report)
	w.Header().Set("ETag", h)
	etag := req.Header.Get("If-None-Match")
	if etag == h {
		w.WriteHeader(304)
		return
	}
	fmt.Fprintf(w, "%s", report)
}

type CoastalArea struct {
	Id  string
	URL string
}

func fetchAreas() ([]CoastalArea, error) {
	type Area struct {
		Id  string `json:"id"`
		URL string `json:"url"`
	}

	type AreaGroup struct {
		Name  string `json:"name"`
		Areas []Area `json:"zones"`
	}

	url := "http://www.meteofrance.com/mf3-rpc-portlet/js/datas/bulletins_marine.json"
	groups := map[string][]AreaGroup{}
	err := getJson(url, &groups)
	if err != nil {
		return nil, err
	}
	group, ok := groups["bulletinsMarineMetropole"]
	if !ok {
		return nil, fmt.Errorf("cannot extract metropole areas")
	}
	areas := []CoastalArea{}
	for _, g := range group {
		if g.Name != "cote" {
			continue
		}
		for _, a := range g.Areas {
			areas = append(areas, CoastalArea{
				Id:  a.Id,
				URL: a.URL,
			})
		}
	}
	return areas, nil
}

const (
	html = `

	{{range .}}
		{{.Name}}

	{{end}}

`
)

func formatAreas(t *template.Template, areas []CoastalArea) (string, error) {
	type Area struct {
		URL  string
		Name string
	}
	data := []Area{}
	for _, area := range areas {
		data = append(data, Area{
			URL:  "areas/" + area.Id,
			Name: area.URL,
		})
	}
	w := &bytes.Buffer{}
	err := t.Execute(w, data)
	if err != nil {
		return "", err
	}
	return w.String(), nil
}

func renderAreas(t *template.Template) (string, error) {
	areas, err := fetchAreas()
	if err != nil {
		return "", err
	}
	return formatAreas(t, areas)
}

func formatJsonAreas(t *template.Template, w http.ResponseWriter, req *http.Request) {
	areas, err := renderAreas(t)
	if err != nil {
		w.Header().Set("Content-Type", "text/plain;charset=utf-8")
		w.WriteHeader(500)
		fmt.Fprintf(w, "error: %s\n", err)
		return
	}
	w.Header().Set("Content-Type", "text/html;charset=utf-8")
	h := hashReport(areas)
	w.Header().Set("ETag", h)
	etag := req.Header.Get("If-None-Match")
	if etag == h {
		w.WriteHeader(304)
		return
	}
	fmt.Fprintf(w, "%s", areas)
}

var (
	serveCmd    = app.Command("serve", "reformat forecasts and serve them over HTTP")
	servePrefix = serveCmd.Flag("prefix", "public URL prefix").String()
	serveHttp   = serveCmd.Flag("http", "HTTP host:port").Default(":5000").String()
)

func serveFn() error {
	prefix := *servePrefix
	addr := *serveHttp
	t, err := template.New("areas").Parse(html)
	if err != nil {
		return err
	}
	mux := http.NewServeMux()
	mux.HandleFunc(prefix+"/", func(w http.ResponseWriter, req *http.Request) {
		formatJsonAreas(t, w, req)
	})
	mux.HandleFunc(prefix+"/areas/", formatJsonWeather)
	return http.ListenAndServe(addr, httpgzip.NewHandler(mux))
}
<|endoftext|>


<commit_before>package upframe

import (
	"path/filepath"

	"github.com/gorilla/sessions"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/upframe/fest/email"
	"github.com/upframe/fest/models"
	"github.com/upframe/fest/pages"
)

var (
	// Store stores the session cookies and help us to handle them
	store *sessions.CookieStore
	// BaseAddress is the base URL to build URLs
	BaseAddress string
	// RootPath is the 'root' directive defined in Caddyfile
	RootPath string
	// TemplatesPath is where the templates are stored
	TemplatesPath string
)

func init() {
	// Generates 5 random key pairs to secure the cookies
	// NOTE: generating this at startup will automatically log out the
	// users when the server is rebooted
	/* keyPairs := [][]byte{}
	   for i := 0; i < 5; i++ {
	       keyPairs = append(keyPairs, make([]byte, 32))
	       _, err := io.ReadFull(rand.Reader, keyPairs[i])
	       if err != nil {
	           log.Fatal(err)
	       }
	   } */

	// TODO: Comment this in production
	keyPairs := [][]byte{[]byte("HEY")}

	// Creates the new cookie session;
	store = sessions.NewCookieStore(keyPairs...)
	store.Options = &sessions.Options{
		Path:     "/",
		MaxAge:   3600 * 3, // 3 hours
		Secure:   false,    // TODO: Change this to secure in production
		HttpOnly: true,
	}

	// Regists the caddy middleware
	caddy.RegisterPlugin("fest", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

func setup(c *caddy.Controller) error {
	// Gets the base address
	cfg := httpserver.GetConfig(c)
	store.Options.Domain = cfg.Host()

	pages.BaseAddress = cfg.Addr.String()
	pages.Templates = filepath.Clean(cfg.Root+"/templates/") + "/"

	var (
		smtpUser, smtpPass, smtpHost, smtpPort string
		dbUser, dbPass, dbHost, dbPort, dbName string
	)

	dbPort = "3306"

	// Gets the options from the Caddyfile
	for c.Next() {
		for c.NextBlock() {
			switch c.Val() {
			case "db_user":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbUser = c.Val()
			case "db_pass":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbPass = c.Val()
			case "db_host":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbHost = c.Val()
			case "db_port":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbPort = c.Val()
			case "db_name":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbName = c.Val()
			case "smtp_user":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpUser = c.Val()
			case "smtp_pass":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpPass = c.Val()
			case "smtp_host":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpHost = c.Val()
			case "smtp_port":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpPort = c.Val()
			}
		}
	}

	// Configures the email
	email.InitSMTP(smtpUser, smtpPass, smtpHost, smtpPort)

	// Connects to the database and checks for an error
	err := models.InitDB(dbUser, dbPass, dbHost, dbPort, dbName)
	if err != nil {
		return err
	}

	// Adds the middleware to Caddy
	mid := func(next httpserver.Handler) httpserver.Handler {
		return Upframe{
			Next: next,
			Root: cfg.Root,
		}
	}

	httpserver.GetConfig(c).AddMiddleware(mid)
	return nil
}
<commit_msg>spring cleaning<commit_after>package upframe

import (
	"crypto/rand"
	"io"
	"log"
	"path/filepath"

	"github.com/gorilla/sessions"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/upframe/fest/email"
	"github.com/upframe/fest/models"
	"github.com/upframe/fest/pages"
)

// TODO: CHANGE THIS IN PRODUCTION
const development = true

var (
	// Store stores the session cookies and help us to handle them
	store *sessions.CookieStore
	// BaseAddress is the base URL to build URLs
	BaseAddress string
	// RootPath is the 'root' directive defined in Caddyfile
	RootPath string
	// TemplatesPath is where the templates are stored
	TemplatesPath string
)

func init() {
	var keyPairs [][]byte

	if !development {
		// Generates 5 random key pairs to secure the cookies
		// NOTE: generating this at startup will automatically log out the
		// users when the server is rebooted
		keyPairs = [][]byte{}
		for i := 0; i < 5; i++ {
			keyPairs = append(keyPairs, make([]byte, 32))
			_, err := io.ReadFull(rand.Reader, keyPairs[i])
			if err != nil {
				log.Fatal(err)
			}
		}
	} else {
		keyPairs = [][]byte{[]byte("HEY")}
	}

	// Creates the new cookie session;
	store = sessions.NewCookieStore(keyPairs...)
	store.Options = &sessions.Options{
		Path:     "/",
		MaxAge:   3600 * 3,
		Secure:   !development,
		HttpOnly: true,
	}

	// Regists the caddy middleware
	caddy.RegisterPlugin("fest", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

func setup(c *caddy.Controller) error {
	// Gets the base address
	cfg := httpserver.GetConfig(c)
	store.Options.Domain = cfg.Host()

	pages.BaseAddress = cfg.Addr.String()
	pages.Templates = filepath.Clean(cfg.Root+"/templates/") + "/"

	var (
		smtpUser, smtpPass, smtpHost, smtpPort string
		dbUser, dbPass, dbHost, dbPort, dbName string
	)

	dbPort = "3306"

	// Gets the options from the Caddyfile
	for c.Next() {
		for c.NextBlock() {
			switch c.Val() {
			case "db_user":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbUser = c.Val()
			case "db_pass":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbPass = c.Val()
			case "db_host":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbHost = c.Val()
			case "db_port":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbPort = c.Val()
			case "db_name":
				if !c.NextArg() {
					return c.ArgErr()
				}

				dbName = c.Val()
			case "smtp_user":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpUser = c.Val()
			case "smtp_pass":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpPass = c.Val()
			case "smtp_host":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpHost = c.Val()
			case "smtp_port":
				if !c.NextArg() {
					return c.ArgErr()
				}

				smtpPort = c.Val()
			}
		}
	}

	// Configures the email
	email.InitSMTP(smtpUser, smtpPass, smtpHost, smtpPort)

	// Connects to the database and checks for an error
	err := models.InitDB(dbUser, dbPass, dbHost, dbPort, dbName)
	if err != nil {
		return err
	}

	// Adds the middleware to Caddy
	mid := func(next httpserver.Handler) httpserver.Handler {
		return Upframe{
			Next: next,
			Root: cfg.Root,
		}
	}

	httpserver.GetConfig(c).AddMiddleware(mid)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin 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 gcs implements a storage backend that saves data to Google Cloud Storage.
package gcs

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	gContext "golang.org/x/net/context"

	"golang.org/x/oauth2/google"
	gcsBE "google.golang.org/api/storage/v1"

	"upspin.io/cloud/storage"
	"upspin.io/errors"
	"upspin.io/log"
)

const (
	scope = gcsBE.DevstorageFullControlScope
)

// These constants define ACLs for writing data to Google Cloud Store.
// Definitions according to https://github.com/google/google-api-go-client/blob/master/storage/v1/storage-gen.go:
//   "publicReadWrite" - Project team owners get OWNER access, and
//       allUsers get WRITER access.
const (
	// PublicRead means project team owners get owner access and all users get reader access.
	PublicRead = "publicRead"
	// Private means project team owners get owner access.
	Private = "private"
	// ProjectPrivate means project team members get access according to their roles.
	ProjectPrivate = "projectPrivate"
	// BucketOwnerFullCtrl means the object owner gets owner access and project team owners get owner access.
	BucketOwnerFullCtrl = "bucketOwnerFullControl"
)

// Keys used for storing dial options.
const (
	projectID  = "gcpProjectId"
	bucketName = "gcpBucketName"
	defaultACL = "defaultACL"
)

// gcsImpl is an implementation of Storage that connects to a Google Cloud Storage (GCS) backend.
type gcsImpl struct {
	client          *http.Client
	service         *gcsBE.Service
	projectID       string
	bucketName      string
	defaultWriteACL string
}

// Guarantee we implement the Storage interface.
var _ storage.Storage = (*gcsImpl)(nil)

// PutLocalFile implements Storage.
func (gcs *gcsImpl) PutLocalFile(srcLocalFilename string, ref string) (refLink string, error error) {
	// Insert an object into a bucket.
	object := &gcsBE.Object{Name: ref}
	file, err := os.Open(srcLocalFilename)
	if err != nil {
		log.Printf("Error opening: %v", err)
		return "", err
	}
	defer file.Close()
	acl := string(gcs.defaultWriteACL)
	res, err := gcs.service.Objects.Insert(gcs.bucketName, object).Media(file).PredefinedAcl(acl).Do()
	if err == nil {
		log.Debug.Printf("Created object %v at location %v", res.Name, res.SelfLink)
	} else {
		log.Error.Printf("Objects.Insert failed: %v", err)
		return "", err
	}
	return res.MediaLink, err
}

// Get implements Storage.
func (gcs *gcsImpl) Get(ref string) (link string, error error) {
	// Get the link of the blob
	res, err := gcs.service.Objects.Get(gcs.bucketName, ref).Do()
	if err != nil {
		return "", err
	}
	log.Debug.Printf("The media download link for %v/%v is %v.", gcs.bucketName, res.Name, res.MediaLink)
	return res.MediaLink, nil
}

// Download implements Storage.
func (gcs *gcsImpl) Download(ref string) ([]byte, error) {
	resp, err := gcs.service.Objects.Get(gcs.bucketName, ref).Download()
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return buf, nil
}

// Put implements Storage.
func (gcs *gcsImpl) Put(ref string, contents []byte) (refLink string, error error) {
	buf := bytes.NewBuffer(contents)
	acl := string(gcs.defaultWriteACL)
	object := &gcsBE.Object{Name: ref}
	res, err := gcs.service.Objects.Insert(gcs.bucketName, object).Media(buf).PredefinedAcl(acl).Do()
	if err == nil {
		log.Debug.Printf("Created object %v at location %v", res.Name, res.SelfLink)
	} else {
		log.Error.Printf("Objects.Insert failed: %v", err)
		return "", err
	}
	return res.MediaLink, err
}

// ListPrefilx implements Storage.
func (gcs *gcsImpl) ListPrefix(prefix string, depth int) ([]string, error) {
	var names []string
	pageToken := ""
	prefixDepth := strings.Count(prefix, "/")
	for {
		objs, err := gcs.service.Objects.List(gcs.bucketName).Prefix(prefix).Fields("items(name),nextPageToken").PageToken(pageToken).Do()
		if err != nil {
			return nil, err
		}
		for _, o := range objs.Items {
			// Only append o.Name if it doesn't violate depth.
			objDepth := strings.Count(o.Name, "/")
			netDepth := objDepth - prefixDepth
			if netDepth < 0 {
				log.Error.Printf("WARN: Negative depth should never happen.")
				continue
			}
			if netDepth <= depth {
				names = append(names, o.Name)
			}
		}
		if objs.NextPageToken == "" {
			break
		}
		pageToken = objs.NextPageToken
	}
	return names, nil
}

// ListDir implements Storage.
func (gcs *gcsImpl) ListDir(dir string) ([]string, error) {
	var names []string
	pageToken := ""
	for {
		objs, err := gcs.service.Objects.List(gcs.bucketName).Prefix(dir).Delimiter("/").Fields("items(name),nextPageToken").PageToken(pageToken).Do()
		if err != nil {
			return nil, err
		}
		for _, o := range objs.Items {
			names = append(names, o.Name)
		}
		if objs.NextPageToken == "" {
			break
		}
		pageToken = objs.NextPageToken
	}
	return names, nil
}

// Delete implements Storage.
func (gcs *gcsImpl) Delete(ref string) error {
	err := gcs.service.Objects.Delete(gcs.bucketName, ref).Do()
	if err != nil {
		return err
	}
	return nil
}

// EmptyBucket completely removes all files in a bucket permanently.
// If verbose is true, every attempt to delete a file is logged to the standard logger.
// This is an expensive operation. It is also dangerous, so use with care.
// Exported, but not part of the GCP interface. Use for testing only.
func (gcs *gcsImpl) EmptyBucket(verbose bool) error {
	const maxParallelDeletes = 10
	pageToken := ""
	var firstErr error
	recordErr := func(err error) bool {
		if err == nil {
			return false
		}
		if firstErr == nil {
			firstErr = err
		}
		return true
	}
	for {
		objs, err := gcs.service.Objects.List(gcs.bucketName).MaxResults(maxParallelDeletes).Fields("items(name),nextPageToken").PageToken(pageToken).Do()
		if recordErr(err) {
			log.Error.Printf("EmptyBucket: List(%q): %v", gcs.bucketName, err)
			break
		}
		for _, o := range objs.Items {
			if verbose {
				log.Debug.Printf("Deleting: %q", o.Name)
			}
			err = gcs.Delete(o.Name)
			if recordErr(err) {
				log.Error.Printf("EmptyBucket: Delete(%q): %v", o.Name, err)
				continue
			}
		}
		if objs.NextPageToken == "" {
			break
		}
		pageToken = objs.NextPageToken
	}
	return firstErr
}

// Dial implements storage.Storage.
func (gcs *gcsImpl) Dial(opts *storage.Opts) error {
	const op = "cloud/storage/gcs.Dial"

	if v, ok := opts.Opts[projectID]; ok {
		gcs.projectID = v
	} else {
		return errors.E(op, errors.Invalid, errors.Str("Project ID argument is required"))
	}
	if v, ok := opts.Opts[bucketName]; ok {
		gcs.bucketName = v
	} else {
		return errors.E(op, errors.Invalid, errors.Str("Bucket name argument is required"))
	}
	if v, ok := opts.Opts[defaultACL]; ok {
		gcs.defaultWriteACL = v
	} else {
		return errors.E(op, errors.Invalid, errors.Str("Default ACL argument is required"))
	}

	// Authentication is provided by the gcloud tool when running locally, and
	// by the associated service account when running on Compute Engine.
	client, err := google.DefaultClient(gContext.Background(), scope)
	if err != nil {
		return errors.E(op, errors.IO, errors.Errorf("Unable to get default client: %s", err))
	}
	service, err := gcsBE.New(client)
	if err != nil {
		errors.E(op, errors.IO, errors.Errorf("Unable to create storage service: %s", err))
	}
	// Initialize the object
	gcs.client = client
	gcs.service = service
	return nil
}

// Close implements Storage.
func (gcs *gcsImpl) Close() {
	// Not much to do, the GCS interface is pretty stateless (HTTP client).
	gcs.client = nil
	gcs.service = nil
}

func init() {
	storage.Register("GCS", &gcsImpl{})
}
<commit_msg>cloud/storage/gcs: return NotExist for GCP error 404<commit_after>// Copyright 2016 The Upspin 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 gcs implements a storage backend that saves data to Google Cloud Storage.
package gcs

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	gContext "golang.org/x/net/context"

	"golang.org/x/oauth2/google"
	"google.golang.org/api/googleapi"
	gcsBE "google.golang.org/api/storage/v1"

	"upspin.io/cloud/storage"
	"upspin.io/errors"
	"upspin.io/log"
)

const (
	scope = gcsBE.DevstorageFullControlScope
)

// These constants define ACLs for writing data to Google Cloud Store.
// Definitions according to https://github.com/google/google-api-go-client/blob/master/storage/v1/storage-gen.go:
//   "publicReadWrite" - Project team owners get OWNER access, and
//       allUsers get WRITER access.
const (
	// PublicRead means project team owners get owner access and all users get reader access.
	PublicRead = "publicRead"
	// Private means project team owners get owner access.
	Private = "private"
	// ProjectPrivate means project team members get access according to their roles.
	ProjectPrivate = "projectPrivate"
	// BucketOwnerFullCtrl means the object owner gets owner access and project team owners get owner access.
	BucketOwnerFullCtrl = "bucketOwnerFullControl"
)

// Keys used for storing dial options.
const (
	projectID  = "gcpProjectId"
	bucketName = "gcpBucketName"
	defaultACL = "defaultACL"
)

// gcsImpl is an implementation of Storage that connects to a Google Cloud Storage (GCS) backend.
type gcsImpl struct {
	client          *http.Client
	service         *gcsBE.Service
	projectID       string
	bucketName      string
	defaultWriteACL string
}

// Guarantee we implement the Storage interface.
var _ storage.Storage = (*gcsImpl)(nil)

// PutLocalFile implements Storage.
func (gcs *gcsImpl) PutLocalFile(srcLocalFilename string, ref string) (refLink string, error error) {
	// Insert an object into a bucket.
	object := &gcsBE.Object{Name: ref}
	file, err := os.Open(srcLocalFilename)
	if err != nil {
		log.Printf("Error opening: %v", err)
		return "", err
	}
	defer file.Close()
	acl := string(gcs.defaultWriteACL)
	res, err := gcs.service.Objects.Insert(gcs.bucketName, object).Media(file).PredefinedAcl(acl).Do()
	if err == nil {
		log.Debug.Printf("Created object %v at location %v", res.Name, res.SelfLink)
	} else {
		log.Error.Printf("Objects.Insert failed: %v", err)
		return "", err
	}
	return res.MediaLink, err
}

// Get implements Storage.
func (gcs *gcsImpl) Get(ref string) (link string, error error) {
	const op = "cloud/storage/gcs.Get"
	// Get the link of the blob
	res, err := gcs.service.Objects.Get(gcs.bucketName, ref).Do()
	if err != nil {
		if gcsErr, ok := err.(*googleapi.Error); ok && gcsErr.Code == 404 {
			return "", errors.E(op, errors.NotExist, err)
		}
		return "", err
	}
	log.Debug.Printf("The media download link for %v/%v is %v.", gcs.bucketName, res.Name, res.MediaLink)
	return res.MediaLink, nil
}

// Download implements Storage.
func (gcs *gcsImpl) Download(ref string) ([]byte, error) {
	const op = "cloud/storage/gcs.Download"
	resp, err := gcs.service.Objects.Get(gcs.bucketName, ref).Download()
	if err != nil {
		if gcsErr, ok := err.(*googleapi.Error); ok && gcsErr.Code == 404 {
			return nil, errors.E(op, errors.NotExist, err)
		}
		return nil, err
	}
	defer resp.Body.Close()
	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return buf, nil
}

// Put implements Storage.
func (gcs *gcsImpl) Put(ref string, contents []byte) (refLink string, error error) {
	buf := bytes.NewBuffer(contents)
	acl := string(gcs.defaultWriteACL)
	object := &gcsBE.Object{Name: ref}
	res, err := gcs.service.Objects.Insert(gcs.bucketName, object).Media(buf).PredefinedAcl(acl).Do()
	if err == nil {
		log.Debug.Printf("Created object %v at location %v", res.Name, res.SelfLink)
	} else {
		log.Error.Printf("Objects.Insert failed: %v", err)
		return "", err
	}
	return res.MediaLink, err
}

// ListPrefilx implements Storage.
func (gcs *gcsImpl) ListPrefix(prefix string, depth int) ([]string, error) {
	var names []string
	pageToken := ""
	prefixDepth := strings.Count(prefix, "/")
	for {
		objs, err := gcs.service.Objects.List(gcs.bucketName).Prefix(prefix).Fields("items(name),nextPageToken").PageToken(pageToken).Do()
		if err != nil {
			return nil, err
		}
		for _, o := range objs.Items {
			// Only append o.Name if it doesn't violate depth.
			objDepth := strings.Count(o.Name, "/")
			netDepth := objDepth - prefixDepth
			if netDepth < 0 {
				log.Error.Printf("WARN: Negative depth should never happen.")
				continue
			}
			if netDepth <= depth {
				names = append(names, o.Name)
			}
		}
		if objs.NextPageToken == "" {
			break
		}
		pageToken = objs.NextPageToken
	}
	return names, nil
}

// ListDir implements Storage.
func (gcs *gcsImpl) ListDir(dir string) ([]string, error) {
	var names []string
	pageToken := ""
	for {
		objs, err := gcs.service.Objects.List(gcs.bucketName).Prefix(dir).Delimiter("/").Fields("items(name),nextPageToken").PageToken(pageToken).Do()
		if err != nil {
			return nil, err
		}
		for _, o := range objs.Items {
			names = append(names, o.Name)
		}
		if objs.NextPageToken == "" {
			break
		}
		pageToken = objs.NextPageToken
	}
	return names, nil
}

// Delete implements Storage.
func (gcs *gcsImpl) Delete(ref string) error {
	err := gcs.service.Objects.Delete(gcs.bucketName, ref).Do()
	if err != nil {
		return err
	}
	return nil
}

// EmptyBucket completely removes all files in a bucket permanently.
// If verbose is true, every attempt to delete a file is logged to the standard logger.
// This is an expensive operation. It is also dangerous, so use with care.
// Exported, but not part of the GCP interface. Use for testing only.
func (gcs *gcsImpl) EmptyBucket(verbose bool) error {
	const maxParallelDeletes = 10
	pageToken := ""
	var firstErr error
	recordErr := func(err error) bool {
		if err == nil {
			return false
		}
		if firstErr == nil {
			firstErr = err
		}
		return true
	}
	for {
		objs, err := gcs.service.Objects.List(gcs.bucketName).MaxResults(maxParallelDeletes).Fields("items(name),nextPageToken").PageToken(pageToken).Do()
		if recordErr(err) {
			log.Error.Printf("EmptyBucket: List(%q): %v", gcs.bucketName, err)
			break
		}
		for _, o := range objs.Items {
			if verbose {
				log.Debug.Printf("Deleting: %q", o.Name)
			}
			err = gcs.Delete(o.Name)
			if recordErr(err) {
				log.Error.Printf("EmptyBucket: Delete(%q): %v", o.Name, err)
				continue
			}
		}
		if objs.NextPageToken == "" {
			break
		}
		pageToken = objs.NextPageToken
	}
	return firstErr
}

// Dial implements storage.Storage.
func (gcs *gcsImpl) Dial(opts *storage.Opts) error {
	const op = "cloud/storage/gcs.Dial"

	if v, ok := opts.Opts[projectID]; ok {
		gcs.projectID = v
	} else {
		return errors.E(op, errors.Invalid, errors.Str("Project ID argument is required"))
	}
	if v, ok := opts.Opts[bucketName]; ok {
		gcs.bucketName = v
	} else {
		return errors.E(op, errors.Invalid, errors.Str("Bucket name argument is required"))
	}
	if v, ok := opts.Opts[defaultACL]; ok {
		gcs.defaultWriteACL = v
	} else {
		return errors.E(op, errors.Invalid, errors.Str("Default ACL argument is required"))
	}

	// Authentication is provided by the gcloud tool when running locally, and
	// by the associated service account when running on Compute Engine.
	client, err := google.DefaultClient(gContext.Background(), scope)
	if err != nil {
		return errors.E(op, errors.IO, errors.Errorf("Unable to get default client: %s", err))
	}
	service, err := gcsBE.New(client)
	if err != nil {
		errors.E(op, errors.IO, errors.Errorf("Unable to create storage service: %s", err))
	}
	// Initialize the object
	gcs.client = client
	gcs.service = service
	return nil
}

// Close implements Storage.
func (gcs *gcsImpl) Close() {
	// Not much to do, the GCS interface is pretty stateless (HTTP client).
	gcs.client = nil
	gcs.service = nil
}

func init() {
	storage.Register("GCS", &gcsImpl{})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/kayac/Gunfish"
)

func main() {
	var (
		confFile string
	)
	flag.StringVar(&confFile, "c", "./test/gunfish_test.toml", "config file")
	flag.Parse()

	config, err := gunfish.LoadConfig(confFile)
	if err != nil {
		return
	}
	gunfish.StartAPNSMockServer(config)
}
<commit_msg>modify apnsmock.go as to use apns sub-package<commit_after>package main

import (
	"flag"

	"github.com/kayac/Gunfish"
	"github.com/kayac/Gunfish/apns"
)

func main() {
	var (
		confFile string
	)
	flag.StringVar(&confFile, "c", "./test/gunfish_test.toml", "config file")
	flag.Parse()

	config, err := gunfish.LoadConfig(confFile)
	if err != nil {
		return
	}
	apns.StartAPNSMockServer(config.Apns.CertFile, config.Apns.KeyFile)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Frank Braun <frank@cryptogroup.net>
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// ccprices prints current currency prices in ledger format.
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

const (
	euroAPI  = "http://api.fixer.io/latest"
	xauAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/GOLD.json?limit=1"
	xagAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/SILVER.json?limit=1"
	coinsAPI = "http://coinmarketcap.northpole.ro/api/v5/all.json"
)

var (
	// Quandl API key can be set via environment variable QUANDL_API_KEY
	quandl = os.Getenv("QUANDL_API_KEY")
	coins  = []string{
		"Bitcoin",
		"Decred",
		"Ethereum",
		"Ethereum Classic",
		"Litecoin",
		"Monero",
		"Namecoin",
		"Zcash",
	}
)

type result struct {
	symbol string
	price  float64
}

func httpGetWithWarning(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		warning(fmt.Sprintf("GET %s: %s", url, resp.Status))
		return nil, nil
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return b, err
}

func getEuroExchangeRates() (map[string]interface{}, error) {
	b, err := httpGetWithWarning(euroAPI)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn["rates"].(map[string]interface{}), nil
}

func getLBMAPrice(api string, dataIndex int) (float64, error) {
	if quandl != "" {
		api += "?api_key=" + quandl
	}
	b, err := httpGetWithWarning(api)
	if err != nil {
		return 0, err
	}
	if b == nil {
		return 0, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return 0, err
	}
	data := jsn["dataset"].(map[string]interface{})["data"].([]interface{})
	var price float64
	if data[0].([]interface{})[dataIndex] != nil {
		// p.m. price is available
		price = data[0].([]interface{})[dataIndex].(float64)
	} else {
		// p.m. price is not available, use a.m. price instead
		price = data[0].([]interface{})[dataIndex-1].(float64)
	}
	return price, nil
}

func getCoinPrices() ([]interface{}, error) {
	b, err := httpGetWithWarning(coinsAPI)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn["markets"].([]interface{}), nil
}

func warning(warn string) {
	fmt.Fprintf(os.Stderr, "%s: warning: %s\n", os.Args[0], warn)
}

func fatal(err error) {
	fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err)
	os.Exit(1)
}

func main() {
	// get euro exchange rates
	rates, err := getEuroExchangeRates()
	if err != nil {
		fatal(err)
	}
	// get gold price
	xau, err := getLBMAPrice(xauAPI, 6)
	if err != nil {
		fatal(err)
	}
	// get silver price
	xag, err := getLBMAPrice(xagAPI, 3)
	if err != nil {
		fatal(err)
	}
	// get all coin prices
	all, err := getCoinPrices()
	if err != nil {
		fatal(err)
	}
	// construct map of coin names we are interested in
	var (
		names  map[string]struct{}
		prices map[string]*result
	)
	if all != nil {
		names = make(map[string]struct{})
		for _, name := range coins {
			names[name] = struct{}{}
		}
		prices = make(map[string]*result)
		// iterate over all coin informations
		for _, info := range all {
			coin := info.(map[string]interface{})
			name := coin["name"].(string)
			_, ok := names[name]
			if ok {
				// we are interested in this coin -> store price and symbol
				f := coin["price"].(map[string]interface{})["eur"].(string)
				p, err := strconv.ParseFloat(f, 64)
				if err != nil {
					fatal(err)
				}
				prices[name] = &result{symbol: coin["symbol"].(string), price: p}
			}
		}
	}
	// output all prices
	t := time.Now().Format("2006/01/02 15:04:05")
	if rates != nil {
		fmt.Printf("P %s USD %11.6f EUR\n", t, 1/rates["USD"].(float64))
		fmt.Printf("P %s GBP %11.6f EUR\n", t, 1/rates["GBP"].(float64))
		fmt.Printf("P %s CHF %11.6f EUR\n", t, 1/rates["CHF"].(float64))
		fmt.Printf("P %s CZK %11.6f EUR\n", t, 1/rates["CZK"].(float64))
		fmt.Printf("P %s THB %11.6f EUR\n", t, 1/rates["THB"].(float64))
	}
	if xau != 0 {
		fmt.Printf("P %s XAU %11.6f EUR\n", t, xau)
	}
	if xag != 0 {
		fmt.Printf("P %s XAG %11.6f EUR\n", t, xag)
	}
	if all != nil {
		for _, name := range coins {
			fmt.Printf("P %s %s %11.6f EUR\n", t, prices[name].symbol,
				prices[name].price)
		}
	}
}
<commit_msg>ccprices: use different API for coinmarketcap<commit_after>// Copyright (c) 2016 Frank Braun <frank@cryptogroup.net>
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// ccprices prints current currency prices in ledger format.
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

const (
	euroAPI  = "http://api.fixer.io/latest"
	xauAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/GOLD.json?limit=1"
	xagAPI   = "https://www.quandl.com/api/v3/datasets/LBMA/SILVER.json?limit=1"
	coinsAPI = "https://api.coinmarketcap.com/v1/ticker/?convert=EUR"
)

var (
	// Quandl API key can be set via environment variable QUANDL_API_KEY
	quandl = os.Getenv("QUANDL_API_KEY")
	coins  = []string{
		"Bitcoin",
		"Decred",
		"Ethereum",
		"Ethereum Classic",
		"Litecoin",
		"Monero",
		"Namecoin",
		"Zcash",
	}
)

type result struct {
	symbol string
	price  float64
}

func httpGetWithWarning(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		warning(fmt.Sprintf("GET %s: %s", url, resp.Status))
		return nil, nil
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return b, err
}

func getEuroExchangeRates() (map[string]interface{}, error) {
	b, err := httpGetWithWarning(euroAPI)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn["rates"].(map[string]interface{}), nil
}

func getLBMAPrice(api string, dataIndex int) (float64, error) {
	if quandl != "" {
		api += "?api_key=" + quandl
	}
	b, err := httpGetWithWarning(api)
	if err != nil {
		return 0, err
	}
	if b == nil {
		return 0, nil
	}
	jsn := make(map[string]interface{})
	if err := json.Unmarshal(b, &jsn); err != nil {
		return 0, err
	}
	data := jsn["dataset"].(map[string]interface{})["data"].([]interface{})
	var price float64
	if data[0].([]interface{})[dataIndex] != nil {
		// p.m. price is available
		price = data[0].([]interface{})[dataIndex].(float64)
	} else {
		// p.m. price is not available, use a.m. price instead
		price = data[0].([]interface{})[dataIndex-1].(float64)
	}
	return price, nil
}

func getCoinPrices() ([]interface{}, error) {
	b, err := httpGetWithWarning(coinsAPI)
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, nil
	}
	jsn := make([]interface{}, 0)
	if err := json.Unmarshal(b, &jsn); err != nil {
		return nil, err
	}
	return jsn, nil
}

func warning(warn string) {
	fmt.Fprintf(os.Stderr, "%s: warning: %s\n", os.Args[0], warn)
}

func fatal(err error) {
	fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err)
	os.Exit(1)
}

func main() {
	// get euro exchange rates
	rates, err := getEuroExchangeRates()
	if err != nil {
		fatal(err)
	}
	// get gold price
	xau, err := getLBMAPrice(xauAPI, 6)
	if err != nil {
		fatal(err)
	}
	// get silver price
	xag, err := getLBMAPrice(xagAPI, 3)
	if err != nil {
		fatal(err)
	}
	// get all coin prices
	all, err := getCoinPrices()
	if err != nil {
		fatal(err)
	}
	// construct map of coin names we are interested in
	var (
		names  map[string]struct{}
		prices map[string]*result
	)
	if all != nil {
		names = make(map[string]struct{})
		for _, name := range coins {
			names[name] = struct{}{}
		}
		prices = make(map[string]*result)
		// iterate over all coin informations
		for _, info := range all {
			coin := info.(map[string]interface{})
			name := coin["name"].(string)
			_, ok := names[name]
			if ok {
				// we are interested in this coin -> store price and symbol
				f := coin["price_eur"].(string)
				p, err := strconv.ParseFloat(f, 64)
				if err != nil {
					fatal(err)
				}
				prices[name] = &result{symbol: coin["symbol"].(string), price: p}
			}
		}
	}
	// output all prices
	t := time.Now().Format("2006/01/02 15:04:05")
	if rates != nil {
		fmt.Printf("P %s USD %11.6f EUR\n", t, 1/rates["USD"].(float64))
		fmt.Printf("P %s GBP %11.6f EUR\n", t, 1/rates["GBP"].(float64))
		fmt.Printf("P %s CHF %11.6f EUR\n", t, 1/rates["CHF"].(float64))
		fmt.Printf("P %s CZK %11.6f EUR\n", t, 1/rates["CZK"].(float64))
		fmt.Printf("P %s THB %11.6f EUR\n", t, 1/rates["THB"].(float64))
	}
	if xau != 0 {
		fmt.Printf("P %s XAU %11.6f EUR\n", t, xau)
	}
	if xag != 0 {
		fmt.Printf("P %s XAG %11.6f EUR\n", t, xag)
	}
	if all != nil {
		for _, name := range coins {
			fmt.Printf("P %s %s %11.6f EUR\n", t, prices[name].symbol,
				prices[name].price)
		}
	}
}
<|endoftext|>