<commit_before>package main

import (
    "os"
    "fmt"
    "log"
    "net"
    "mob/proto"
    "time"
    "sync/atomic"
    //"sync"
    "github.com/cenkalti/rpc2"
)

var peerMap map[string][]string
var songQueue []string

var currSong string
var clientsPlaying int64
var doneResponses int64

// TODO: when all clients in peerMap make rpc to say that they are done with the song
// notify the next set of seeders to begin seeding
func main() {
    peerMap   = make(map[string][]string)
    songQueue = make([]string, 0)
    currSong = ""
    clientsPlaying = 0
    doneResponses = 0

    srv := rpc2.NewServer()

    // join the peer network
    srv.Handle("join", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        peerMap[args.Ip] = args.List
        fmt.Println("Accepted a new client: " + args.Ip)
        return nil
    })

    // Return list of songs available to be played
    srv.Handle("list-songs", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        reply.Res = getSongList()
        return nil
    })

    // Return list of peers connected to tracker
    srv.Handle("list-peers", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        keys := make([]string, 0, len(peerMap))
        for k := range peerMap {
            keys = append(keys, k)
        }
        reply.Res = keys
        return nil
    })

    // Enqueue song into song queue
    srv.Handle("play", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
        for _, song := range getSongList() {
            if args.Arg == song {
                songQueue = append(songQueue, args.Arg)
                break
            }
        }

        return nil
    })

    srv.Handle("leave", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        delete(peerMap, args.Ip)
        fmt.Println("Removing client " + args.Ip)
        return nil
    })

    // Contact peers with the song locally to start seeding
    // Clients ask tracker when they can start seeding and when they can start
    // playing the buffered mp3 frames
    // TODO: Synchronization by including a time delay to "start-playing" rpc
    srv.Handle("ping", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        //if clientsPlaying != 0 {
        //    return nil
        //}
        if doneResponses != 0 {
            return nil
        }

        // not playing a song; set currSong if not already set
        if currSong == "" && len(songQueue) > 0 {
            currSong = songQueue[0]
        }

        // Dispatch call to seeder or call to non-seeder
        if currSong != "" {
            //fmt.Println("next song to play is " + currSong)
            // contact source seeders to start seeding
            for _, song := range peerMap[args.Ip] {
                if song == currSong {
                    client.Call("seed", proto.TrackerRes{currSong}, nil)
                    return nil
                }
            }

            //fmt.Println("Why are we getting here!")
            // contact non-source-seeders to listen for mp3 packets
            client.Call("listen-for-mp3", proto.TrackerRes{""}, nil)
        }

        return nil
    })

    // Notify the tracker that the client ready to start playing the song
    srv.Handle("ready-to-play", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
        //fmt.Println("A client is ready to play!")
        atomic.AddInt64(&clientsPlaying, 1)
        // block until everyone is ready
        for clientsPlaying != int64(len(peerMap)) {}
        t := time.Now().Add(10 * time.Second)
        client.Call("start-playing", proto.TimePacket{t}, nil)
        return nil
    })

    // Notify the tracker that the client is done playing the audio for the mp3
    srv.Handle("done-playing", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
        atomic.AddInt64(&clientsPlaying, -1)
        atomic.AddInt64(&doneResponses, 1)
        log.Println("Done response from a client!")
        if clientsPlaying == 0 { // on the last done-playing, we reset the currSong
            //log.Println("Start to play the next song")
            songQueue = append(songQueue[:0], songQueue[1:]...)
            currSong = ""
            doneResponses = 0
        }

        return nil
    })

    ln, err := net.Listen("tcp", ":" + os.Args[1])
    if err != nil {
        log.Println(err)
    }

    ip, ipErr := proto.GetLocalIp()
    if ipErr != nil {
        log.Fatal("Error: not connected to the internet.")
        os.Exit(1)
    }

    fmt.Println("mob tracker listening on: " + ip + ":" + os.Args[1] + " ...")

    for {
        srv.Accept(ln)
    }
}

// TODO: maybe return unique song list
func getSongList() ([]string) {
    var songs []string

    keys := make([]string, 0, len(peerMap))
    for k := range peerMap {
        keys = append(keys, k)
    }

    for i := 0; i < len(keys); i++ {
        songs = append(songs, peerMap[keys[i]]...)
    }

    encountered := map[int]bool{}
    result := []int{}

    for i := range songs {
        if !encountered[songs[i]] {
            encountered[songs[i]] = true
            result = append(result, songs[i])
        }
    }

    return result
}
<commit_msg>remove todo<commit_after>package main

import (
    "os"
    "fmt"
    "log"
    "net"
    "mob/proto"
    "time"
    "sync/atomic"
    //"sync"
    "github.com/cenkalti/rpc2"
)

var peerMap map[string][]string
var songQueue []string

var currSong string
var clientsPlaying int64
var doneResponses int64

// TODO: when all clients in peerMap make rpc to say that they are done with the song
// notify the next set of seeders to begin seeding
func main() {
    peerMap   = make(map[string][]string)
    songQueue = make([]string, 0)
    currSong = ""
    clientsPlaying = 0
    doneResponses = 0

    srv := rpc2.NewServer()

    // join the peer network
    srv.Handle("join", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        peerMap[args.Ip] = args.List
        fmt.Println("Accepted a new client: " + args.Ip)
        return nil
    })

    // Return list of songs available to be played
    srv.Handle("list-songs", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        reply.Res = getSongList()
        return nil
    })

    // Return list of peers connected to tracker
    srv.Handle("list-peers", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerSlice) error {
        keys := make([]string, 0, len(peerMap))
        for k := range peerMap {
            keys = append(keys, k)
        }
        reply.Res = keys
        return nil
    })

    // Enqueue song into song queue
    srv.Handle("play", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
        for _, song := range getSongList() {
            if args.Arg == song {
                songQueue = append(songQueue, args.Arg)
                break
            }
        }

        return nil
    })

    srv.Handle("leave", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        delete(peerMap, args.Ip)
        fmt.Println("Removing client " + args.Ip)
        return nil
    })

    // Contact peers with the song locally to start seeding
    // Clients ask tracker when they can start seeding and when they can start
    // playing the buffered mp3 frames
    // TODO: Synchronization by including a time delay to "start-playing" rpc
    srv.Handle("ping", func(client *rpc2.Client, args *proto.ClientInfoMsg, reply *proto.TrackerRes) error {
        //if clientsPlaying != 0 {
        //    return nil
        //}
        if doneResponses != 0 {
            return nil
        }

        // not playing a song; set currSong if not already set
        if currSong == "" && len(songQueue) > 0 {
            currSong = songQueue[0]
        }

        // Dispatch call to seeder or call to non-seeder
        if currSong != "" {
            //fmt.Println("next song to play is " + currSong)
            // contact source seeders to start seeding
            for _, song := range peerMap[args.Ip] {
                if song == currSong {
                    client.Call("seed", proto.TrackerRes{currSong}, nil)
                    return nil
                }
            }

            //fmt.Println("Why are we getting here!")
            // contact non-source-seeders to listen for mp3 packets
            client.Call("listen-for-mp3", proto.TrackerRes{""}, nil)
        }

        return nil
    })

    // Notify the tracker that the client ready to start playing the song
    srv.Handle("ready-to-play", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
        //fmt.Println("A client is ready to play!")
        atomic.AddInt64(&clientsPlaying, 1)
        // block until everyone is ready
        for clientsPlaying != int64(len(peerMap)) {}
        t := time.Now().Add(10 * time.Second)
        client.Call("start-playing", proto.TimePacket{t}, nil)
        return nil
    })

    // Notify the tracker that the client is done playing the audio for the mp3
    srv.Handle("done-playing", func(client *rpc2.Client, args *proto.ClientCmdMsg, reply *proto.TrackerRes) error {
        atomic.AddInt64(&clientsPlaying, -1)
        atomic.AddInt64(&doneResponses, 1)
        log.Println("Done response from a client!")
        if clientsPlaying == 0 { // on the last done-playing, we reset the currSong
            //log.Println("Start to play the next song")
            songQueue = append(songQueue[:0], songQueue[1:]...)
            currSong = ""
            doneResponses = 0
        }

        return nil
    })

    ln, err := net.Listen("tcp", ":" + os.Args[1])
    if err != nil {
        log.Println(err)
    }

    ip, ipErr := proto.GetLocalIp()
    if ipErr != nil {
        log.Fatal("Error: not connected to the internet.")
        os.Exit(1)
    }

    fmt.Println("mob tracker listening on: " + ip + ":" + os.Args[1] + " ...")

    for {
        srv.Accept(ln)
    }
}

func getSongList() ([]string) {
    var songs []string

    keys := make([]string, 0, len(peerMap))
    for k := range peerMap {
        keys = append(keys, k)
    }

    for i := 0; i < len(keys); i++ {
        songs = append(songs, peerMap[keys[i]]...)
    }

    encountered := map[int]bool{}
    result := []int{}

    for i := range songs {
        if !encountered[songs[i]] {
            encountered[songs[i]] = true
            result = append(result, songs[i])
        }
    }

    return result
}
<|endoftext|>


<commit_before>// Copyright 2016 Stefan Böhmann.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package envconf

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPrepareKey(t *testing.T) {
	assert := assert.New(t)

	assert.Empty(prepareKey(""))
	assert.NotEmpty(prepareKey("ID"))

	assert.Equal("ID", prepareKey("ID"))
	assert.Equal("ID", prepareKey("id"))
	assert.Equal("ID", prepareKey("id "))
	assert.Equal("ID", prepareKey("  id "))

	assert.Equal("FOO_BAR", prepareKey("FOO_BAR"))
	assert.Equal("FOO_BAR", prepareKey("foo_bar"))
	assert.Equal("FOO_BAR", prepareKey("foo_bar "))
	assert.Equal("FOO_BAR", prepareKey("  foo_bar "))
	assert.Equal("FOO_BAR", prepareKey("Foo Bar"))
	assert.Equal("FOO_BAR", prepareKey("  FOO  BAR "))

	assert.NotEqual("Max Mustermann", prepareKey("Max Mustermann"))

	SetPrefix("foo")
	assert.Equal("FOO_FOO_BAR", prepareKey("FOO_BAR"))

	SetPrefix("")
	assert.Equal("FOO_BAR", prepareKey("FOO_BAR"))
}

func TestParseBool(t *testing.T) {
	assert := assert.New(t)

	// Test true
	for _, k := range []string{"", "1", "y", "Y", "true", "True", "TRUE", "yes", "Yes", "YES", "On", "on", "ON"} {
		value, okay := parseBool(k)
		assert.True(value, "Boolean value of \""+k+"\" is false but should be true")
		assert.True(okay, "Parsing \""+k+"\" as boolean failed")
	}

	// Test false
	for _, k := range []string{"0", "n", "N", "false", "False", "FALSE", "no", "No", "NO", "Off", "off", "OFF"} {
		value, okay := parseBool(k)
		assert.False(value, "Boolean value of \""+k+"\" is true but should be false")
		assert.True(okay, "Parsing \""+k+"\" as boolean failed")
	}

	// Test invalid
	for _, k := range []string{"foo", "Disabled", "Enabled", "12", "-1", "2"} {
		value, okay := parseBool(k)
		assert.False(value, "Boolean value of \""+k+"\" is true but should be false")
		assert.False(okay, "Parsing \""+k+"\" as boolean should fail!")
	}
}

func TestPrefix(t *testing.T) {
	assert := assert.New(t)

	assert.Empty(GetPrefix())

	SetPrefix("foo")
	assert.NotEmpty(GetPrefix())
	assert.Equal("FOO_", GetPrefix())

	SetPrefix(" BAR  ")
	assert.NotEmpty(GetPrefix())
	assert.Equal("BAR_", GetPrefix())

	SetPrefix(" FOO  BAR  ")
	assert.NotEmpty(GetPrefix())
	assert.Equal("FOO_BAR_", GetPrefix())

	SetPrefix("")
	assert.Empty(GetPrefix())
	SetPrefix("   	")
	assert.Empty(GetPrefix())
}

func TestString(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test_0815")
	str, ok := GetString("envconf_test_0815")
	assert.False(ok)
	assert.Empty(str)

	SetString("envconf_test_0815", "Foo Bar 42")
	str, ok = GetString("envconf_test_0815")
	assert.True(ok)
	assert.NotEmpty(str)
	assert.Equal("Foo Bar 42", str)

	SetDefaultString("envconf_test_0815", "Bar Foo 98")
	str, ok = GetString("envconf_test_0815")
	assert.True(ok)
	assert.NotEmpty(str)
	assert.Equal("Foo Bar 42", str)

	UnsetKey("envconf_test_0815")
	str, ok = GetString("envconf_test_0815")
	assert.False(ok)
	assert.Empty(str)

	SetDefaultString("envconf_test_3345", "34")
	str, ok = GetString("envconf_test_3345")
	assert.True(ok)
	assert.NotEmpty(str)
	assert.Equal("34", str)

	SetString("envconf_test_3345", "23")
	str = MustGetString("envconf_test_3345")
	assert.NotEmpty(str)
	assert.Equal("23", str)

	SetString("envconf_test_3345", "")
	str, ok = GetString("envconf_test_3345")
	assert.True(ok)
	assert.Empty(str)
	assert.Equal("", str)

	assert.True(IssetKey("envconf_test_3345"))
	UnsetKey("envconf_test_3345")
	assert.False(IssetKey("envconf_test_3345"))

	assert.Panics(func() { MustGetString("envconf_test_3345") })
}
<commit_msg>Add more tests<commit_after>// Copyright 2016 Stefan Böhmann.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package envconf

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPrepareKey(t *testing.T) {
	assert := assert.New(t)

	assert.Empty(prepareKey(""))
	assert.NotEmpty(prepareKey("ID"))

	assert.Equal("ID", prepareKey("ID"))
	assert.Equal("ID", prepareKey("id"))
	assert.Equal("ID", prepareKey("id "))
	assert.Equal("ID", prepareKey("  id "))

	assert.Equal("FOO_BAR", prepareKey("FOO_BAR"))
	assert.Equal("FOO_BAR", prepareKey("foo_bar"))
	assert.Equal("FOO_BAR", prepareKey("foo_bar "))
	assert.Equal("FOO_BAR", prepareKey("  foo_bar "))
	assert.Equal("FOO_BAR", prepareKey("Foo Bar"))
	assert.Equal("FOO_BAR", prepareKey("  FOO  BAR "))

	assert.NotEqual("Max Mustermann", prepareKey("Max Mustermann"))

	SetPrefix("foo")
	assert.Equal("FOO_FOO_BAR", prepareKey("FOO_BAR"))

	SetPrefix("")
	assert.Equal("FOO_BAR", prepareKey("FOO_BAR"))
}

func TestParseBool(t *testing.T) {
	assert := assert.New(t)

	// Test true
	for _, k := range []string{"", "1", "y", "Y", "true", "True", "TRUE", "yes", "Yes", "YES", "On", "on", "ON"} {
		value, okay := parseBool(k)
		assert.True(value, "Boolean value of \""+k+"\" is false but should be true")
		assert.True(okay, "Parsing \""+k+"\" as boolean failed")
	}

	// Test false
	for _, k := range []string{"0", "n", "N", "false", "False", "FALSE", "no", "No", "NO", "Off", "off", "OFF"} {
		value, okay := parseBool(k)
		assert.False(value, "Boolean value of \""+k+"\" is true but should be false")
		assert.True(okay, "Parsing \""+k+"\" as boolean failed")
	}

	// Test invalid
	for _, k := range []string{"foo", "Disabled", "Enabled", "12", "-1", "2"} {
		value, okay := parseBool(k)
		assert.False(value, "Boolean value of \""+k+"\" is true but should be false")
		assert.False(okay, "Parsing \""+k+"\" as boolean should fail!")
	}
}

func TestPrefix(t *testing.T) {
	assert := assert.New(t)

	assert.Empty(GetPrefix())

	SetPrefix("foo")
	assert.NotEmpty(GetPrefix())
	assert.Equal("FOO_", GetPrefix())

	SetPrefix(" BAR  ")
	assert.NotEmpty(GetPrefix())
	assert.Equal("BAR_", GetPrefix())

	SetPrefix(" FOO  BAR  ")
	assert.NotEmpty(GetPrefix())
	assert.Equal("FOO_BAR_", GetPrefix())

	SetPrefix("")
	assert.Empty(GetPrefix())
	SetPrefix("   	")
	assert.Empty(GetPrefix())
}

func TestString(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test_0815")
	str, ok := GetString("envconf_test_0815")
	assert.False(ok)
	assert.Empty(str)

	assert.False(IssetKey("envconf_test_0815"))
	SetString("envconf_test_0815", "")
	assert.True(IssetKey("envconf_test_0815"))
	assert.Equal("", MustGetString("envconf_test_0815"))

	SetString("envconf_test_0815", "Foo Bar 42")
	str, ok = GetString("envconf_test_0815")
	assert.True(ok)
	assert.NotEmpty(str)
	assert.Equal("Foo Bar 42", str)

	SetDefaultString("envconf_test_0815", "Bar Foo 98")
	str, ok = GetString("envconf_test_0815")
	assert.True(ok)
	assert.NotEmpty(str)
	assert.Equal("Foo Bar 42", str)

	UnsetKey("envconf_test_0815")
	str, ok = GetString("envconf_test_0815")
	assert.False(ok)
	assert.Empty(str)

	SetDefaultString("envconf_test_3345", "34")
	str, ok = GetString("envconf_test_3345")
	assert.True(ok)
	assert.NotEmpty(str)
	assert.Equal("34", str)

	SetString("envconf_test_3345", "23")
	str = MustGetString("envconf_test_3345")
	assert.NotEmpty(str)
	assert.Equal("23", str)

	SetString("envconf_test_3345", "")
	str, ok = GetString("envconf_test_3345")
	assert.True(ok)
	assert.Empty(str)
	assert.Equal("", str)

	assert.True(IssetKey("envconf_test_3345"))
	UnsetKey("envconf_test_3345")
	assert.False(IssetKey("envconf_test_3345"))

	assert.Panics(func() { MustGetString("envconf_test_3345") })
}

func TestBool(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test_bool_1")
	str, ok := GetString("envconf_test_bool_1")
	assert.False(ok)
	assert.Empty(str)

	UnsetKey("envconf_test_bool_2")
	str, ok = GetString("envconf_test_bool_2")
	assert.False(ok)
	assert.Empty(str)

	// Not existing environment variable equals false
	v, ok := GetBool("envconf_test_bool_1")
	assert.False(v)
	assert.True(ok)
	assert.False(MustGetBool("envconf_test_bool_1"))

	// A empty variable is considered to be true
	SetString("envconf_test_bool_1", "")
	v, ok = GetBool("envconf_test_bool_1")
	assert.True(v)
	assert.True(ok)
	assert.True(MustGetBool("envconf_test_bool_1"))

	SetString("envconf_test_bool_1", "1")
	v, ok = GetBool("envconf_test_bool_1")
	assert.True(v)
	assert.True(ok)
	assert.True(MustGetBool("envconf_test_bool_1"))

	SetString("envconf_test_bool_1", "0")
	v, ok = GetBool("envconf_test_bool_1")
	assert.False(v)
	assert.True(ok)
	assert.False(MustGetBool("envconf_test_bool_1"))

	SetString("envconf_test_bool_1", "blah")
	v, ok = GetBool("envconf_test_bool_1")
	assert.False(v)
	assert.False(ok)
	assert.Panics(func() { MustGetBool("envconf_test_bool_1") })

	UnsetKey("envconf_test_bool_1")
	UnsetKey("envconf_test_bool_2")

	SetBool("envconf_test_bool_1", true)
	assert.True(MustGetBool("envconf_test_bool_1"))
	assert.False(MustGetBool("envconf_test_bool_2"))

	SetDefaultBool("envconf_test_bool_1", false)
	assert.True(MustGetBool("envconf_test_bool_1"))
	assert.False(MustGetBool("envconf_test_bool_2"))

	SetDefaultBool("envconf_test_bool_2", true)
	assert.True(MustGetBool("envconf_test_bool_1"))
	assert.True(MustGetBool("envconf_test_bool_2"))

	SetBool("envconf_test_bool_2", false)
	SetDefaultBool("envconf_test_bool_1", false)
	SetDefaultBool("envconf_test_bool_2", false)
	assert.True(MustGetBool("envconf_test_bool_1"))
	assert.False(MustGetBool("envconf_test_bool_2"))
}

func TestDuration(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test1")
	UnsetKey("envconf_test2")

	v, ok := GetDuration("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetDuration("envconf_test1") })

	SetString("envconf_test1", "blahBlah")
	v, ok = GetDuration("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetDuration("envconf_test1") })
}

func TestFloat64(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test1")
	UnsetKey("envconf_test2")

	v, ok := GetFloat64("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetFloat64("envconf_test1") })

	SetString("envconf_test1", "blahBlah")
	v, ok = GetFloat64("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetFloat64("envconf_test1") })
}

func TestInt(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test1")
	UnsetKey("envconf_test2")

	v, ok := GetInt("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetInt("envconf_test1") })

	SetString("envconf_test1", "blahBlah")
	v, ok = GetInt("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetInt("envconf_test1") })
}

func TestInt64(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test1")
	UnsetKey("envconf_test2")

	v, ok := GetInt64("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetInt64("envconf_test1") })

	SetString("envconf_test1", "blahBlah")
	v, ok = GetInt64("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetInt64("envconf_test1") })
}

func TestUInt(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test1")
	UnsetKey("envconf_test2")

	v, ok := GetUInt("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetUInt("envconf_test1") })

	SetString("envconf_test1", "blahBlah")
	v, ok = GetUInt("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetUInt("envconf_test1") })
}

func TestUInt64(t *testing.T) {
	assert := assert.New(t)

	UnsetKey("envconf_test1")
	UnsetKey("envconf_test2")

	v, ok := GetUInt64("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetUInt64("envconf_test1") })

	SetString("envconf_test1", "blahBlah")
	v, ok = GetUInt64("envconf_test1")
	assert.False(ok)
	assert.Zero(v)
	assert.Panics(func() { MustGetUInt64("envconf_test1") })
}
<|endoftext|>


<commit_before>package creeper

import "io/ioutil"

func Open(path string) *Creeper {
	buf, _ := ioutil.ReadFile(path)
	raw := string(buf)
	return New(raw)
}

func New(raw string) *Creeper {
	f := Formatting(raw)
	return NewByFormatted(f)
}

func NewByFormatted(f *Formatted) *Creeper {
	c := new(Creeper)

	c.Nodes = f.Nodes
	for _, n := range c.Nodes {
		n.Creeper = c
	}

	c.Towns = f.Towns
	for _, t := range c.Towns {
		t.Creeper = c
	}

	cache := map[string]string{}
	c.CacheGet = func(k string) (string, bool) {
		v, e := cache[k]
		return v, e
	}
	c.CacheSet = func(k string, v string) {
		cache[k] = v
	}

	return c
}

type Creeper struct {
	Nodes []*Node
	Towns []*Town

	CacheGet func(string) (string, bool)
	CacheSet func(string, string)

	Node *Node
}

func (c *Creeper) Array(key string) *Creeper {
	if c.Node == nil {
		c.Node = c.Nodes[0].SearchFlatScope(key)
	} else {
		c.Node = c.Node.FirstChildNode.SearchFlatScope(key)
	}
	return c
}

func (c *Creeper) String(key string) string {
	v, _ := c.StringE(key)
	return v
}

func (c *Creeper) StringE(key string) (string, error) {
	return c.Node.FirstChildNode.SearchFlatScope(key).Value()
}

func (c *Creeper) Each(cle func(*Creeper)) {
	stor := []string{}
	for {
		v, err := c.Node.Primary().Value()
		if err != nil { continue }
		for _, s := range stor {
			if s == MD5(v) {
				return
			}
		}
		stor = append(stor, MD5(v))
		cle(c)
		c.Next()
	}
}

func (c *Creeper) Next() *Creeper {
	c.Node.Inc()
	return c
}
<commit_msg>Optimized each-break judgment method & Deduplication<commit_after>package creeper

import "io/ioutil"

func Open(path string) *Creeper {
	buf, _ := ioutil.ReadFile(path)
	raw := string(buf)
	return New(raw)
}

func New(raw string) *Creeper {
	f := Formatting(raw)
	return NewByFormatted(f)
}

func NewByFormatted(f *Formatted) *Creeper {
	c := new(Creeper)

	c.Nodes = f.Nodes
	for _, n := range c.Nodes {
		n.Creeper = c
	}

	c.Towns = f.Towns
	for _, t := range c.Towns {
		t.Creeper = c
	}

	cache := map[string]string{}
	c.CacheGet = func(k string) (string, bool) {
		v, e := cache[k]
		return v, e
	}
	c.CacheSet = func(k string, v string) {
		cache[k] = v
	}

	return c
}

type Creeper struct {
	Nodes []*Node
	Towns []*Town

	CacheGet func(string) (string, bool)
	CacheSet func(string, string)

	Node *Node
}

func (c *Creeper) Array(key string) *Creeper {
	if c.Node == nil {
		c.Node = c.Nodes[0].SearchFlatScope(key)
	} else {
		c.Node = c.Node.FirstChildNode.SearchFlatScope(key)
	}
	return c
}

func (c *Creeper) String(key string) string {
	v, _ := c.StringE(key)
	return v
}

func (c *Creeper) StringE(key string) (string, error) {
	return c.Node.FirstChildNode.SearchFlatScope(key).Value()
}

func (c *Creeper) Each(cle func(*Creeper)) {
	fstNv := []string{}
	repStor := []string{}
	each: for {
		v, err := c.Node.Primary().Value()
		if err != nil { continue }

		if c.Node.Index == 0 {
			for _, s := range fstNv {
				if s == MD5(v) {
					break each
				}
			}
			fstNv = append(fstNv, MD5(v))
		}

		for _, s := range repStor {
			if s == MD5(v) {
				continue each
			}
		}
		repStor = append(repStor, MD5(v))

		cle(c)
		c.Next()
	}
}

func (c *Creeper) Next() *Creeper {
	c.Node.Inc()
	return c
}
<|endoftext|>


<commit_before>package rkive

import (
	check "gopkg.in/check.v1"
	"time"
)

func (s *riakSuite) TestCounter(c *check.C) {
	startt := time.Now()

	var ct *Counter
	var err error
	ct, err = s.cl.Bucket("testbucket").NewCounter("test-counter", 0)
	if err != nil {
		c.Fatal(err)
	}

	start := ct.Val()

	err = ct.Add(5)
	if err != nil {
		c.Error(err)
	}

	if ct.Val() != start+5 {
		c.Errorf("Expected value %d; got %d", start+5, ct.Val())
	}

	err = ct.Refresh()
	if err != nil {
		c.Error(err)
	}

	if ct.Val() != start+5 {
		c.Errorf("Expected value %d; got %d", start+5, ct.Val())
	}

	nct, err := s.cl.Bucket("testbucket").GetCounter("test-counter")
	if err != nil {
		c.Fatal(err)
	}

	if nct.Val() != start+5 {
		c.Errorf("Expected value %d; got %d", start+5, nct.Val())
	}

	err = ct.Destroy()
	if err != nil {
		c.Error(err)
	}

	nct, err = s.cl.Bucket("testbucket").GetCounter("test-counter")
	if err != ErrNotFound {
		c.Errorf("Expected ErrNotFound (%q); got %q", ErrNotFound, err)
	}

	s.runtime += time.Since(startt)
}
<commit_msg>don't run counter test in CI<commit_after>package rkive

import (
	check "gopkg.in/check.v1"
	"os"
	"time"
)

func (s *riakSuite) TestCounter(c *check.C) {
	travis := os.Getenv("TRAVIS")
	werck := os.Getenv("WERCKER")
	if travis != "" || werck != "" {
		c.Skip(`The CI environment does not have "allow_mult" set to 'true'`)
	}

	startt := time.Now()

	var ct *Counter
	var err error
	ct, err = s.cl.Bucket("testbucket").NewCounter("test-counter", 0)
	if err != nil {
		c.Fatal(err)
	}

	start := ct.Val()

	err = ct.Add(5)
	if err != nil {
		c.Error(err)
	}

	if ct.Val() != start+5 {
		c.Errorf("Expected value %d; got %d", start+5, ct.Val())
	}

	err = ct.Refresh()
	if err != nil {
		c.Error(err)
	}

	if ct.Val() != start+5 {
		c.Errorf("Expected value %d; got %d", start+5, ct.Val())
	}

	nct, err := s.cl.Bucket("testbucket").GetCounter("test-counter")
	if err != nil {
		c.Fatal(err)
	}

	if nct.Val() != start+5 {
		c.Errorf("Expected value %d; got %d", start+5, nct.Val())
	}

	err = ct.Destroy()
	if err != nil {
		c.Error(err)
	}

	nct, err = s.cl.Bucket("testbucket").GetCounter("test-counter")
	if err != ErrNotFound {
		c.Errorf("Expected ErrNotFound (%q); got %q", ErrNotFound, err)
	}

	s.runtime += time.Since(startt)
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handlers

import (
	"encoding/base64"
	"reflect"
	"strings"
	"time"

	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/core/adapters/udp"
	"github.com/TheThingsNetwork/ttn/semtech"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/TheThingsNetwork/ttn/utils/pointer"
	"github.com/TheThingsNetwork/ttn/utils/stats"
	"github.com/brocaar/lorawan"
)

// Semtech implements the Semtech protocol and make a bridge between gateways and routers
type Semtech struct{}

// Handle implements the udp.Handler interface
func (s Semtech) Handle(conn chan<- udp.MsgUDP, packets chan<- udp.MsgReq, msg udp.MsgUDP) error {
	pkt := new(semtech.Packet)
	err := pkt.UnmarshalBinary(msg.Data)
	if err != nil {
		return errors.New(errors.Structural, err)
	}

	switch pkt.Identifier {
	case semtech.PULL_DATA: // PULL_DATA -> Respond to the recipient with an ACK
		stats.MarkMeter("semtech_adapter.pull_data")
		data, err := semtech.Packet{
			Version:    semtech.VERSION,
			Token:      pkt.Token,
			Identifier: semtech.PULL_ACK,
		}.MarshalBinary()
		if err != nil {
			return errors.New(errors.Structural, err)
		}
		conn <- udp.MsgUDP{
			Addr: msg.Addr,
			Data: data,
		}
	case semtech.PUSH_DATA: // PUSH_DATA -> Transfer all RXPK to the component
		stats.MarkMeter("semtech_adapter.push_data")
		data, err := semtech.Packet{
			Version:    semtech.VERSION,
			Token:      pkt.Token,
			Identifier: semtech.PUSH_ACK,
		}.MarshalBinary()
		if err != nil {
			return errors.New(errors.Structural, err)
		}
		conn <- udp.MsgUDP{
			Addr: msg.Addr,
			Data: data,
		}

		if pkt.Payload == nil {
			return errors.New(errors.Structural, "Unable to process empty PUSH_DATA payload")
		}

		for _, rxpk := range pkt.Payload.RXPK {
			go func(rxpk semtech.RXPK) {
				pktOut, err := rxpk2packet(rxpk, pkt.GatewayId)
				if err != nil {
					// TODO Log error
					return
				}
				data, err := pktOut.MarshalBinary()
				if err != nil {
					// TODO Log error
					return
				}
				chresp := make(chan udp.MsgRes)
				packets <- udp.MsgReq{Data: data, Chresp: chresp}
				select {
				case resp := <-chresp:
					itf, err := core.UnmarshalPacket(resp)
					if err != nil {
						return
					}
					pkt, ok := itf.(core.RPacket) // NOTE Here we'll handle join-accept
					if !ok {
						return
					}
					txpk, err := packet2txpk(pkt)
					if err != nil {
						// TODO Log error
						return
					}

					data, err := semtech.Packet{
						Version:    semtech.VERSION,
						Identifier: semtech.PULL_RESP,
						Payload:    &semtech.Payload{TXPK: &txpk},
					}.MarshalBinary()
					if err != nil {
						// TODO Log error
						return
					}
					conn <- udp.MsgUDP{Addr: msg.Addr, Data: data}
				case <-time.After(time.Second * 2):
				}
			}(rxpk)
		}
	default:
		return errors.New(errors.Implementation, "Unhandled packet type")
	}
	return nil
}

func rxpk2packet(p semtech.RXPK, gid []byte) (core.Packet, error) {
	// First, we have to get the physical payload which is encoded in the Data field
	if p.Data == nil {
		return nil, errors.New(errors.Structural, "There's no data in the packet")
	}

	// RXPK Data are base64 encoded, yet without the trailing "==" if any.....
	encoded := *p.Data
	switch len(encoded) % 4 {
	case 2:
		encoded += "=="
	case 3:
		encoded += "="
	}

	raw, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return nil, errors.New(errors.Structural, err)
	}

	payload := lorawan.NewPHYPayload(true)
	if err = payload.UnmarshalBinary(raw); err != nil {
		return nil, errors.New(errors.Structural, err)
	}

	// Then, we interpret every other known field as a metadata and store them into an appropriate
	// metadata object.
	metadata := core.Metadata{}
	rxpkValue := reflect.ValueOf(p)
	rxpkStruct := rxpkValue.Type()
	metas := reflect.ValueOf(&metadata).Elem()
	for i := 0; i < rxpkStruct.NumField(); i++ {
		field := rxpkStruct.Field(i).Name
		if metas.FieldByName(field).CanSet() {
			metas.FieldByName(field).Set(rxpkValue.Field(i))
		}
	}

	// At the end, our converted packet hold the same metadata than the RXPK packet but the Data
	// which as been completely transformed into a lorawan Physical Payload.
	return core.NewRPacket(payload, gid, metadata)
}

func packet2txpk(p core.RPacket) (semtech.TXPK, error) {
	// Step 1, convert the physical payload to a base64 string (without the padding)
	raw, err := p.Payload().MarshalBinary()
	if err != nil {
		return semtech.TXPK{}, errors.New(errors.Structural, err)
	}

	data := strings.Trim(base64.StdEncoding.EncodeToString(raw), "=")
	txpk := semtech.TXPK{Data: pointer.String(data)}

	// Step 2, copy every compatible metadata from the packet to the TXPK packet.
	// We are possibly loosing information here.
	metadataValue := reflect.ValueOf(p.Metadata())
	metadataStruct := metadataValue.Type()
	txpkStruct := reflect.ValueOf(&txpk).Elem()
	for i := 0; i < metadataStruct.NumField(); i++ {
		field := metadataStruct.Field(i).Name
		if txpkStruct.FieldByName(field).CanSet() {
			txpkStruct.FieldByName(field).Set(metadataValue.Field(i))
		}
	}

	return txpk, nil
}
<commit_msg>[issues/#45+#49] Handle Stats packet in semtech adapter<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handlers

import (
	"encoding/base64"
	"reflect"
	"strings"
	"time"

	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/core/adapters/udp"
	"github.com/TheThingsNetwork/ttn/semtech"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/TheThingsNetwork/ttn/utils/pointer"
	"github.com/TheThingsNetwork/ttn/utils/stats"
	"github.com/brocaar/lorawan"
)

// Semtech implements the Semtech protocol and make a bridge between gateways and routers
type Semtech struct{}

// Handle implements the udp.Handler interface
func (s Semtech) Handle(conn chan<- udp.MsgUDP, packets chan<- udp.MsgReq, msg udp.MsgUDP) error {
	pkt := new(semtech.Packet)
	err := pkt.UnmarshalBinary(msg.Data)
	if err != nil {
		return errors.New(errors.Structural, err)
	}

	switch pkt.Identifier {
	case semtech.PULL_DATA: // PULL_DATA -> Respond to the recipient with an ACK
		stats.MarkMeter("semtech_adapter.pull_data")
		data, err := semtech.Packet{
			Version:    semtech.VERSION,
			Token:      pkt.Token,
			Identifier: semtech.PULL_ACK,
		}.MarshalBinary()
		if err != nil {
			return errors.New(errors.Structural, err)
		}
		conn <- udp.MsgUDP{
			Addr: msg.Addr,
			Data: data,
		}
	case semtech.PUSH_DATA: // PUSH_DATA -> Transfer all RXPK to the component
		stats.MarkMeter("semtech_adapter.push_data")
		data, err := semtech.Packet{
			Version:    semtech.VERSION,
			Token:      pkt.Token,
			Identifier: semtech.PUSH_ACK,
		}.MarshalBinary()
		if err != nil {
			return errors.New(errors.Structural, err)
		}
		conn <- udp.MsgUDP{
			Addr: msg.Addr,
			Data: data,
		}

		if pkt.Payload == nil {
			return errors.New(errors.Structural, "Unable to process empty PUSH_DATA payload")
		}

		// Handle stat payload
		if pkt.Payload.Stat != nil {
			spacket, err := core.NewSPacket(pkt.GatewayId, extractMetadata(*pkt.Payload.Stat))
			if err == nil {
				data, err := spacket.MarshalBinary()
				if err == nil {
					go func() {
						packets <- udp.MsgReq{Data: data, Chresp: nil}
					}()
				}
			}
		}

		// Handle rxpks payloads
		for _, rxpk := range pkt.Payload.RXPK {
			go func(rxpk semtech.RXPK) {
				pktOut, err := rxpk2packet(rxpk, pkt.GatewayId)
				if err != nil {
					// TODO Log error
					return
				}
				data, err := pktOut.MarshalBinary()
				if err != nil {
					// TODO Log error
					return
				}
				chresp := make(chan udp.MsgRes)
				packets <- udp.MsgReq{Data: data, Chresp: chresp}
				select {
				case resp := <-chresp:
					itf, err := core.UnmarshalPacket(resp)
					if err != nil {
						return
					}
					pkt, ok := itf.(core.RPacket) // NOTE Here we'll handle join-accept
					if !ok {
						return
					}
					txpk, err := packet2txpk(pkt)
					if err != nil {
						// TODO Log error
						return
					}

					data, err := semtech.Packet{
						Version:    semtech.VERSION,
						Identifier: semtech.PULL_RESP,
						Payload:    &semtech.Payload{TXPK: &txpk},
					}.MarshalBinary()
					if err != nil {
						// TODO Log error
						return
					}
					conn <- udp.MsgUDP{Addr: msg.Addr, Data: data}
				case <-time.After(time.Second * 2):
				}
			}(rxpk)
		}
	default:
		return errors.New(errors.Implementation, "Unhandled packet type")
	}
	return nil
}

func rxpk2packet(p semtech.RXPK, gid []byte) (core.Packet, error) {
	// First, we have to get the physical payload which is encoded in the Data field
	if p.Data == nil {
		return nil, errors.New(errors.Structural, "There's no data in the packet")
	}

	// RXPK Data are base64 encoded, yet without the trailing "==" if any.....
	encoded := *p.Data
	switch len(encoded) % 4 {
	case 2:
		encoded += "=="
	case 3:
		encoded += "="
	}

	raw, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return nil, errors.New(errors.Structural, err)
	}

	payload := lorawan.NewPHYPayload(true)
	if err = payload.UnmarshalBinary(raw); err != nil {
		return nil, errors.New(errors.Structural, err)
	}
	// At the end, our converted packet hold the same metadata than the RXPK packet but the Data
	// which as been completely transformed into a lorawan Physical Payload.
	return core.NewRPacket(payload, gid, extractMetadata(p))
}

func packet2txpk(p core.RPacket) (semtech.TXPK, error) {
	// Step 1, convert the physical payload to a base64 string (without the padding)
	raw, err := p.Payload().MarshalBinary()
	if err != nil {
		return semtech.TXPK{}, errors.New(errors.Structural, err)
	}

	data := strings.Trim(base64.StdEncoding.EncodeToString(raw), "=")
	txpk := semtech.TXPK{Data: pointer.String(data)}

	// Step 2, copy every compatible metadata from the packet to the TXPK packet.
	// We are possibly loosing information here.
	injectMetadata(&txpk, p.Metadata())

	return txpk, nil
}

func injectMetadata(ptr interface{}, metadata core.Metadata) {
	v := reflect.ValueOf(metadata)
	t := v.Type()
	d := reflect.ValueOf(ptr).Elem()

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i).Name
		if d.FieldByName(field).CanSet() {
			d.FieldByName(field).Set(v.Field(i))
		}
	}
}

func extractMetadata(xpk interface{}) core.Metadata {
	metadata := core.Metadata{}
	v := reflect.ValueOf(xpk)
	t := v.Type()
	m := reflect.ValueOf(&metadata).Elem()

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i).Name
		if m.FieldByName(field).CanSet() {
			m.FieldByName(field).Set(v.Field(i))
		}
	}

	return metadata
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package manager

import (
	"encoding/json"
	"errors"
	"strconv"

	"github.com/ernestio/ernest-cli/helper"
	"github.com/ernestio/ernest-cli/model"
)

// ListUsers ...
func (m *Manager) ListUsers(token string) (users []model.User, err error) {
	body, resp, err := m.doRequest("/api/users/", "GET", []byte(""), token, "")
	if err != nil {
		if resp.StatusCode == 400 {
			return users, errors.New("You're not allowed to perform this action, please log in")
		}
		if resp.StatusCode == 404 {
			return users, errors.New("Couldn't found any users")
		}
		return nil, err
	}
	err = json.Unmarshal([]byte(body), &users)
	if err != nil {
		return nil, err
	}
	return users, err
}

// GetUserByUsername : Gets a user by name
func (m *Manager) GetUserByUsername(token string, name string) (user model.User, err error) {
	users, err := m.ListUsers(token)
	for _, u := range users {
		if u.Username == name {
			return u, nil
		}
	}
	return user, errors.New("User not found")
}

// GetUser ...
func (m *Manager) GetUser(token string, userid string) (user model.User, err error) {
	res, _, err := m.doRequest("/api/users/"+userid, "GET", nil, token, "application/yaml")
	if err != nil {
		return user, err
	}
	err = json.Unmarshal([]byte(res), &user)
	if err != nil {
		return user, err
	}
	return user, nil
}

// CreateUser ...
func (m *Manager) CreateUser(token string, name string, email string, user string, password string) error {
	payload := []byte(`{"group_id": 0, "username": "` + user + `", "email": "` + email + `", "password": "` + password + `"}`)
	body, resp, err := m.doRequest("/api/users/", "POST", payload, token, "")
	if err != nil {
		if resp.StatusCode != 200 {
			e := helper.ResponseMessage([]byte(body))
			return errors.New(e.Message)
		}
		return err
	}
	return nil
}

// ChangePassword ...
func (m *Manager) ChangePassword(token string, userid int, username string, usergroup int, oldpassword string, newpassword string) error {
	payload := []byte(`{"id":` + strconv.Itoa(userid) + `, "username": "` + username + `", "group_id": ` + strconv.Itoa(usergroup) + `, "password": "` + newpassword + `", "oldpassword": "` + oldpassword + `"}`)
	body, resp, err := m.doRequest("/api/users/"+strconv.Itoa(userid), "PUT", payload, token, "application/yaml")
	if err != nil {
		if resp.StatusCode != 200 {
			e := helper.ResponseMessage([]byte(body))
			return errors.New(e.Message)
		}
		return err
	}
	return nil
}

// ChangePasswordByAdmin ...
func (m *Manager) ChangePasswordByAdmin(token string, userid int, username string, usergroup int, newpassword string) error {
	payload := []byte(`{"id":` + strconv.Itoa(userid) + `, "username": "` + username + `", "group_id": ` + strconv.Itoa(usergroup) + `, "password": "` + newpassword + `"}`)
	body, resp, err := m.doRequest("/api/users/"+strconv.Itoa(userid), "PUT", payload, token, "application/yaml")
	if err != nil {
		if resp.StatusCode != 200 {
			e := helper.ResponseMessage([]byte(body))
			return errors.New(e.Message)
		}
		return err
	}
	return nil
}
<commit_msg>Updated error response message<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package manager

import (
	"encoding/json"
	"errors"
	"strconv"
	"strings"

	"github.com/ernestio/ernest-cli/helper"
	"github.com/ernestio/ernest-cli/model"
)

// ListUsers ...
func (m *Manager) ListUsers(token string) (users []model.User, err error) {
	body, resp, err := m.doRequest("/api/users/", "GET", []byte(""), token, "")
	if err != nil {
		if resp.StatusCode == 400 {
			return users, errors.New("You're not allowed to perform this action, please log in")
		}
		if resp.StatusCode == 404 {
			return users, errors.New("Couldn't found any users")
		}
		return nil, err
	}
	err = json.Unmarshal([]byte(body), &users)
	if err != nil {
		return nil, err
	}
	return users, err
}

// GetUserByUsername : Gets a user by name
func (m *Manager) GetUserByUsername(token string, name string) (user model.User, err error) {
	users, err := m.ListUsers(token)
	for _, u := range users {
		if u.Username == name {
			return u, nil
		}
	}
	return user, errors.New("User not found")
}

// GetUser ...
func (m *Manager) GetUser(token string, userid string) (user model.User, err error) {
	res, _, err := m.doRequest("/api/users/"+userid, "GET", nil, token, "application/yaml")
	if err != nil {
		return user, err
	}
	err = json.Unmarshal([]byte(res), &user)
	if err != nil {
		return user, err
	}
	return user, nil
}

// CreateUser ...
func (m *Manager) CreateUser(token string, name string, email string, user string, password string) error {
	payload := []byte(`{"group_id": 0, "username": "` + user + `", "email": "` + email + `", "password": "` + password + `"}`)
	body, resp, err := m.doRequest("/api/users/", "POST", payload, token, "")
	if err != nil {
		if resp.StatusCode != 200 {
			e := helper.ResponseMessage([]byte(body))
			if strings.Contains(e.Message, "invalid jwt") {
				return errors.New("You're not allowed to perform this action, please log in")
			}
			return errors.New(e.Message)
		}
		return err
	}
	return nil
}

// ChangePassword ...
func (m *Manager) ChangePassword(token string, userid int, username string, usergroup int, oldpassword string, newpassword string) error {
	payload := []byte(`{"id":` + strconv.Itoa(userid) + `, "username": "` + username + `", "group_id": ` + strconv.Itoa(usergroup) + `, "password": "` + newpassword + `", "oldpassword": "` + oldpassword + `"}`)
	body, resp, err := m.doRequest("/api/users/"+strconv.Itoa(userid), "PUT", payload, token, "application/yaml")
	if err != nil {
		if resp.StatusCode != 200 {
			e := helper.ResponseMessage([]byte(body))
			return errors.New(e.Message)
		}
		return err
	}
	return nil
}

// ChangePasswordByAdmin ...
func (m *Manager) ChangePasswordByAdmin(token string, userid int, username string, usergroup int, newpassword string) error {
	payload := []byte(`{"id":` + strconv.Itoa(userid) + `, "username": "` + username + `", "group_id": ` + strconv.Itoa(usergroup) + `, "password": "` + newpassword + `"}`)
	body, resp, err := m.doRequest("/api/users/"+strconv.Itoa(userid), "PUT", payload, token, "application/yaml")
	if err != nil {
		if resp.StatusCode != 200 {
			e := helper.ResponseMessage([]byte(body))
			return errors.New(e.Message)
		}
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"compress/lzw"
	"errors"
	"image"
	"image/color"
	"image/gif"
	"io"
	"math"
	"os"
)

var (
	errDelay        = errors.New("gif: number of images and delays doesn't match")
	errNoImage      = errors.New("gif: no images given (needs at least 1)")
	errNegativeLoop = errors.New("gif: loop count can't be negative (use 0 for infinite)")
)

func writeHeader(w *bufio.Writer, image *gif.GIF) {
	w.Write([]uint8("GIF89a"))

	b := image.Image[0].Bounds()
	w.WriteByte(uint8(b.Max.X % 255)) // Paletted width, LSB.
	w.WriteByte(uint8(b.Max.X / 255)) // Paletted width, MSB.
	w.WriteByte(uint8(b.Max.Y % 255)) // Paletted height, LSB.
	w.WriteByte(uint8(b.Max.Y / 255)) // Paletted height, MSB.

	w.WriteByte(uint8(0xF7)) // GCT follows for 256 colors with resolution
	// 3 x 8 bits/primary
	w.WriteByte(uint8(0x00)) // Background color.
	w.WriteByte(uint8(0x00)) // Default pixel aspect ratio.

	// Global Color Table.
	palette := image.Image[0].Palette
	for _, c := range palette {
		r, g, b, _ := c.RGBA()
		w.WriteByte(uint8(r))
		w.WriteByte(uint8(g))
		w.WriteByte(uint8(b))
	}

	w.WriteByte(uint8(0x21))                   // Application Extension block.
	w.WriteByte(uint8(0xFF))                   // Application Extension block (cont).
	w.WriteByte(uint8(0x0B))                   // Next 11 bytes are Application Extension.
	w.Write([]uint8("NETSCAPE2.0"))            // 8 Character application name.
	w.WriteByte(uint8(0x03))                   // 3 more bytes of Application Extension.
	w.WriteByte(uint8(0x01))                   // Data sub-block index (always 1).
	w.WriteByte(uint8(image.LoopCount % 0xFF)) // Number of repetitions, LSB.
	w.WriteByte(uint8(image.LoopCount / 0xFF)) // Number of repetitions, MSB.
	w.WriteByte(uint8(0x00))                   // End of Application Extension block.
}

func writeFrameHeader(w *bufio.Writer, m *image.Paletted, delay int) {
	w.WriteByte(uint8(0x21)) // Start of Graphic Control Extension.
	w.WriteByte(uint8(0xF9)) // Start of Graphic Control Extension (cont).
	w.WriteByte(uint8(0x04)) // 4 more bytes of GCE.

	w.WriteByte(uint8(0x08))         // There is no transparent pixel.
	w.WriteByte(uint8(delay % 0xFF)) // Animation delay, in centiseconds, LSB.
	w.WriteByte(uint8(delay / 0xFF)) // Animation delay, in centiseconds, MSB.
	w.WriteByte(uint8(0x00))         // Transparent color #, if we were using.
	w.WriteByte(uint8(0x00))         // End of Application Extension data.

	w.WriteByte(uint8(0x2C)) // Start of Paletted Descriptor.

	b := m.Bounds()
	w.WriteByte(uint8(b.Min.X % 255)) // Minimum x (can be > 0), LSB.
	w.WriteByte(uint8(b.Min.X / 255)) // Minimum x (can be > 0), MSB.
	w.WriteByte(uint8(b.Min.Y % 255)) // Minimum y (can be > 0), LSB.
	w.WriteByte(uint8(b.Min.Y / 255)) // Minimum y (can be > 0), MSB.

	w.WriteByte(uint8(b.Max.X % 255)) // Frame width, LSB.
	w.WriteByte(uint8(b.Max.X / 255)) // Frame width, MSB.
	w.WriteByte(uint8(b.Max.Y % 255)) // Frame height, LSB.
	w.WriteByte(uint8(b.Max.Y / 255)) // Frame height, MSB.

	w.WriteByte(uint8(0x00)) // No local color table.
}

func compressImage(m *image.Paletted) *bytes.Buffer {
	compressedImageBuffer := bytes.NewBuffer(make([]uint8, 0, 255))
	lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))
	lzww.Write(m.Pix)
	lzww.Close()

	return compressedImageBuffer
}

func writeFrame(w *bufio.Writer, m *image.Paletted, delay int) {
	writeFrameHeader(w, m, delay)

	w.WriteByte(uint8(0x08)) // Start of LZW with minimum code size 8.

	compressedImage := compressImage(m)

	const maxBlockSize = 255
	bytesSoFar := 0
	bytesRemaining := compressedImage.Len()
	for bytesRemaining > 0 {
		if bytesSoFar == 0 {
			blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
			w.WriteByte(uint8(blockSize))
		}

		b, _ := compressedImage.ReadByte()
		w.WriteByte(b)

		bytesSoFar = (bytesSoFar + 1) % maxBlockSize
		bytesRemaining--
	}

	w.WriteByte(uint8(0x00)) // End of LZW data.
}

func Encode(w io.Writer, m *image.Paletted) error {
	animation := gif.GIF{[]*image.Paletted{m}, []int{0}, 0}
	return EncodeAll(w, &animation)
}

func EncodeAll(w io.Writer, animation *gif.GIF) error {
	if len(animation.Image) != len(animation.Delay) {
		return errDelay
	}

	if len(animation.Image) == 0 {
		return errNoImage
	}

    if animation.LoopCount < 0 {
        return errNegativeLoop
    }

	buffer := bufio.NewWriter(w)

	writeHeader(buffer, animation)
	for i, _ := range animation.Image {
		image := animation.Image[i]
		delay := animation.Delay[i]
		writeFrame(buffer, image, delay)
	}
	buffer.WriteByte(';')
	buffer.Flush()

	return nil
}

func main() {
	p := make([]color.Color, 256)

	for i := 0; i < 256; i++ {
		c := uint8((i / 16) ^ (i % 16))
		p[i] = color.RGBA{c, c, c, 0xFF}
	}

    images := make([]*image.Paletted, 25)
    delays := make([]int, 25)

    for i := 0; i < 25; i++ {
        m := image.NewPaletted(image.Rect(0, 0, 100, 100), p)
        for x := 0; x < 100; x++ {
            for y := 0; y < 100; y++ {
                m.SetColorIndex(x, y, uint8(x * y / (i + 1)))
            }
        }

        images[i] = m
        delays[i] = 10
    }

	file, _ := os.Create("new_image.gif")

	animation := gif.GIF{images, delays, 0}
	EncodeAll(file, &animation)
}
<commit_msg>Add new error checking<commit_after>package main

import (
	"bufio"
	"bytes"
	"compress/lzw"
	"errors"
	"image"
	"image/color"
	"image/gif"
	"io"
	"math"
	"os"
)

var (
	errDelay           = errors.New("gif: number of images and delays doesn't match")
	errNoImage         = errors.New("gif: no images given (needs at least 1)")
	errNegativeLoop    = errors.New("gif: loop count can't be negative (use 0 for infinite)")
	errPaletteTooBig   = errors.New("gif: global color palette has too many elements")
	errPaletteTooSmall = errors.New("gif: global color palette has too few elements")
)

func writeHeader(w *bufio.Writer, image *gif.GIF) {
	w.Write([]uint8("GIF89a"))

	b := image.Image[0].Bounds()
	w.WriteByte(uint8(b.Max.X % 255)) // Paletted width, LSB.
	w.WriteByte(uint8(b.Max.X / 255)) // Paletted width, MSB.
	w.WriteByte(uint8(b.Max.Y % 255)) // Paletted height, LSB.
	w.WriteByte(uint8(b.Max.Y / 255)) // Paletted height, MSB.

	w.WriteByte(uint8(0xF7)) // GCT follows for 256 colors with resolution
	// 3 x 8 bits/primary
	w.WriteByte(uint8(0x00)) // Background color.
	w.WriteByte(uint8(0x00)) // Default pixel aspect ratio.

	// Global Color Table.
	palette := image.Image[0].Palette
	for _, c := range palette {
		r, g, b, _ := c.RGBA()
		w.WriteByte(uint8(r))
		w.WriteByte(uint8(g))
		w.WriteByte(uint8(b))
	}

	w.WriteByte(uint8(0x21))                   // Application Extension block.
	w.WriteByte(uint8(0xFF))                   // Application Extension block (cont).
	w.WriteByte(uint8(0x0B))                   // Next 11 bytes are Application Extension.
	w.Write([]uint8("NETSCAPE2.0"))            // 8 Character application name.
	w.WriteByte(uint8(0x03))                   // 3 more bytes of Application Extension.
	w.WriteByte(uint8(0x01))                   // Data sub-block index (always 1).
	w.WriteByte(uint8(image.LoopCount % 0xFF)) // Number of repetitions, LSB.
	w.WriteByte(uint8(image.LoopCount / 0xFF)) // Number of repetitions, MSB.
	w.WriteByte(uint8(0x00))                   // End of Application Extension block.
}

func writeFrameHeader(w *bufio.Writer, m *image.Paletted, delay int) {
	w.WriteByte(uint8(0x21)) // Start of Graphic Control Extension.
	w.WriteByte(uint8(0xF9)) // Start of Graphic Control Extension (cont).
	w.WriteByte(uint8(0x04)) // 4 more bytes of GCE.

	w.WriteByte(uint8(0x08))         // There is no transparent pixel.
	w.WriteByte(uint8(delay % 0xFF)) // Animation delay, in centiseconds, LSB.
	w.WriteByte(uint8(delay / 0xFF)) // Animation delay, in centiseconds, MSB.
	w.WriteByte(uint8(0x00))         // Transparent color #, if we were using.
	w.WriteByte(uint8(0x00))         // End of Application Extension data.

	w.WriteByte(uint8(0x2C)) // Start of Paletted Descriptor.

	b := m.Bounds()
	w.WriteByte(uint8(b.Min.X % 255)) // Minimum x (can be > 0), LSB.
	w.WriteByte(uint8(b.Min.X / 255)) // Minimum x (can be > 0), MSB.
	w.WriteByte(uint8(b.Min.Y % 255)) // Minimum y (can be > 0), LSB.
	w.WriteByte(uint8(b.Min.Y / 255)) // Minimum y (can be > 0), MSB.

	w.WriteByte(uint8(b.Max.X % 255)) // Frame width, LSB.
	w.WriteByte(uint8(b.Max.X / 255)) // Frame width, MSB.
	w.WriteByte(uint8(b.Max.Y % 255)) // Frame height, LSB.
	w.WriteByte(uint8(b.Max.Y / 255)) // Frame height, MSB.

	w.WriteByte(uint8(0x00)) // No local color table.
}

func compressImage(m *image.Paletted) *bytes.Buffer {
	compressedImageBuffer := bytes.NewBuffer(make([]uint8, 0, 255))
	lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))
	lzww.Write(m.Pix)
	lzww.Close()

	return compressedImageBuffer
}

func writeFrame(w *bufio.Writer, m *image.Paletted, delay int) {
	writeFrameHeader(w, m, delay)

	w.WriteByte(uint8(0x08)) // Start of LZW with minimum code size 8.

	compressedImage := compressImage(m)

	const maxBlockSize = 255
	bytesSoFar := 0
	bytesRemaining := compressedImage.Len()
	for bytesRemaining > 0 {
		if bytesSoFar == 0 {
			blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
			w.WriteByte(uint8(blockSize))
		}

		b, _ := compressedImage.ReadByte()
		w.WriteByte(b)

		bytesSoFar = (bytesSoFar + 1) % maxBlockSize
		bytesRemaining--
	}

	w.WriteByte(uint8(0x00)) // End of LZW data.
}

func Encode(w io.Writer, m *image.Paletted) error {
	animation := gif.GIF{[]*image.Paletted{m}, []int{0}, 0}
	return EncodeAll(w, &animation)
}

func EncodeAll(w io.Writer, animation *gif.GIF) error {
	if len(animation.Image) != len(animation.Delay) {
		return errDelay
	}

	if len(animation.Image) == 0 {
		return errNoImage
	}

	if animation.LoopCount < 0 {
		return errNegativeLoop
	}

    if len(animation.Image[0].Palette) > 256 {
        return errPaletteTooBig
    }

    if len(animation.Image[0].Palette) < 256 {
        return errPaletteTooSmall
    }

	buffer := bufio.NewWriter(w)

	writeHeader(buffer, animation)
	for i, _ := range animation.Image {
		image := animation.Image[i]
		delay := animation.Delay[i]
		writeFrame(buffer, image, delay)
	}
	buffer.WriteByte(';')
	buffer.Flush()

	return nil
}

func main() {
	p := make([]color.Color, 256)

	for i := 0; i < 256; i++ {
		c := uint8((i / 16) ^ (i % 16))
		p[i] = color.RGBA{c, c, c, 0xFF}
	}

	images := make([]*image.Paletted, 25)
	delays := make([]int, 25)

	for i := 0; i < 25; i++ {
		m := image.NewPaletted(image.Rect(0, 0, 100, 100), p)
		for x := 0; x < 100; x++ {
			for y := 0; y < 100; y++ {
				m.SetColorIndex(x, y, uint8(x*y/(i+1)))
			}
		}

		images[i] = m
		delays[i] = 100
	}

	file, _ := os.Create("new_image.gif")

	animation := gif.GIF{images, delays, 0}
	EncodeAll(file, &animation)
}
<|endoftext|>


<commit_before>package rest

import (
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/alxeg/flibooks/datastore"
	"github.com/alxeg/flibooks/inpx"
	"github.com/alxeg/flibooks/models"
	"github.com/alxeg/flibooks/utils"
	"github.com/emicklei/go-restful"
)

type RestService struct {
	listen    string
	dataDir   string
	dataStore datastore.DataStorer
	container *restful.Container
}

func (service RestService) registerBookResource(container *restful.Container) {
	ws := new(restful.WebService)
	ws.
		Path("/book").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	ws.Route(ws.GET("/{bookId}").
		To(service.getBook).
		Doc("Get specific book info").
		Operation("getBook").
		Param(ws.PathParameter("bookId", "identifier of the book").DataType("int")).
		Returns(200, "OK", models.Book{}))

	ws.Route(ws.GET("/langs").
		To(service.getLangs).
		Doc("Get all available books languages").
		Operation("getLangs").
		Returns(200, "OK", []string{"en"}))

	ws.Route(ws.GET("/{bookId}/download").
		To(service.downloadBook).
		Doc("Download book content").
		Operation("downloadBook").
		Param(ws.PathParameter("bookId", "identifier of the book").DataType("int")).
		Returns(200, "OK", models.Book{}))

	ws.Route(ws.POST("/search").
		To(service.searchBooks).
		Doc("Search for the books").
		Operation("searchBooks").
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.POST("/series").
		To(service.searchSeries).
		Doc("Search for the books").
		Operation("searchBooks").
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.GET("/lib/{libId}").
		To(service.getBooksByLibID).
		Doc("Get books by libId").
		Operation("getBooksByLibId").
		Param(ws.PathParameter("libId", "libId of the book").DataType("string")).
		Returns(200, "OK", []models.Book{}))

	container.Add(ws)
}

func (service RestService) registerAuthorResource(container *restful.Container) {
	ws := new(restful.WebService)
	ws.
		Path("/author").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	ws.Route(ws.GET("/{authorId}").
		To(service.getAuthor).
		Doc("Get author's info").
		Operation("getAuthor").
		Param(ws.PathParameter("authorId", "identifier of the author").DataType("int")).
		Returns(200, "OK", models.Author{}))

	ws.Route(ws.GET("/{authorId}/books").
		To(service.listAuthorsBooks).
		Doc("Show author's books").
		Operation("listAuthorsBooks").
		Param(ws.PathParameter("authorId", "identifier of the author").DataType("int")).
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.POST("/{authorId}/books").
		To(service.listAuthorsBooksPost).
		Doc("Show author's books").
		Operation("listAuthorsBooks").
		Param(ws.PathParameter("authorId", "identifier of the author").DataType("int")).
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.POST("/search").
		To(service.searchAuthors).
		Doc("Search authors").
		Operation("searchAuthors").
		Returns(200, "OK", []models.Author{}))

	container.Add(ws)
}

func (service RestService) getBook(request *restful.Request, response *restful.Response) {
	bookID, _ := strconv.ParseUint(request.PathParameter("bookId"), 0, 32)
	log.Println("Requesting book ", bookID)
	result, err := service.dataStore.GetBook(uint(bookID))
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Book wasn't found")
	}
}

func (service RestService) getBooksByLibID(request *restful.Request, response *restful.Response) {
	libID := request.PathParameter("libId")
	log.Println("Get books by libId ", libID)
	result, err := service.dataStore.FindBooksByLibID(libID)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) downloadBook(request *restful.Request, response *restful.Response) {
	bookID, _ := strconv.ParseUint(request.PathParameter("bookId"), 0, 32)
	log.Println("Downloading book ", bookID)
	result, err := service.dataStore.GetBook(uint(bookID))
	if err == nil {
		authors := ""
		for _, a := range result.Authors {
			authors = authors + a.Name
		}
		outName := authors + " - "
		if result.SerNo != "" {
			if len(result.SerNo) == 1 {
				result.SerNo = "0" + result.SerNo
			}
			outName = outName + "[" + result.SerNo + "] "
		}
		outName = outName + result.Title + "." + result.Ext

		response.AddHeader("Content-Type", "application/octet-stream")
		response.AddHeader("Content-disposition", "attachment; filename*=UTF-8''"+strings.Replace(url.QueryEscape(
			utils.ReplaceUnsupported(outName)), "+", "%20", -1))

		err := inpx.UnzipBookToWriter(service.dataDir, result, response)
		if err != nil {
			response.AddHeader("Content-Type", "text/plain")
			response.WriteErrorString(http.StatusNotFound, "Book wasn't found")
		}
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Book wasn't found")
	}
}

func (service RestService) searchBooks(request *restful.Request, response *restful.Response) {
	search := models.Search{}
	request.ReadEntity(&search)
	log.Println("Searching books ", search)

	result, err := service.dataStore.FindBooks(search)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) searchSeries(request *restful.Request, response *restful.Response) {
	search := models.Search{}
	request.ReadEntity(&search)
	log.Println("Searching books ", search)

	result, err := service.dataStore.FindBooks(search)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) getLangs(request *restful.Request, response *restful.Response) {
	log.Println("Getting languages")

	result, err := service.dataStore.GetLangs()
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) searchAuthors(request *restful.Request, response *restful.Response) {
	search := models.Search{}
	request.ReadEntity(&search)
	log.Println("Searching authors ", search)

	result, err := service.dataStore.FindAuthors(search.Author, search.Limit)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) getAuthor(request *restful.Request, response *restful.Response) {
	authorId, _ := strconv.ParseUint(request.PathParameter("authorId"), 0, 32)
	log.Println("Requesting author ", authorId)

	result, err := service.dataStore.GetAuthor(uint(authorId))
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "No author was found")
	}
}

func (service RestService) listAuthorsBooks(request *restful.Request, response *restful.Response) {
	authorId, _ := strconv.ParseUint(request.PathParameter("authorId"), 0, 32)
	noDetails, _ := utils.ParseBool(request.QueryParameter("no-details"))

	log.Println("Requesting author's books ", authorId)

	result, err := service.dataStore.ListAuthorBooks(uint(authorId), noDetails, models.Search{})
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "No books was found")
	}
}

func (service RestService) listAuthorsBooksPost(request *restful.Request, response *restful.Response) {
	authorId, _ := strconv.ParseUint(request.PathParameter("authorId"), 0, 32)
	noDetails, _ := utils.ParseBool(request.QueryParameter("no-details"))
	search := models.Search{}
	request.ReadEntity(&search)

	log.Println("Requesting author's books ", authorId)

	result, err := service.dataStore.ListAuthorBooks(uint(authorId), noDetails, search)
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "No books was found")
	}
}

func (service RestService) StartListen() {
	log.Println("Start listening on ", service.listen)
	server := &http.Server{Addr: service.listen, Handler: service.container}
	log.Fatal(server.ListenAndServe())
}

func NewRestService(listen string, dataStore datastore.DataStorer, dataDir string) *RestService {
	service := new(RestService)
	service.listen = listen
	service.dataStore = dataStore
	service.dataDir = dataDir
	service.container = restful.NewContainer()
	service.container.Router(restful.CurlyRouter{})

	service.registerBookResource(service.container)
	service.registerAuthorResource(service.container)

	return service
}
<commit_msg>Fix series search query.<commit_after>package rest

import (
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/alxeg/flibooks/datastore"
	"github.com/alxeg/flibooks/inpx"
	"github.com/alxeg/flibooks/models"
	"github.com/alxeg/flibooks/utils"
	"github.com/emicklei/go-restful"
)

type RestService struct {
	listen    string
	dataDir   string
	dataStore datastore.DataStorer
	container *restful.Container
}

func (service RestService) registerBookResource(container *restful.Container) {
	ws := new(restful.WebService)
	ws.
		Path("/book").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	ws.Route(ws.GET("/{bookId}").
		To(service.getBook).
		Doc("Get specific book info").
		Operation("getBook").
		Param(ws.PathParameter("bookId", "identifier of the book").DataType("int")).
		Returns(200, "OK", models.Book{}))

	ws.Route(ws.GET("/langs").
		To(service.getLangs).
		Doc("Get all available books languages").
		Operation("getLangs").
		Returns(200, "OK", []string{"en"}))

	ws.Route(ws.GET("/{bookId}/download").
		To(service.downloadBook).
		Doc("Download book content").
		Operation("downloadBook").
		Param(ws.PathParameter("bookId", "identifier of the book").DataType("int")).
		Returns(200, "OK", models.Book{}))

	ws.Route(ws.POST("/search").
		To(service.searchBooks).
		Doc("Search for the books").
		Operation("searchBooks").
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.POST("/series").
		To(service.searchSeries).
		Doc("Search for the books").
		Operation("searchBooks").
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.GET("/lib/{libId}").
		To(service.getBooksByLibID).
		Doc("Get books by libId").
		Operation("getBooksByLibId").
		Param(ws.PathParameter("libId", "libId of the book").DataType("string")).
		Returns(200, "OK", []models.Book{}))

	container.Add(ws)
}

func (service RestService) registerAuthorResource(container *restful.Container) {
	ws := new(restful.WebService)
	ws.
		Path("/author").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	ws.Route(ws.GET("/{authorId}").
		To(service.getAuthor).
		Doc("Get author's info").
		Operation("getAuthor").
		Param(ws.PathParameter("authorId", "identifier of the author").DataType("int")).
		Returns(200, "OK", models.Author{}))

	ws.Route(ws.GET("/{authorId}/books").
		To(service.listAuthorsBooks).
		Doc("Show author's books").
		Operation("listAuthorsBooks").
		Param(ws.PathParameter("authorId", "identifier of the author").DataType("int")).
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.POST("/{authorId}/books").
		To(service.listAuthorsBooksPost).
		Doc("Show author's books").
		Operation("listAuthorsBooks").
		Param(ws.PathParameter("authorId", "identifier of the author").DataType("int")).
		Returns(200, "OK", []models.Book{}))

	ws.Route(ws.POST("/search").
		To(service.searchAuthors).
		Doc("Search authors").
		Operation("searchAuthors").
		Returns(200, "OK", []models.Author{}))

	container.Add(ws)
}

func (service RestService) getBook(request *restful.Request, response *restful.Response) {
	bookID, _ := strconv.ParseUint(request.PathParameter("bookId"), 0, 32)
	log.Println("Requesting book ", bookID)
	result, err := service.dataStore.GetBook(uint(bookID))
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Book wasn't found")
	}
}

func (service RestService) getBooksByLibID(request *restful.Request, response *restful.Response) {
	libID := request.PathParameter("libId")
	log.Println("Get books by libId ", libID)
	result, err := service.dataStore.FindBooksByLibID(libID)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) downloadBook(request *restful.Request, response *restful.Response) {
	bookID, _ := strconv.ParseUint(request.PathParameter("bookId"), 0, 32)
	log.Println("Downloading book ", bookID)
	result, err := service.dataStore.GetBook(uint(bookID))
	if err == nil {
		authors := ""
		for _, a := range result.Authors {
			authors = authors + a.Name
		}
		outName := authors + " - "
		if result.SerNo != "" {
			if len(result.SerNo) == 1 {
				result.SerNo = "0" + result.SerNo
			}
			outName = outName + "[" + result.SerNo + "] "
		}
		outName = outName + result.Title + "." + result.Ext

		response.AddHeader("Content-Type", "application/octet-stream")
		response.AddHeader("Content-disposition", "attachment; filename*=UTF-8''"+strings.Replace(url.QueryEscape(
			utils.ReplaceUnsupported(outName)), "+", "%20", -1))

		err := inpx.UnzipBookToWriter(service.dataDir, result, response)
		if err != nil {
			response.AddHeader("Content-Type", "text/plain")
			response.WriteErrorString(http.StatusNotFound, "Book wasn't found")
		}
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Book wasn't found")
	}
}

func (service RestService) searchBooks(request *restful.Request, response *restful.Response) {
	search := models.Search{}
	request.ReadEntity(&search)
	log.Println("Searching books ", search)

	result, err := service.dataStore.FindBooks(search)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) searchSeries(request *restful.Request, response *restful.Response) {
	search := models.Search{}
	request.ReadEntity(&search)
	log.Println("Searching book series ", search)

	result, err := service.dataStore.FindBooksSeries(search)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) getLangs(request *restful.Request, response *restful.Response) {
	log.Println("Getting languages")

	result, err := service.dataStore.GetLangs()
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) searchAuthors(request *restful.Request, response *restful.Response) {
	search := models.Search{}
	request.ReadEntity(&search)
	log.Println("Searching authors ", search)

	result, err := service.dataStore.FindAuthors(search.Author, search.Limit)
	if err == nil && len(result) != 0 {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "Nothing was found")
	}
}

func (service RestService) getAuthor(request *restful.Request, response *restful.Response) {
	authorId, _ := strconv.ParseUint(request.PathParameter("authorId"), 0, 32)
	log.Println("Requesting author ", authorId)

	result, err := service.dataStore.GetAuthor(uint(authorId))
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "No author was found")
	}
}

func (service RestService) listAuthorsBooks(request *restful.Request, response *restful.Response) {
	authorId, _ := strconv.ParseUint(request.PathParameter("authorId"), 0, 32)
	noDetails, _ := utils.ParseBool(request.QueryParameter("no-details"))

	log.Println("Requesting author's books ", authorId)

	result, err := service.dataStore.ListAuthorBooks(uint(authorId), noDetails, models.Search{})
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "No books was found")
	}
}

func (service RestService) listAuthorsBooksPost(request *restful.Request, response *restful.Response) {
	authorId, _ := strconv.ParseUint(request.PathParameter("authorId"), 0, 32)
	noDetails, _ := utils.ParseBool(request.QueryParameter("no-details"))
	search := models.Search{}
	request.ReadEntity(&search)

	log.Println("Requesting author's books ", authorId)

	result, err := service.dataStore.ListAuthorBooks(uint(authorId), noDetails, search)
	if err == nil {
		response.WriteEntity(result)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "No books was found")
	}
}

func (service RestService) StartListen() {
	log.Println("Start listening on ", service.listen)
	server := &http.Server{Addr: service.listen, Handler: service.container}
	log.Fatal(server.ListenAndServe())
}

func NewRestService(listen string, dataStore datastore.DataStorer, dataDir string) *RestService {
	service := new(RestService)
	service.listen = listen
	service.dataStore = dataStore
	service.dataDir = dataDir
	service.container = restful.NewContainer()
	service.container.Router(restful.CurlyRouter{})

	service.registerBookResource(service.container)
	service.registerAuthorResource(service.container)

	return service
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"flag"
	"fmt"

	"github.com/smallnest/rpcx/protocol"
	"github.com/smallnest/rpcx/share"

	example "github.com/rpcx-ecosystem/rpcx-examples3"
	"github.com/smallnest/rpcx/server"
)

var (
	addr = flag.String("addr", "localhost:8972", "server address")
)

func main() {
	flag.Parse()

	share.Codecs[protocol.SerializeType(4)] = &GobCodec{}
	s := server.NewServer()
	//s.RegisterName("Arith", new(example.Arith), "")
	s.Register(new(example.Arith), "")
	s.Serve("tcp", *addr)
}

type GobCodec struct {
}

func (c *GobCodec) Decode(data []byte, i interface{}) error {)
	enc := gob.NewDecoder(bytes.NewBuffer(data))
	err := enc.Decode(i)
	return err
}

func (c *GobCodec) Encode(i interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(i)
	return buf.Bytes(), err
}
<commit_msg>fix typo<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"flag"

	"github.com/smallnest/rpcx/protocol"
	"github.com/smallnest/rpcx/share"

	example "github.com/rpcx-ecosystem/rpcx-examples3"
	"github.com/smallnest/rpcx/server"
)

var (
	addr = flag.String("addr", "localhost:8972", "server address")
)

func main() {
	flag.Parse()

	share.Codecs[protocol.SerializeType(4)] = &GobCodec{}
	s := server.NewServer()
	//s.RegisterName("Arith", new(example.Arith), "")
	s.Register(new(example.Arith), "")
	s.Serve("tcp", *addr)
}

type GobCodec struct {
}

func (c *GobCodec) Decode(data []byte, i interface{}) error {
	enc := gob.NewDecoder(bytes.NewBuffer(data))
	err := enc.Decode(i)
	return err
}

func (c *GobCodec) Encode(i interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(i)
	return buf.Bytes(), err
}
<|endoftext|>


<commit_before>package makeupdater

// Note this package is exclusively compatible with Drupal 7 make files.

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

// replaceTextInFile will replace a string of test in a file.
func replaceTextInFile(fullPath string, oldString string, newString string) {
	read, err := ioutil.ReadFile(fullPath)
	if err != nil {
		panic(err)
	}
	newContents := strings.Replace(string(read), oldString, newString, -1)
	err = ioutil.WriteFile(fullPath, []byte(newContents), 0)
	if err != nil {
		panic(err)
	}
}

// removeChar will remove particular characters from a string.
func removeChar(input string, chars ...string) string {
	for _, value := range chars {
		input = strings.Replace(input, value, "", -1)
	}
	return input
}

// inArray will return the quanity of specific input values in the input slice.
func inArray(input []string, subject string) int {
	counter := 0
	for _, value := range input {
		if value == subject {
			counter++
		}
	}
	return counter
}

// GetCoreFromMake will return the major core version used in the input make file.
func GetCoreFromMake(fullpath string) (int64, error) {
	command := fmt.Sprintf("cat %v | grep core", fullpath)
	catCmd := exec.Command("sh", "-c", command)
	cmdOut, cmdErr := catCmd.CombinedOutput()
	if cmdErr != nil {
		fmt.Errorf("Could not execute '%v'", command)
	}
	var majorVersion int64
	for _, line := range strings.Split(string(cmdOut), "\n") {
		line = strings.Replace(line, "core", "", -1)
		line = strings.Replace(line, "=", "", -1)
		line = strings.Replace(line, "\"", "", -1)
		line = strings.Replace(line, ".x", "", -1)
		line = strings.Replace(line, " ", "", -1)
		x, y := strconv.ParseInt(line, 0, 0)
		if y == nil {
			majorVersion = x
		}
	}
	if majorVersion != 0 {
		return majorVersion, nil
	} else {
		return majorVersion, errors.New("Could not identify a version associated to this make file.")
	}
}

// UpdateMake will update the version numbers in a specified make file
func UpdateMake(fullpath string) {
	_, err := os.Stat(fullpath)
	if err != nil {
		panic(err)
	}
	projects := GetProjectsFromMake(fullpath)
	count := 0
	wg := sync.WaitGroup{}
	for _, project := range projects {
		func(project string) {
			majorVersion, _ := GetCoreFromMake(fullpath)
			majorVersionString := fmt.Sprintf("%v", majorVersion)
			if project != "" {
				wg.Add(1)
				catCmd := "cat " + fullpath + " | grep \"projects\\[" + project + "\\]\" | grep version | cut -d '=' -f2"
				z, _ := exec.Command("sh", "-c", catCmd).Output()
				for _, stream := range strings.Split(string(z), "\n") {
					if !strings.Contains(stream, "x-dev") {
						stream = strings.Replace(stream, "\"", "", -1)
						stream = strings.Replace(stream, " ", "", -1)
						if stream != "" {
							command := "drush pm-releases --default-major=" + majorVersionString + " --pipe " + project + " | grep .x- | grep Recommended | cut -d',' -f2"
							x, _ := exec.Command("sh", "-c", command).CombinedOutput()
							for _, Line := range strings.Split(string(x), "\n") {
								if strings.Contains(Line, ".x-") {
									versionNew := removeChar(Line, " ", "5.x-", "6.x-", "7.x-", "8.x-", "\"", "\n", "\t", "[", "]")
									if len(versionNew) < 15 {
										if fmt.Sprintf("%v", versionNew) != "" && stream != versionNew {
											fmt.Printf("Replacing %v v%v with v%v\n", project, stream, versionNew)
											replaceTextInFile(fullpath, fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, stream), fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionNew))
											replaceTextInFile(fullpath, fmt.Sprintf("projects[%v][version] = %v\n", project, stream), fmt.Sprintf("projects[%v][version] = %v\n", project, versionNew))
											count++
										}
									} else {
										fmt.Printf("There was an error in checking version numbers for %v, please check it manually.\n", project)
									}
								}
							}
						}
					} else {
						fmt.Printf("Project %v is using development version, do not use in production!\n", project)
					}
				}

			}
			wg.Done()
		}(project)
	}
	wg.Wait()
	if count == 0 {
		fmt.Printf("%v is already up to date.\n", fullpath)
	} else {
		fmt.Printf("Updated %v projects in %v.\n", count, fullpath)
	}
}

// FindDuplicatesInMake will find and report Duplicate projects in Drupal make files.
// It will not return a value.
func FindDuplicatesInMake(makefile string) {
	projects := GetProjectsFromMake(makefile)
	// Run a short report containing information on all duplicates.
	for _, project := range projects {
		projectCounter := 0
		if project != "" {
			catCmd := "cat " + makefile + " | grep \"projects\\[" + project + "\\]\" | grep version | cut -d '=' -f2"
			z, _ := exec.Command("sh", "-c", catCmd).Output()
			for _, stream := range strings.Split(string(z), "\n") {
				if stream != "" {
					projectCounter++
				}
			}
			if projectCounter > 1 {
				fmt.Printf("Found %v instances of project %v\n", projectCounter, project)
			}
		}
	}
}

// GetProjectsFromMake returns a list of projects from a given make file
func GetProjectsFromMake(fullpath string) []string {
	Projects := []string{}
	catCmd := "cat " + fullpath + " | grep projects | cut -d'[' -f2 | cut -d']' -f1 | uniq | sort"
	y, _ := exec.Command("sh", "-c", catCmd).Output()
	rawProjects := strings.Split(string(y), "\n")
	for _, project := range rawProjects {
		project = strings.Replace(project, " ", "", -1)
		if project != "" && project != "projects" {
			if inArray(Projects, project) == 0 {
				Projects = append(Projects, project)
			}
		}
	}
	return Projects
}

// GenerateMake takes a []string of projects and writes out a make file
// Modules are added with the latest recommended version.
func GenerateMake(Projects []string, File string) {
	headerLines := []string{}

	majorVersion, _ := GetCoreFromMake(File)
	majorVersionString := fmt.Sprintf("%v", majorVersion)

	headerLines = append(headerLines, "; Generated by make-updater")
	headerLines = append(headerLines, "; Script created by Fubarhouse")
	headerLines = append(headerLines, "; Toolkit available at github.com/fubarhouse/golang-drush/...")
	headerLines = append(headerLines, "core = "+majorVersionString+".x")
	headerLines = append(headerLines, "api = 2")
	headerLines = append(headerLines, "")

	// Rewrite core, if core is in the original Projects list.

	for _, Project := range Projects {
		if Project == "drupal" {
			headerLines = append(headerLines, "; core")
			x, _ := exec.Command("sh", "-c", "drush pm-releases --default-major="+majorVersionString+" --pipe drupal | grep Recommended | cut -d',' -f2").Output()
			ProjectVersion := removeChar(string(x), " ", "5.x-", "6.x-", "7.x-", "8.x-", "\"", "\n", "[", "]")
			headerLines = append(headerLines, "projects[drupal][type] = \"core\"")
			headerLines = append(headerLines, fmt.Sprintf("projects[drupal][version] = \"%v\"", ProjectVersion))
			headerLines = append(headerLines, "projects[drupal][download][type] = \"get\"")
			headerLines = append(headerLines, fmt.Sprintf("projects[drupal][download][url] = \"https://ftp.drupal.org/files/projects/drupal-%v.tar.gz\"", ProjectVersion))
			headerLines = append(headerLines, "")
		}
	}

	// Rewrite contrib
	headerLines = append(headerLines, "; modules")
	headerLines = append(headerLines, "defaults[projects][subdir] = contrib")
	headerLines = append(headerLines, "")

	for _, Project := range Projects {

		if Project != "drupal" {
			x, y := exec.Command("sh", "-c", "drush pm-releases --default-major="+majorVersionString+" --pipe "+Project+" | grep Recommended | cut -d',' -f2").Output()
			if y == nil {
				ProjectVersion := removeChar(string(x), " ", "5.x-", "6.x-", "7.x-", "8.x-", "\"", "\n", "[", "]")
				ProjectType := "contrib"
				if ProjectVersion == "" {
					ProjectType = "custom"
				}
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][version] = \"%v\"", Project, ProjectVersion))
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][type] = \"module\"", Project))
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][subdir] = \"%v\"", Project, ProjectType))
				headerLines = append(headerLines, fmt.Sprint(""))
			}
		}
	}

	// Print to path File

	newFile, _ := os.Create(File)
	for _, line := range headerLines {
		fmt.Fprintln(newFile, line)
	}
	newFile.Sync()
	defer newFile.Close()

}
<commit_msg>Prevent the make file from resulting in non-quoted version numbers (eg 7.10 instead of "7.10" as expected)<commit_after>package makeupdater

// Note this package is exclusively compatible with Drupal 7 make files.

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

// replaceTextInFile will replace a string of test in a file.
func replaceTextInFile(fullPath string, oldString string, newString string) {
	read, err := ioutil.ReadFile(fullPath)
	if err != nil {
		panic(err)
	}
	newContents := strings.Replace(string(read), oldString, newString, -1)
	err = ioutil.WriteFile(fullPath, []byte(newContents), 0)
	if err != nil {
		panic(err)
	}
}

// removeChar will remove particular characters from a string.
func removeChar(input string, chars ...string) string {
	for _, value := range chars {
		input = strings.Replace(input, value, "", -1)
	}
	return input
}

// inArray will return the quanity of specific input values in the input slice.
func inArray(input []string, subject string) int {
	counter := 0
	for _, value := range input {
		if value == subject {
			counter++
		}
	}
	return counter
}

// GetCoreFromMake will return the major core version used in the input make file.
func GetCoreFromMake(fullpath string) (int64, error) {
	command := fmt.Sprintf("cat %v | grep core", fullpath)
	catCmd := exec.Command("sh", "-c", command)
	cmdOut, cmdErr := catCmd.CombinedOutput()
	if cmdErr != nil {
		fmt.Errorf("Could not execute '%v'", command)
	}
	var majorVersion int64
	for _, line := range strings.Split(string(cmdOut), "\n") {
		line = strings.Replace(line, "core", "", -1)
		line = strings.Replace(line, "=", "", -1)
		line = strings.Replace(line, "\"", "", -1)
		line = strings.Replace(line, ".x", "", -1)
		line = strings.Replace(line, " ", "", -1)
		x, y := strconv.ParseInt(line, 0, 0)
		if y == nil {
			majorVersion = x
		}
	}
	if majorVersion != 0 {
		return majorVersion, nil
	} else {
		return majorVersion, errors.New("Could not identify a version associated to this make file.")
	}
}

// UpdateMake will update the version numbers in a specified make file
func UpdateMake(fullpath string) {
	_, err := os.Stat(fullpath)
	if err != nil {
		panic(err)
	}
	projects := GetProjectsFromMake(fullpath)
	count := 0
	wg := sync.WaitGroup{}
	for _, project := range projects {
		func(project string) {
			majorVersion, _ := GetCoreFromMake(fullpath)
			majorVersionString := fmt.Sprintf("%v", majorVersion)
			if project != "" {
				wg.Add(1)
				catCmd := "cat " + fullpath + " | grep \"projects\\[" + project + "\\]\" | grep version | cut -d '=' -f2"
				z, _ := exec.Command("sh", "-c", catCmd).Output()
				for _, stream := range strings.Split(string(z), "\n") {
					if !strings.Contains(stream, "x-dev") {
						stream = strings.Replace(stream, "\"", "", -1)
						stream = strings.Replace(stream, " ", "", -1)
						if stream != "" {
							command := "drush pm-releases --default-major=" + majorVersionString + " --pipe " + project + " | grep .x- | grep Recommended | cut -d',' -f2"
							x, _ := exec.Command("sh", "-c", command).CombinedOutput()
							for _, Line := range strings.Split(string(x), "\n") {
								if strings.Contains(Line, ".x-") {
									versionNew := removeChar(Line, " ", "5.x-", "6.x-", "7.x-", "8.x-", "\"", "\n", "\t", "[", "]")
									if len(versionNew) < 15 {
										if fmt.Sprintf("%v", versionNew) != "" && stream != versionNew {
											fmt.Printf("Replacing %v v%v with v%v\n", project, stream, versionNew)
											replaceTextInFile(fullpath, fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, stream), fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionNew))
											replaceTextInFile(fullpath, fmt.Sprintf("projects[%v][version] = %v\n", project, stream), fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionNew))
											count++
										}
									} else {
										fmt.Printf("There was an error in checking version numbers for %v, please check it manually.\n", project)
									}
								}
							}
						}
					} else {
						fmt.Printf("Project %v is using development version, do not use in production!\n", project)
					}
				}

			}
			wg.Done()
		}(project)
	}
	wg.Wait()
	if count == 0 {
		fmt.Printf("%v is already up to date.\n", fullpath)
	} else {
		fmt.Printf("Updated %v projects in %v.\n", count, fullpath)
	}
}

// FindDuplicatesInMake will find and report Duplicate projects in Drupal make files.
// It will not return a value.
func FindDuplicatesInMake(makefile string) {
	projects := GetProjectsFromMake(makefile)
	// Run a short report containing information on all duplicates.
	for _, project := range projects {
		projectCounter := 0
		if project != "" {
			catCmd := "cat " + makefile + " | grep \"projects\\[" + project + "\\]\" | grep version | cut -d '=' -f2"
			z, _ := exec.Command("sh", "-c", catCmd).Output()
			for _, stream := range strings.Split(string(z), "\n") {
				if stream != "" {
					projectCounter++
				}
			}
			if projectCounter > 1 {
				fmt.Printf("Found %v instances of project %v\n", projectCounter, project)
			}
		}
	}
}

// GetProjectsFromMake returns a list of projects from a given make file
func GetProjectsFromMake(fullpath string) []string {
	Projects := []string{}
	catCmd := "cat " + fullpath + " | grep projects | cut -d'[' -f2 | cut -d']' -f1 | uniq | sort"
	y, _ := exec.Command("sh", "-c", catCmd).Output()
	rawProjects := strings.Split(string(y), "\n")
	for _, project := range rawProjects {
		project = strings.Replace(project, " ", "", -1)
		if project != "" && project != "projects" {
			if inArray(Projects, project) == 0 {
				Projects = append(Projects, project)
			}
		}
	}
	return Projects
}

// GenerateMake takes a []string of projects and writes out a make file
// Modules are added with the latest recommended version.
func GenerateMake(Projects []string, File string) {
	headerLines := []string{}

	majorVersion, _ := GetCoreFromMake(File)
	majorVersionString := fmt.Sprintf("%v", majorVersion)

	headerLines = append(headerLines, "; Generated by make-updater")
	headerLines = append(headerLines, "; Script created by Fubarhouse")
	headerLines = append(headerLines, "; Toolkit available at github.com/fubarhouse/golang-drush/...")
	headerLines = append(headerLines, "core = "+majorVersionString+".x")
	headerLines = append(headerLines, "api = 2")
	headerLines = append(headerLines, "")

	// Rewrite core, if core is in the original Projects list.

	for _, Project := range Projects {
		if Project == "drupal" {
			headerLines = append(headerLines, "; core")
			x, _ := exec.Command("sh", "-c", "drush pm-releases --default-major="+majorVersionString+" --pipe drupal | grep Recommended | cut -d',' -f2").Output()
			ProjectVersion := removeChar(string(x), " ", "5.x-", "6.x-", "7.x-", "8.x-", "\"", "\n", "[", "]")
			headerLines = append(headerLines, "projects[drupal][type] = \"core\"")
			headerLines = append(headerLines, fmt.Sprintf("projects[drupal][version] = \"%v\"", ProjectVersion))
			headerLines = append(headerLines, "projects[drupal][download][type] = \"get\"")
			headerLines = append(headerLines, fmt.Sprintf("projects[drupal][download][url] = \"https://ftp.drupal.org/files/projects/drupal-%v.tar.gz\"", ProjectVersion))
			headerLines = append(headerLines, "")
		}
	}

	// Rewrite contrib
	headerLines = append(headerLines, "; modules")
	headerLines = append(headerLines, "defaults[projects][subdir] = contrib")
	headerLines = append(headerLines, "")

	for _, Project := range Projects {

		if Project != "drupal" {
			x, y := exec.Command("sh", "-c", "drush pm-releases --default-major="+majorVersionString+" --pipe "+Project+" | grep Recommended | cut -d',' -f2").Output()
			if y == nil {
				ProjectVersion := removeChar(string(x), " ", "5.x-", "6.x-", "7.x-", "8.x-", "\"", "\n", "[", "]")
				ProjectType := "contrib"
				if ProjectVersion == "" {
					ProjectType = "custom"
				}
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][version] = \"%v\"", Project, ProjectVersion))
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][type] = \"module\"", Project))
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][subdir] = \"%v\"", Project, ProjectType))
				headerLines = append(headerLines, fmt.Sprint(""))
			}
		}
	}

	// Print to path File

	newFile, _ := os.Create(File)
	for _, line := range headerLines {
		fmt.Fprintln(newFile, line)
	}
	newFile.Sync()
	defer newFile.Close()

}
<|endoftext|>