<commit_before>// Version 1.82
// Supports Windows, Linux, Mac, and Raspberry Pi, Beagle Bone Black

package main

import (
	"flag"
	"fmt"
	"go/build"
	"log"
	"net/http"
	"os"
	"path/filepath"
	//"net/http/pprof"
	"github.com/kardianos/osext"
	//"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily
	//"github.com/sanderhahn/gozip"
	"github.com/vharitonsky/iniflags"
	"runtime/debug"
	"text/template"
	"time"
)

var (
	version      = "1.83"
	versionFloat = float32(1.83)
	addr         = flag.String("addr", ":8989", "http service address")
	addrSSL      = flag.String("addrSSL", ":8990", "https service address")
	//assets       = flag.String("assets", defaultAssetPath(), "path to assets")
	verbose = flag.Bool("v", true, "show debug logging")
	//verbose = flag.Bool("v", false, "show debug logging")
	//homeTempl *template.Template
	isLaunchSelf = flag.Bool("ls", false, "launch self 5 seconds later")

	configIni = flag.String("configFile", "config.ini", "config file path")
	// regular expression to sort the serial port list
	// typically this wouldn't be provided, but if the user wants to clean
	// up their list with a regexp so it's cleaner inside their end-user interface
	// such as ChiliPeppr, this can make the massive list that Linux gives back
	// to you be a bit more manageable
	regExpFilter = flag.String("regex", "usb|acm|com", "Regular expression to filter serial port list")

	// allow garbageCollection()
	//isGC = flag.Bool("gc", false, "Is garbage collection on? Off by default.")
	//isGC = flag.Bool("gc", true, "Is garbage collection on? Off by default.")
	gcType = flag.String("gc", "std", "Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage)")

	// whether to do buffer flow debugging
	bufFlowDebugType = flag.String("bufflowdebug", "off", "off = (default) We do not send back any debug JSON, on = We will send back a JSON response with debug info based on the configuration of the buffer flow that the user picked")

	// hostname. allow user to override, otherwise we look it up
	hostname = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")

	updateUrl = flag.String("updateUrl", "", "")
	appName   = flag.String("appName", "", "")
)

var globalConfigMap map[string]interface{}

type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

func defaultAssetPath() string {
	//p, err := build.Default.Import("gary.burd.info/go-websocket-chat", "", build.FindOnly)
	p, err := build.Default.Import("github.com/johnlauer/serial-port-json-server", "", build.FindOnly)
	if err != nil {
		return "."
	}
	return p.Dir
}

func homeHandler(c http.ResponseWriter, req *http.Request) {
	homeTemplate.Execute(c, req.Host)
}

func launchSelfLater() {
	log.Println("Going to launch myself 5 seconds later.")
	time.Sleep(2 * 1000 * time.Millisecond)
	log.Println("Done waiting 5 secs. Now launching...")
}

func main() {

	go func() {

		// autoextract self
		src, _ := osext.Executable()
		dest := filepath.Dir(src)

		// save the config.ini (if it exists)
		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			fmt.Println("First run, unzipping self")
			err := Unzip(src, dest)
			fmt.Println("Self extraction, err:", err)
		}

		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			flag.Parse()
			fmt.Println("No config.ini at", *configIni)
		} else {
			flag.Set("config", dest+"/"+*configIni)
			iniflags.Parse()
		}

		// setup logging
		log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

		// see if we are supposed to wait 5 seconds
		if *isLaunchSelf {
			launchSelfLater()
		}

		var updater = &Updater{
			CurrentVersion: version,
			ApiURL:         *updateUrl,
			BinURL:         *updateUrl,
			DiffURL:        "",
			Dir:            "update/",
			CmdName:        *appName,
		}

		if updater != nil {
			go updater.BackgroundRun()
		}

		// data, err := Asset("arduino.zip")
		// if err != nil {
		// 	log.Println("arduino tools not found")
		// }

		createGlobalConfigMap(&globalConfigMap)

		//getList()
		f := flag.Lookup("addr")
		log.Println("Version:" + version)

		// hostname
		hn, _ := os.Hostname()
		if *hostname == "unknown-hostname" {
			*hostname = hn
		}
		log.Println("Hostname:", *hostname)

		// turn off garbage collection
		// this is dangerous, as u could overflow memory
		//if *isGC {
		if *gcType == "std" {
			log.Println("Garbage collection is on using Standard mode, meaning we just let Golang determine when to garbage collect.")
		} else if *gcType == "max" {
			log.Println("Garbage collection is on for MAXIMUM real-time collecting on each send/recv from serial port. Higher CPU, but less stopping of the world to garbage collect since it is being done on a constant basis.")
		} else {
			log.Println("Garbage collection is off. Memory use will grow unbounded. You WILL RUN OUT OF RAM unless you send in the gc command to manually force garbage collection. Lower CPU, but progressive memory footprint.")
			debug.SetGCPercent(-1)
		}

		ip := "0.0.0.0"
		log.Print("Starting server and websocket on " + ip + "" + f.Value.String())
		//homeTempl = template.Must(template.ParseFiles(filepath.Join(*assets, "home.html")))

		log.Println("The Serial Port JSON Server is now running.")
		log.Println("If you are using ChiliPeppr, you may go back to it and connect to this server.")

		// see if they provided a regex filter
		if len(*regExpFilter) > 0 {
			log.Printf("You specified a serial port regular expression filter: %v\n", *regExpFilter)
		}

		// list serial ports
		portList, _ := GetList(false)
		/*if errSys != nil {
			log.Printf("Got system error trying to retrieve serial port list. Err:%v\n", errSys)
			log.Fatal("Exiting")
		}*/
		log.Println("Your serial ports:")
		if len(portList) == 0 {
			log.Println("\tThere are no serial ports to list.")
		}
		for _, element := range portList {
			log.Printf("\t%v\n", element)

		}

		if !*verbose {
			log.Println("You can enter verbose mode to see all logging by starting with the -v command line switch.")
			log.SetOutput(new(NullWriter)) //route all logging to nullwriter
		}

		// launch the hub routine which is the singleton for the websocket server
		go h.run()
		// launch our serial port routine
		go sh.run()
		// launch our dummy data routine
		//go d.run()

		go func() {
			http.HandleFunc("/", homeHandler)
			http.HandleFunc("/ws", wsHandler)
			http.HandleFunc("/upload", uploadHandler)

			if err := http.ListenAndServeTLS(*addrSSL, filepath.Join(dest, "cert.pem"), filepath.Join(dest, "key.pem"), nil); err != nil {
				fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
				log.Fatal("Error ListenAndServe:", err)
			}
		}()

		if err := http.ListenAndServe(*addr, nil); err != nil {
			fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
			log.Fatal("Error ListenAndServe:", err)
		}
	}()
	setupSysTray()
}

var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHtml))

// If you navigate to this server's homepage, you'll get this HTML
// so you can directly interact with the serial port server
const homeTemplateHtml = `<!DOCTYPE html>

<title>Serial Port Example</title>

<form id="form">
    <input type="submit" value="Send" />
    <input type="text" id="msg" size="64"/>
</form>

`
<commit_msg>Added deamon capabilities<commit_after>// Version 1.82
// Supports Windows, Linux, Mac, and Raspberry Pi, Beagle Bone Black

package main

import (
	"flag"
	"fmt"
	"go/build"
	"log"
	"net/http"
	"os"
	"path/filepath"
	//"net/http/pprof"
	"github.com/kardianos/osext"
	//"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily
	//"github.com/sanderhahn/gozip"
	"runtime/debug"
	"text/template"
	"time"

	"github.com/kardianos/service"

	"github.com/vharitonsky/iniflags"
)

var (
	version      = "1.83"
	versionFloat = float32(1.83)
	addr         = flag.String("addr", ":8989", "http service address")
	addrSSL      = flag.String("addrSSL", ":8990", "https service address")
	//assets       = flag.String("assets", defaultAssetPath(), "path to assets")
	verbose = flag.Bool("v", true, "show debug logging")
	//verbose = flag.Bool("v", false, "show debug logging")
	//homeTempl *template.Template
	isLaunchSelf = flag.Bool("ls", false, "launch self 5 seconds later")

	configIni = flag.String("configFile", "config.ini", "config file path")
	// regular expression to sort the serial port list
	// typically this wouldn't be provided, but if the user wants to clean
	// up their list with a regexp so it's cleaner inside their end-user interface
	// such as ChiliPeppr, this can make the massive list that Linux gives back
	// to you be a bit more manageable
	regExpFilter = flag.String("regex", "usb|acm|com", "Regular expression to filter serial port list")

	// allow garbageCollection()
	//isGC = flag.Bool("gc", false, "Is garbage collection on? Off by default.")
	//isGC = flag.Bool("gc", true, "Is garbage collection on? Off by default.")
	gcType = flag.String("gc", "std", "Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage)")

	// whether to do buffer flow debugging
	bufFlowDebugType = flag.String("bufflowdebug", "off", "off = (default) We do not send back any debug JSON, on = We will send back a JSON response with debug info based on the configuration of the buffer flow that the user picked")

	// hostname. allow user to override, otherwise we look it up
	hostname = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")

	updateUrl = flag.String("updateUrl", "", "")
	appName   = flag.String("appName", "", "")
)

var globalConfigMap map[string]interface{}

type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

func defaultAssetPath() string {
	//p, err := build.Default.Import("gary.burd.info/go-websocket-chat", "", build.FindOnly)
	p, err := build.Default.Import("github.com/johnlauer/serial-port-json-server", "", build.FindOnly)
	if err != nil {
		return "."
	}
	return p.Dir
}

func homeHandler(c http.ResponseWriter, req *http.Request) {
	homeTemplate.Execute(c, req.Host)
}

func launchSelfLater() {
	log.Println("Going to launch myself 5 seconds later.")
	time.Sleep(2 * 1000 * time.Millisecond)
	log.Println("Done waiting 5 secs. Now launching...")
}

var logger service.Logger

type program struct{}

func (p *program) Start(s service.Service) error {
	// Start should not block. Do the actual work async.
	go p.run()
	return nil
}
func (p *program) run() {
	startDaemon()
}
func (p *program) Stop(s service.Service) error {
	// Stop should not block. Return with a few seconds.
	<-time.After(time.Second * 13)
	return nil
}

func main() {
	svcConfig := &service.Config{
		Name:        "ArduinoCreateBridge",
		DisplayName: "Arduino Create Bridge",
		Description: "A bridge that allows Arduino Create to operate on the boards connected to the computer",
	}

	prg := &program{}
	s, err := service.New(prg, svcConfig)
	if err != nil {
		log.Fatal(err)
	}
	if len(os.Args) > 1 {
		err = service.Control(s, os.Args[1])
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	logger, err = s.Logger(nil)
	if err != nil {
		log.Fatal(err)
	}

	err = s.Install()
	if err != nil {
		logger.Error(err)
	}

	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}

func startDaemon() {
	go func() {

		// autoextract self
		src, _ := osext.Executable()
		dest := filepath.Dir(src)

		// save the config.ini (if it exists)
		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			fmt.Println("First run, unzipping self")
			err := Unzip(src, dest)
			fmt.Println("Self extraction, err:", err)
		}

		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			flag.Parse()
			fmt.Println("No config.ini at", *configIni)
		} else {
			flag.Set("config", dest+"/"+*configIni)
			iniflags.Parse()
		}

		// setup logging
		log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

		// see if we are supposed to wait 5 seconds
		if *isLaunchSelf {
			launchSelfLater()
		}

		var updater = &Updater{
			CurrentVersion: version,
			ApiURL:         *updateUrl,
			BinURL:         *updateUrl,
			DiffURL:        "",
			Dir:            "update/",
			CmdName:        *appName,
		}

		if updater != nil {
			go updater.BackgroundRun()
		}

		// data, err := Asset("arduino.zip")
		// if err != nil {
		// 	log.Println("arduino tools not found")
		// }

		createGlobalConfigMap(&globalConfigMap)

		//getList()
		f := flag.Lookup("addr")
		log.Println("Version:" + version)

		// hostname
		hn, _ := os.Hostname()
		if *hostname == "unknown-hostname" {
			*hostname = hn
		}
		log.Println("Hostname:", *hostname)

		// turn off garbage collection
		// this is dangerous, as u could overflow memory
		//if *isGC {
		if *gcType == "std" {
			log.Println("Garbage collection is on using Standard mode, meaning we just let Golang determine when to garbage collect.")
		} else if *gcType == "max" {
			log.Println("Garbage collection is on for MAXIMUM real-time collecting on each send/recv from serial port. Higher CPU, but less stopping of the world to garbage collect since it is being done on a constant basis.")
		} else {
			log.Println("Garbage collection is off. Memory use will grow unbounded. You WILL RUN OUT OF RAM unless you send in the gc command to manually force garbage collection. Lower CPU, but progressive memory footprint.")
			debug.SetGCPercent(-1)
		}

		ip := "0.0.0.0"
		log.Print("Starting server and websocket on " + ip + "" + f.Value.String())
		//homeTempl = template.Must(template.ParseFiles(filepath.Join(*assets, "home.html")))

		log.Println("The Serial Port JSON Server is now running.")
		log.Println("If you are using ChiliPeppr, you may go back to it and connect to this server.")

		// see if they provided a regex filter
		if len(*regExpFilter) > 0 {
			log.Printf("You specified a serial port regular expression filter: %v\n", *regExpFilter)
		}

		// list serial ports
		portList, _ := GetList(false)
		/*if errSys != nil {
			log.Printf("Got system error trying to retrieve serial port list. Err:%v\n", errSys)
			log.Fatal("Exiting")
		}*/
		log.Println("Your serial ports:")
		if len(portList) == 0 {
			log.Println("\tThere are no serial ports to list.")
		}
		for _, element := range portList {
			log.Printf("\t%v\n", element)

		}

		if !*verbose {
			log.Println("You can enter verbose mode to see all logging by starting with the -v command line switch.")
			log.SetOutput(new(NullWriter)) //route all logging to nullwriter
		}

		// launch the hub routine which is the singleton for the websocket server
		go h.run()
		// launch our serial port routine
		go sh.run()
		// launch our dummy data routine
		//go d.run()

		go func() {
			http.HandleFunc("/", homeHandler)
			http.HandleFunc("/ws", wsHandler)
			http.HandleFunc("/upload", uploadHandler)

			if err := http.ListenAndServeTLS(*addrSSL, filepath.Join(dest, "cert.pem"), filepath.Join(dest, "key.pem"), nil); err != nil {
				fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
				log.Fatal("Error ListenAndServe:", err)
			}
		}()

		if err := http.ListenAndServe(*addr, nil); err != nil {
			fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
			log.Fatal("Error ListenAndServe:", err)
		}
	}()
	setupSysTray()
}

var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHtml))

// If you navigate to this server's homepage, you'll get this HTML
// so you can directly interact with the serial port server
const homeTemplateHtml = `<!DOCTYPE html>

<title>Serial Port Example</title>

<form id="form">
    <input type="submit" value="Send" />
    <input type="text" id="msg" size="64"/>
</form>

`
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"github.com/UniversityRadioYork/bifrost-go"
	"github.com/UniversityRadioYork/bifrost-server/request"
	"github.com/UniversityRadioYork/bifrost-server/tcpserver"
	//"github.com/docopt/docopt-go"
	_ "github.com/lib/pq"
)

var hostport = flag.String("hostport", "localhost:8123", "The host and port on which trackd should listen (host:port).")
var resolver = flag.String("resolver", "resolve", "The two-argument command to which trackids will be sent on stdin.")

func resolve(recordid, trackid string) (out string, err error) {
	cmd := exec.Command(*resolver, recordid, trackid)

	var outb []byte
	outb, err = cmd.Output()
	out = string(outb)

	return
}

func main() {
	flag.Parse()

	sample, serr := resolve("recordid", "trackid")
	if serr != nil {
		log.Fatal(serr)
	}

	log.Printf("example resolve: %s recordid trackid -> %s", *resolver, sample)

	db, err := getDB()
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := db.Close(); err != nil {
			log.Fatal(err)
		}
	}()

	t := NewTrackDB(db, resolve)
	s := NewEnumResourceNode([]string{"running", "quitting"})

	// TODO(CaptainHayashi): factor this out?
	rtree := bifrost.NewDirectoryResourceNode(
		map[string]bifrost.ResourceNoder{
			"control": bifrost.NewDirectoryResourceNode(
				map[string]bifrost.ResourceNoder{ "state": s },
			),
			"tracks": &TrackResourceNode{
				trackdb: t,
			},
		},
	)

	log.Printf("listening on %s", *hostport)
	tcpserver.Serve(request.Map{
		bifrost.RqRead:  handleRead,
		bifrost.RqWrite: handleWrite,
	}, rtree, "trackd", *hostport)
}

// EnumResourceNode is the type of resource nodes that can hold one of a fixed set of values.
//
// EnumResourceNodes are case-insensitive.
//
// EnumResourceNodes hold three parameters:
// - `current`, which is the current value of the enum;
// - `allowed`, which is the list of allowed values for this enum;
// - `terminal`, which, if set to a value in `allowed`, will be the value to which the enum is set if deleted.
//
// EnumResourceNodes can be accessed using the resource API in the following ways:
//
// Read   /        -> Resource {current: /current, allowed: /allowed}
// Read   /current -> Resource (current value as a string)
// Read   /allowed -> Resource (allowed values as a directory indexed from 0 up)
//
// Write  /        -> As `Write /current`
// Write  /current -> If payload is in /allowed, sets /current to payload.
//                    Otherwise, throw error.
// Write  /allowed -> *not allowed*.
//
// Delete /        -> *not allowed*.
// Delete /current -> *not allowed*.
// Delete /allowed -> *not allowed*.
type EnumResourceNode struct {
	bifrost.ResourceNode

	// These are exported mainly to make NRead able to use ToResource.

	// Current is the current state of the EnumResourceNode.
	Current string   `res:current`
	// Allowed is the set of allowed states of the EnumResourceNode.
	Allowed []string `res:allowed`
}

// NewEnumResourceNode creates a new EnumResourceNode.
//
// The node will have initial value `allowed[0]`.
func NewEnumResourceNode(allowed []string) *EnumResourceNode {
	return &EnumResourceNode{
		Current: allowed[0],
		Allowed: allowed,
	}
}

func isCurrent(relpath []string) bool {
	return len(relpath) == 1 && strings.EqualFold(relpath[0], "current")
}

func (r *EnumResourceNode) isAllowed(state string) bool {
	for _, a := range(r.Allowed) {
		if strings.EqualFold(state, a) {
			return true
		}
	}
	return false
}

func (r *EnumResourceNode) NRead(prefix, relpath []string) ([]bifrost.Resource, error) {
	// Is this looking at the whole node?  If so, just send it as a resource.
	if len(relpath) == 0 {
		return bifrost.ToResource(prefix, r), nil
	}

	// We have two (scalar-ish) children, so maybe this is trying to get one of those.
	// But it'll be easier to knock out the error case first.
	if len(relpath) != 1 {
		return []bifrost.Resource{}, fmt.Errorf("can't find %q", relpath)
	}

	// Which child is it?
	if strings.EqualFold(relpath[0], "current") {
		return bifrost.ToResource(prefix, r.Current), nil
	}
	if strings.EqualFold(relpath[0], "allowed") {
		return bifrost.ToResource(prefix, r.Allowed), nil
	}

	return []bifrost.Resource{}, fmt.Errorf("can't find %q", relpath)
}

func (r *EnumResourceNode) NWrite(prefix, relpath []string, val bifrost.BifrostType) error {
	// Trying to write to an enum is the same as trying to write to its current value.
	// Nothing else can be written.
	if !(len(relpath) == 0 || isCurrent(relpath)) {
		return fmt.Errorf("can't write to %q", relpath)
	}

	// TODO(CaptainHayashi): support more than strings here?
	st, ok := val.(bifrost.BifrostTypeString)
	if !ok {
		return fmt.Errorf("state must be a string, got %q", val)
	}
	_, s := st.ResourceBody()

	if !r.isAllowed(s) {
		return fmt.Errorf("%s is not an allowed state", s)
	}

	r.Current = s
	return nil
}

func (r *EnumResourceNode) NDelete(prefix, relpath []string) error {
	// Deleting = writing "quitting" by design.
	// Since we can't write to children of a state node, this is sound.
	return r.NWrite(prefix, relpath, bifrost.BifrostTypeString("quitting"))
}

func (r *EnumResourceNode) NAdd(_, _ []string, _ bifrost.ResourceNoder) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't add to state")
}

type TrackResourceNode struct {
	bifrost.ResourceNode

	trackdb *TrackDB
}

func (r *TrackResourceNode) NRead(prefix, relpath []string) ([]bifrost.Resource, error) {
	// Is this about us, or one of our children?
	if len(relpath) == 0 {
		// TODO(CaptainHayashi): This should be something else.
		// Like, maybe, a Query?
		return bifrost.ToResource(prefix, struct{}{}), nil
	}
	// We're expecting relpath to contain the trackID and nothing else.
	// Bail out if this isn't the case.
	if len(relpath) != 1 {
		return []bifrost.Resource{}, fmt.Errorf("expected only one child, got %q", relpath)
	}
	return r.trackdb.LookupTrack(prefix, relpath[0])
}

func (r *TrackResourceNode) NWrite(_, _ []string, _ bifrost.BifrostType) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't write to trackdb")
}

func (r *TrackResourceNode) NDelete(_, _ []string) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't delete trackdb")
}

func (r *TrackResourceNode) NAdd(_, _ []string, _ bifrost.ResourceNoder) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't add to trackdb")
}

func handleRead(_ chan<- *bifrost.Message, response chan<- *bifrost.Message, args []string, it interface{}) (bool, error) {
	t := it.(bifrost.ResourceNoder)

	// read TAG PATH
	if 2 == len(args) {
		// Reading can never quit the server (we hope).
		res := bifrost.Read(t, args[1])
		// TODO(CaptainHayashi): don't unpack this?
		if res.Status.Code != bifrost.StatusOk {
			return false, fmt.Errorf("fixme: %q", res.Status.String())
		}
		for _, r := range res.Resources {
			response <- r.Message(args[0])
		}

		return false, nil
	}

	return false, fmt.Errorf("FIXME: bad read %q", args)
}

func handleWrite(_ chan<- *bifrost.Message, response chan<- *bifrost.Message, args []string, it interface{}) (bool, error) {
	t := it.(bifrost.ResourceNoder)

	// write TAG(ignored) PATH VALUE
	if 3 == len(args) {
		// TODO(CaptainHayashi): figuring out if the server has quit is very convoluted at the moment

		res := bifrost.Write(t, args[1], args[2])
		// TODO(CaptainHayashi): don't unpack this (as above)?
		if res.Status.Code != bifrost.StatusOk {
			return false, fmt.Errorf("fixme: %q", res.Status.String())
		}

		// Ugh... please fix this.
		return args[1] == "/control/state" && strings.EqualFold(args[2], "quitting"), nil
	}

	return false, fmt.Errorf("FIXME: bad write %q", args)
}
<commit_msg>Update to struct-style server.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"github.com/UniversityRadioYork/bifrost-go"
	"github.com/UniversityRadioYork/bifrost-server/tcpserver"
	//"github.com/docopt/docopt-go"
	_ "github.com/lib/pq"
)

var hostport = flag.String("hostport", "localhost:8123", "The host and port on which trackd should listen (host:port).")
var resolver = flag.String("resolver", "resolve", "The two-argument command to which trackids will be sent on stdin.")

func resolve(recordid, trackid string) (out string, err error) {
	cmd := exec.Command(*resolver, recordid, trackid)

	var outb []byte
	outb, err = cmd.Output()
	out = string(outb)

	return
}

func main() {
	flag.Parse()

	sample, serr := resolve("recordid", "trackid")
	if serr != nil {
		log.Fatal(serr)
	}

	log.Printf("example resolve: %s recordid trackid -> %s", *resolver, sample)

	db, err := getDB()
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := db.Close(); err != nil {
			log.Fatal(err)
		}
	}()

	t := NewTrackDB(db, resolve)
	s := NewEnumResourceNode([]string{"running", "quitting"})

	// TODO(CaptainHayashi): factor this out?
	rtree := bifrost.NewDirectoryResourceNode(
		map[string]bifrost.ResourceNoder{
			"control": bifrost.NewDirectoryResourceNode(
				map[string]bifrost.ResourceNoder{ "state": s },
			),
			"tracks": &TrackResourceNode{
				trackdb: t,
			},
		},
	)

	log.Printf("listening on %s", *hostport)

	td := tcpserver.New("trackd", *hostport, rtree)
	td.AddHandler(bifrost.RqRead, handleRead).AddHandler(bifrost.RqWrite, handleWrite).Serve()
}

// EnumResourceNode is the type of resource nodes that can hold one of a fixed set of values.
//
// EnumResourceNodes are case-insensitive.
//
// EnumResourceNodes hold three parameters:
// - `current`, which is the current value of the enum;
// - `allowed`, which is the list of allowed values for this enum;
// - `terminal`, which, if set to a value in `allowed`, will be the value to which the enum is set if deleted.
//
// EnumResourceNodes can be accessed using the resource API in the following ways:
//
// Read   /        -> Resource {current: /current, allowed: /allowed}
// Read   /current -> Resource (current value as a string)
// Read   /allowed -> Resource (allowed values as a directory indexed from 0 up)
//
// Write  /        -> As `Write /current`
// Write  /current -> If payload is in /allowed, sets /current to payload.
//                    Otherwise, throw error.
// Write  /allowed -> *not allowed*.
//
// Delete /        -> *not allowed*.
// Delete /current -> *not allowed*.
// Delete /allowed -> *not allowed*.
type EnumResourceNode struct {
	bifrost.ResourceNode

	// These are exported mainly to make NRead able to use ToResource.

	// Current is the current state of the EnumResourceNode.
	Current string   `res:current`
	// Allowed is the set of allowed states of the EnumResourceNode.
	Allowed []string `res:allowed`
}

// NewEnumResourceNode creates a new EnumResourceNode.
//
// The node will have initial value `allowed[0]`.
func NewEnumResourceNode(allowed []string) *EnumResourceNode {
	return &EnumResourceNode{
		Current: allowed[0],
		Allowed: allowed,
	}
}

func isCurrent(relpath []string) bool {
	return len(relpath) == 1 && strings.EqualFold(relpath[0], "current")
}

func (r *EnumResourceNode) isAllowed(state string) bool {
	for _, a := range(r.Allowed) {
		if strings.EqualFold(state, a) {
			return true
		}
	}
	return false
}

func (r *EnumResourceNode) NRead(prefix, relpath []string) ([]bifrost.Resource, error) {
	// Is this looking at the whole node?  If so, just send it as a resource.
	if len(relpath) == 0 {
		return bifrost.ToResource(prefix, r), nil
	}

	// We have two (scalar-ish) children, so maybe this is trying to get one of those.
	// But it'll be easier to knock out the error case first.
	if len(relpath) != 1 {
		return []bifrost.Resource{}, fmt.Errorf("can't find %q", relpath)
	}

	// Which child is it?
	if strings.EqualFold(relpath[0], "current") {
		return bifrost.ToResource(prefix, r.Current), nil
	}
	if strings.EqualFold(relpath[0], "allowed") {
		return bifrost.ToResource(prefix, r.Allowed), nil
	}

	return []bifrost.Resource{}, fmt.Errorf("can't find %q", relpath)
}

func (r *EnumResourceNode) NWrite(prefix, relpath []string, val bifrost.BifrostType) error {
	// Trying to write to an enum is the same as trying to write to its current value.
	// Nothing else can be written.
	if !(len(relpath) == 0 || isCurrent(relpath)) {
		return fmt.Errorf("can't write to %q", relpath)
	}

	// TODO(CaptainHayashi): support more than strings here?
	st, ok := val.(bifrost.BifrostTypeString)
	if !ok {
		return fmt.Errorf("state must be a string, got %q", val)
	}
	_, s := st.ResourceBody()

	if !r.isAllowed(s) {
		return fmt.Errorf("%s is not an allowed state", s)
	}

	r.Current = s
	return nil
}

func (r *EnumResourceNode) NDelete(prefix, relpath []string) error {
	// Deleting = writing "quitting" by design.
	// Since we can't write to children of a state node, this is sound.
	return r.NWrite(prefix, relpath, bifrost.BifrostTypeString("quitting"))
}

func (r *EnumResourceNode) NAdd(_, _ []string, _ bifrost.ResourceNoder) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't add to state")
}

type TrackResourceNode struct {
	bifrost.ResourceNode

	trackdb *TrackDB
}

func (r *TrackResourceNode) NRead(prefix, relpath []string) ([]bifrost.Resource, error) {
	// Is this about us, or one of our children?
	if len(relpath) == 0 {
		// TODO(CaptainHayashi): This should be something else.
		// Like, maybe, a Query?
		return bifrost.ToResource(prefix, struct{}{}), nil
	}
	// We're expecting relpath to contain the trackID and nothing else.
	// Bail out if this isn't the case.
	if len(relpath) != 1 {
		return []bifrost.Resource{}, fmt.Errorf("expected only one child, got %q", relpath)
	}
	return r.trackdb.LookupTrack(prefix, relpath[0])
}

func (r *TrackResourceNode) NWrite(_, _ []string, _ bifrost.BifrostType) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't write to trackdb")
}

func (r *TrackResourceNode) NDelete(_, _ []string) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't delete trackdb")
}

func (r *TrackResourceNode) NAdd(_, _ []string, _ bifrost.ResourceNoder) error {
	// TODO(CaptainHayashi): correct error
	return fmt.Errorf("can't add to trackdb")
}

func handleRead(_ chan<- *bifrost.Message, response chan<- *bifrost.Message, args []string, it interface{}) (bool, error) {
	t := it.(bifrost.ResourceNoder)

	// read TAG PATH
	if 2 == len(args) {
		// Reading can never quit the server (we hope).
		res := bifrost.Read(t, args[1])
		// TODO(CaptainHayashi): don't unpack this?
		if res.Status.Code != bifrost.StatusOk {
			return false, fmt.Errorf("fixme: %q", res.Status.String())
		}
		for _, r := range res.Resources {
			response <- r.Message(args[0])
		}

		return false, nil
	}

	return false, fmt.Errorf("FIXME: bad read %q", args)
}

func handleWrite(_ chan<- *bifrost.Message, response chan<- *bifrost.Message, args []string, it interface{}) (bool, error) {
	t := it.(bifrost.ResourceNoder)

	// write TAG(ignored) PATH VALUE
	if 3 == len(args) {
		// TODO(CaptainHayashi): figuring out if the server has quit is very convoluted at the moment

		res := bifrost.Write(t, args[1], args[2])
		// TODO(CaptainHayashi): don't unpack this (as above)?
		if res.Status.Code != bifrost.StatusOk {
			return false, fmt.Errorf("fixme: %q", res.Status.String())
		}

		// Ugh... please fix this.
		return args[1] == "/control/state" && strings.EqualFold(args[2], "quitting"), nil
	}

	return false, fmt.Errorf("FIXME: bad write %q", args)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os"

	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/engine-api/types/strslice"
)

func main() {
	// Create docker client
	cli, err := client.NewEnvClient()
	quitOnErr(err)

	// Let's see if we can even talk to it
	ver, err := cli.ServerVersion(context.Background())
	quitOnErr(err)
	fmt.Println("Was able to connect to docker daemon")
	fmt.Println(ver)

	// Let's create a container
	cmd := strslice.StrSlice{"echo", "hello", "world"}
	containerConf := &container.Config{
		Image: "busybox",
		Cmd:   cmd,
	}
	resp, err := cli.ContainerCreate(context.Background(), containerConf, nil, nil, "")
	quitOnErr(err)
	err = cli.ContainerStart(context.Background(), resp.ID, types.ContainerStartOptions{})
	quitOnErr(err)
	fmt.Println("Was able to run a container")
}

func quitOnErr(err error) {
	if err != nil {
		fmt.Printf("error ocurred: %v", err)
		os.Exit(1)
	}
}
<commit_msg>Comments..<commit_after>package main

import (
	"context"
	"fmt"
	"os"

	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/engine-api/types/strslice"
)

func main() {
	// Create docker client
	cli, err := client.NewEnvClient()
	quitOnErr(err)

	// Let's see if we can even talk to Docker daemon
	ver, err := cli.ServerVersion(context.Background())
	quitOnErr(err)
	fmt.Println("Was able to connect to docker daemon")
	fmt.Println(ver)

	// Let's create a container
	cmd := strslice.StrSlice{"echo", "hello", "world"}
	containerConf := &container.Config{
		Image: "busybox",
		Cmd:   cmd,
	}
	resp, err := cli.ContainerCreate(context.Background(), containerConf, nil, nil, "")
	quitOnErr(err)

	// Let's start the container
	err = cli.ContainerStart(context.Background(), resp.ID, types.ContainerStartOptions{})
	quitOnErr(err)
	fmt.Println("Was able to run a container")
}

func quitOnErr(err error) {
	if err != nil {
		fmt.Printf("error ocurred: %v", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

func main() {

	app := cli.NewApp()
	app.Name = "kube"
	app.Usage = "cli for sharing kubernetes resource files repos and management"
	app.Version = "1.0.0"
	// global level flags
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Show more output",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "search",
			Aliases: []string{"s"},
			Usage:   "Search global kubernetes resource with keyword.",
			Action: func(c *cli.Context) {
				keyword := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only support one keyword in v1")
				}
				if len(c.Args()) > 0 {
					keyword = c.Args()[0]
					searchTag(keyword)
				} else {
					println("you need at least one keyword for search. for example: kube search redis")

				}
			},
		},
		{
			Name:    "push",
			Aliases: []string{"p"},
			Usage:   "Push resource files to kube.hub with tag",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "tag, t",
					Usage: "Tag your kubernetes resource files repo a name.",
				},
				cli.StringFlag{
					Name:  "description, d",
					Usage: "Write a description for your kubernetes resource repo.",
				},
			},
			Action: func(c *cli.Context) {
				filename := ""
				tag := ""
				description := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only pushing one resource file in v1")
				}
				if len(c.Args()) > 0 {
					if c.String("tag") == "" {
						println("You need to specify a tag")
						return
					}
					filename = c.Args()[0]
					tag = c.String("tag")
					description = c.String("description")
					println("pushing file: ", filename)
					println("with tag: ", tag)
					println("with description: ", description)
					pushFile(filename, tag, description)
				} else {
					println("You need to push with files, for example: kube push my-redis-rc.json -t ming.redis")

				}
			},
		},
		{
			Name:    "get",
			Aliases: []string{"g"},
			Usage:   "Get resource files from kube.hub by tag",
			Action: func(c *cli.Context) {
				tag := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only get one repo at a time")
				}
				if len(c.Args()) > 0 {
					tag = c.Args().First()
					println("get tag: ", tag)
					println("NOTICE: You will get the resource files, you can use kubectl command after that.")
					getFile(tag)
				} else {
					println("NOTICE: You need to specify a tag for download, for example: kube get ming.redis")
					println("NOTICE: You can use subcommand search to find one, for example: kube search redis.")
				}
			},
		},
		{
			Name:    "like",
			Aliases: []string{"star"},
			Usage:   "Add a star for a specific resource tag",
			Action: func(c *cli.Context) {
				tag := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only like one repo at a time")
				}
				if len(c.Args()) > 0 {
					tag = c.Args().First()
					println("I like tag: ", tag)
					likeIt(tag)
				} else {
					println("NOTICE: You need to specify a tag to give it a start")
				}
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>change get sumcommand to pull<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

func main() {

	app := cli.NewApp()
	app.Name = "kube"
	app.Usage = "cli for sharing kubernetes resource files repos and management"
	app.Version = "1.0.1"
	// global level flags
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Show more output",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "search",
			Aliases: []string{"s"},
			Usage:   "Search global kubernetes resource with keyword.",
			Action: func(c *cli.Context) {
				keyword := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only support one keyword in v1")
				}
				if len(c.Args()) > 0 {
					keyword = c.Args()[0]
					searchTag(keyword)
				} else {
					println("you need at least one keyword for search. for example: kube search redis")

				}
			},
		},
		{
			Name:    "push",
			Usage:   "Push resource files to kube.hub with tag",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "tag, t",
					Usage: "Tag your kubernetes resource files repo a name.",
				},
				cli.StringFlag{
					Name:  "description, d",
					Usage: "Write a description for your kubernetes resource repo.",
				},
			},
			Action: func(c *cli.Context) {
				filename := ""
				tag := ""
				description := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only pushing one resource file in v1")
				}
				if len(c.Args()) > 0 {
					if c.String("tag") == "" {
						println("You need to specify a tag")
						return
					}
					filename = c.Args()[0]
					tag = c.String("tag")
					description = c.String("description")
					println("pushing file: ", filename)
					println("with tag: ", tag)
					println("with description: ", description)
					pushFile(filename, tag, description)
				} else {
					println("You need to push with files, for example: kube push my-redis-rc.json -t ming.redis")

				}
			},
		},
		{
			Name:    "pull",
			Usage:   "Get resource files from kube.hub by tag",
			Action: func(c *cli.Context) {
				tag := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only get one repo at a time")
				}
				if len(c.Args()) > 0 {
					tag = c.Args().First()
					println("get tag: ", tag)
					println("NOTICE: You will get the resource files, you can use kubectl command after that.")
					getFile(tag)
				} else {
					println("NOTICE: You need to specify a tag for download, for example: kube pull ming.redis")
					println("NOTICE: You can use subcommand search to find one, for example: kube search redis.")
				}
			},
		},
		{
			Name:    "like",
			Aliases: []string{"star"},
			Usage:   "Add a star for a specific resource tag",
			Action: func(c *cli.Context) {
				tag := ""
				if len(c.Args()) > 1 {
					println("NOTICE: Only like one repo at a time")
				}
				if len(c.Args()) > 0 {
					tag = c.Args().First()
					println("I like tag: ", tag)
					likeIt(tag)
				} else {
					println("NOTICE: You need to specify a tag to give it a start")
				}
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/nguyenthenguyen/docx"
)

// Go forth and create an agenda.
func main() {
	nt := nextTuesday(time.Now())
	fmt.Println("Generating Agenda for", monthDayCommaYear(nt))

	if err := createDoc(nt); err != nil {
		panic(err)
	}
}

// Creates a word document with the name DD.MM.YYYY.docx based on Agenda.docx.
func createDoc(t time.Time) error {
	r, err := docx.ReadDocxFile("./Agenda.docx")
	if err != nil {
		return err
	}

	d := r.Editable()
	if err := replaceFields(d, t); err != nil {
		return err
	}

	d.WriteToFile("./" + formatDate(t, delimiterPeriods) + ".docx")
	r.Close()

	return nil
}

// ReplaceSpeakers replaces the speech time, speaker, speech, evaluator time, and evaluator.
func replaceSpeakers(d *docx.Docx, s []*Speaker) {
	// Time for Speech evaluation goals starts at 7:13 pm.
	curTime := time.Date(2017, time.January, 1, 7, 13, 0, 0, time.UTC)
	nextTime, _ := addMinutes(curTime, 0)
	var pastSpeechTime int
	var printString string
	for i := range s {
		speechOrder := i + 1
		soString := strconv.Itoa(speechOrder)
		speaker := s[i]

		d.Replace("evaluator"+soString, speaker.evaluator, -1)
		d.Replace("speaker"+soString+"FirstLastName", speaker.name, -1)
		d.Replace("firstName"+soString, speaker.firstName(), -1)
		d.Replace("speaker"+soString+"Manual", speaker.Speech.manualName, -1)
		d.Replace("speaker"+soString+"Speech", speaker.Speech.info(), -1)

		// Replace Speech times for Speaker and evaluator based on last max Speech time plus one.
		nextTime, printString = addMinutes(nextTime, pastSpeechTime)
		d.Replace("e"+soString+"t"+soString, printString, 1)

		nextTime, printString = addMinutes(nextTime, +1)
		d.Replace("s"+soString+"t"+soString, printString, 1)
		pastSpeechTime = speaker.Speech.max + 1
	}

	_, ttTime := addMinutes(nextTime, pastSpeechTime)
	d.Replace("ttmt", ttTime, 1)
}

// ReplaceFutureWeeks replaces the future schedule on the agenda.
func replaceFutureWeeks(d *docx.Docx, fw [][]string) {
	for i := range fw {
		nextWeek := fw[i]

		for j := range nextWeek {
			d.Replace("w"+strconv.Itoa(i)+"_"+strconv.Itoa(j), nextWeek[j], 1)
		}
	}
}

// ReplaceFields handles the replacement of all the placeholders in the agenda.
func replaceFields(d *docx.Docx, t time.Time) error {
	roles, err := NewAgendaRoles(formatDate(t, delimiterSlashes))

	if err != nil {
		return err
	}

	d.ReplaceHeader("Date", monthDayCommaYear(t))
	d.Replace("president", roles.BoardMembers.President, -1)
	d.Replace("vpe", roles.BoardMembers.VPE, -1)
	d.Replace("vpm", roles.BoardMembers.VPM, -1)
	d.Replace("vppr", roles.BoardMembers.VPPR, -1)
	d.Replace("secretary", roles.BoardMembers.Secretary, -1)
	d.Replace("treasurer", roles.BoardMembers.Treasurer, -1)
	d.Replace("saa", roles.BoardMembers.SAA, -1)
	d.Replace("jokeMaster", roles.JokeMaster, -1)
	d.Replace("toastmasterOfDay", roles.Toastmaster, -1)
	d.Replace("generalEval", roles.GE, -1)
	d.Replace("timer", roles.Timer, -1)
	d.Replace("ah-counter", roles.AhCounter, -1)
	d.Replace("grammarian", roles.Grammarian, -1)
	d.Replace("tTMaster", roles.TableTopicsMaster, -1)

	replaceSpeakers(d, roles.Speakers)
	replaceFutureWeeks(d, roles.FutureWeeks)
	return nil
}
<commit_msg>Defer file close.<commit_after>package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/nguyenthenguyen/docx"
)

// Go forth and create an agenda.
func main() {
	nt := nextTuesday(time.Now())
	fmt.Println("Generating Agenda for", monthDayCommaYear(nt))

	if err := createDoc(nt); err != nil {
		panic(err)
	}
}

// Creates a word document with the Name DD.MM.YYYY.docx based on Agenda.docx.
func createDoc(t time.Time) error {
	r, err := docx.ReadDocxFile("./Agenda.docx")
	defer r.Close()

	if err != nil {
		return err
	}

	d := r.Editable()
	if err := replaceFields(d, t); err != nil {
		return err
	}

	d.WriteToFile("./" + formatDate(t, delimiterPeriods) + ".docx")
	return nil
}

// ReplaceSpeakers replaces the speech time, speaker, speech, Evaluator time, and Evaluator.
func replaceSpeakers(d *docx.Docx, s []*Speaker) {
	// Time for Speech evaluation goals starts at 7:13 pm.
	curTime := time.Date(2017, time.January, 1, 7, 13, 0, 0, time.UTC)
	nextTime, _ := addMinutes(curTime, 0)
	var pastSpeechTime int
	var printString string
	for i := range s {
		speechOrder := i + 1
		soString := strconv.Itoa(speechOrder)
		speaker := s[i]

		d.Replace("Evaluator"+soString, speaker.Evaluator, -1)
		d.Replace("speaker"+soString+"FirstLastName", speaker.Name, -1)
		d.Replace("firstName"+soString, speaker.firstName(), -1)
		d.Replace("speaker"+soString+"Manual", speaker.Speech.ManualName, -1)
		d.Replace("speaker"+soString+"Speech", speaker.Speech.info(), -1)

		// Replace Speech times for Speaker and Evaluator based on last Max Speech time plus one.
		nextTime, printString = addMinutes(nextTime, pastSpeechTime)
		d.Replace("e"+soString+"t"+soString, printString, 1)

		nextTime, printString = addMinutes(nextTime, +1)
		d.Replace("s"+soString+"t"+soString, printString, 1)
		pastSpeechTime = speaker.Speech.Max + 1
	}

	_, ttTime := addMinutes(nextTime, pastSpeechTime)
	d.Replace("ttmt", ttTime, 1)
}

// ReplaceFutureWeeks replaces the future schedule on the agenda.
func replaceFutureWeeks(d *docx.Docx, fw [][]string) {
	for i := range fw {
		nextWeek := fw[i]

		for j := range nextWeek {
			d.Replace("w"+strconv.Itoa(i)+"_"+strconv.Itoa(j), nextWeek[j], 1)
		}
	}
}

// ReplaceFields handles the replacement of all the placeholders in the agenda.
func replaceFields(d *docx.Docx, t time.Time) error {
	roles, err := NewAgendaRoles(formatDate(t, delimiterSlashes))

	if err != nil {
		return err
	}

	d.ReplaceHeader("Date", monthDayCommaYear(t))
	d.Replace("president", roles.BoardMembers.President, -1)
	d.Replace("vpe", roles.BoardMembers.VPE, -1)
	d.Replace("vpm", roles.BoardMembers.VPM, -1)
	d.Replace("vppr", roles.BoardMembers.VPPR, -1)
	d.Replace("secretary", roles.BoardMembers.Secretary, -1)
	d.Replace("treasurer", roles.BoardMembers.Treasurer, -1)
	d.Replace("saa", roles.BoardMembers.SAA, -1)
	d.Replace("jokeMaster", roles.JokeMaster, -1)
	d.Replace("toastmasterOfDay", roles.Toastmaster, -1)
	d.Replace("generalEval", roles.GE, -1)
	d.Replace("timer", roles.Timer, -1)
	d.Replace("ah-counter", roles.AhCounter, -1)
	d.Replace("grammarian", roles.Grammarian, -1)
	d.Replace("tTMaster", roles.TableTopicsMaster, -1)

	replaceSpeakers(d, roles.Speakers)
	replaceFutureWeeks(d, roles.FutureWeeks)
	return nil
}
<|endoftext|>


<commit_before>package main

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

var (
	version   = "0.1.0"
	github    = "https://github.com/ekalinin/awsping"
	useragent = fmt.Sprintf("AwsPing/%s (+%s)", version, github)
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func mkRandoString(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

// AWSRegion description of the AWS EC2 region
type AWSRegion struct {
	Name    string
	Code    string
	Latency time.Duration
	Error   error
}

// CheckLatency fills internal field Latency
func (r *AWSRegion) CheckLatency(wg *sync.WaitGroup) {
	url := fmt.Sprintf("http://dynamodb.%s.amazonaws.com/ping?x=%s",
		r.Code, mkRandoString(13))
	client := &http.Client{}

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", useragent)

	start := time.Now()
	resp, err := client.Do(req)
	r.Latency = time.Since(start)

	r.Error = err
	resp.Body.Close()

	wg.Done()
}

func main() {
	regions := []AWSRegion{
		{Name: "US-East (Virginia)", Code: "us-east-1"},
		{Name: "US-West (California)", Code: "us-west-1"},
		{Name: "US-West (Oregon)", Code: "us-west-2"},
		{Name: "Asia Pacific (Mumbai)", Code: "ap-south-1"},
		{Name: "Asia Pacific (Seoul)", Code: "ap-northeast-2"},
		{Name: "Asia Pacific (Singapore)", Code: "ap-southeast-1"},
		{Name: "Asia Pacific (Sydney)", Code: "ap-southeast-2"},
		{Name: "Asia Pacific (Tokyo)", Code: "ap-northeast-1"},
		{Name: "Europe (Ireland)", Code: "eu-west-1"},
		{Name: "Europe (Frankfurt)", Code: "eu-central-1"},
		{Name: "South America (São Paulo)", Code: "sa-east-1"},
		//{Name: "China (Beijing)", Code: "cn-north-1"},
	}
	var wg sync.WaitGroup
	wg.Add(len(regions))

	for i := range regions {
		go regions[i].CheckLatency(&wg)
	}

	wg.Wait()

	outFmt := "|%5v|%-30s|%20s|%20v|\n"
	fmt.Printf(outFmt, "", "Region", "Latency", "Error")
	for i, r := range regions {
		ms := fmt.Sprintf("%.2f ms", float64(r.Latency.Nanoseconds())/1000/1000)
		fmt.Printf(outFmt, i, r.Name, ms, r.Error)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<commit_msg>added sorting; the closest region is first in the list<commit_after>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"sort"
	"sync"
	"time"
)

var (
	version   = "0.1.0"
	github    = "https://github.com/ekalinin/awsping"
	useragent = fmt.Sprintf("AwsPing/%s (+%s)", version, github)
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func mkRandoString(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

// AWSRegion description of the AWS EC2 region
type AWSRegion struct {
	Name    string
	Code    string
	Latency time.Duration
	Error   error
}

// CheckLatency fills internal field Latency
func (r *AWSRegion) CheckLatency(wg *sync.WaitGroup) {
	url := fmt.Sprintf("http://dynamodb.%s.amazonaws.com/ping?x=%s",
		r.Code, mkRandoString(13))
	client := &http.Client{}

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", useragent)

	start := time.Now()
	resp, err := client.Do(req)
	r.Latency = time.Since(start)

	r.Error = err
	resp.Body.Close()

	wg.Done()
}

// AWSRegions slice of the AWSRegion
type AWSRegions []AWSRegion

func (rs AWSRegions) Len() int {
	return len(rs)
}

func (rs AWSRegions) Less(i, j int) bool {
	return rs[i].Latency < rs[j].Latency
}

func (rs AWSRegions) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

func main() {
	regions := AWSRegions{
		{Name: "US-East (Virginia)", Code: "us-east-1"},
		{Name: "US-West (California)", Code: "us-west-1"},
		{Name: "US-West (Oregon)", Code: "us-west-2"},
		{Name: "Asia Pacific (Mumbai)", Code: "ap-south-1"},
		{Name: "Asia Pacific (Seoul)", Code: "ap-northeast-2"},
		{Name: "Asia Pacific (Singapore)", Code: "ap-southeast-1"},
		{Name: "Asia Pacific (Sydney)", Code: "ap-southeast-2"},
		{Name: "Asia Pacific (Tokyo)", Code: "ap-northeast-1"},
		{Name: "Europe (Ireland)", Code: "eu-west-1"},
		{Name: "Europe (Frankfurt)", Code: "eu-central-1"},
		{Name: "South America (São Paulo)", Code: "sa-east-1"},
		//{Name: "China (Beijing)", Code: "cn-north-1"},
	}
	var wg sync.WaitGroup
	wg.Add(len(regions))

	for i := range regions {
		go regions[i].CheckLatency(&wg)
	}

	wg.Wait()

	sort.Sort(regions)

	outFmt := "|%5v|%-30s|%20s|%20v|\n"
	fmt.Printf(outFmt, "", "Region", "Latency", "Error")
	for i, r := range regions {
		ms := fmt.Sprintf("%.2f ms", float64(r.Latency.Nanoseconds())/1000/1000)
		fmt.Printf(outFmt, i, r.Name, ms, r.Error)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<|endoftext|>


<commit_before>// Workflow written in SciPipe.
// For more information about SciPipe, see: http://scipipe.org
package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/d4l3k/turtle"
	"github.com/flowbase/flowbase"
	"log"
	"os"
	str "strings"
)

const (
	BUFSIZE = 16
)

func main() {
	flowbase.InitLogInfo()

	inFileName := flag.String("infile", "", "The input file name")
	flag.Parse()
	if *inFileName == "" {
		fmt.Println("No filename specified to --infile")
		os.Exit(1)
	}

	// Create a pipeline runner
	pipeRunner := flowbase.NewPipelineRunner()

	// Initialize processes and add to runner
	fileReader := NewFileReader()
	pipeRunner.AddProcess(fileReader)

	//tripleParser := NewTripleParser()
	//pipeRunner.AddProcess(tripleParser)

	turtleToTriples := NewTurtleToTriples()
	pipeRunner.AddProcess(turtleToTriples)

	triplePrinter := NewTriplePrinter()
	pipeRunner.AddProcess(triplePrinter)

	// Connect workflow dependency network
	turtleToTriples.In = fileReader.OutLine
	triplePrinter.In = turtleToTriples.Out

	// Run the pipeline!
	go func() {
		defer close(fileReader.InFileName)
		fileReader.InFileName <- *inFileName
	}()

	pipeRunner.Run()

}

// ================================================================================
// Components
// ================================================================================

// --------------------------------------------------------------------------------
// FileReader
// --------------------------------------------------------------------------------

type FileReader struct {
	InFileName chan string
	OutLine    chan string
}

func NewFileReader() *FileReader {
	return &FileReader{
		InFileName: make(chan string, BUFSIZE),
		OutLine:    make(chan string, BUFSIZE),
	}
}

func (p *FileReader) Run() {
	defer close(p.OutLine)

	flowbase.Debug.Println("Starting loop")
	for fileName := range p.InFileName {
		flowbase.Debug.Printf("Starting processing file %s\n", fileName)
		fh, err := os.Open(fileName)
		if err != nil {
			log.Fatal(err)
		}
		defer fh.Close()

		sc := bufio.NewScanner(fh)
		for sc.Scan() {
			if err := sc.Err(); err != nil {
				log.Fatal(err)
			}
			p.OutLine <- sc.Text()
		}
	}
}

// --------------------------------------------------------------------------------
// TripleParser
// --------------------------------------------------------------------------------

type TripleParser struct {
	In  chan string
	Out chan *RDFTriple
}

func NewTripleParser() *TripleParser {
	return &TripleParser{
		In:  make(chan string, BUFSIZE),
		Out: make(chan *RDFTriple, BUFSIZE),
	}
}

func (p *TripleParser) Run() {
	defer close(p.Out)
	for line := range p.In {
		triple := NewRDFTriple()
		bits := str.Split(line, " ")
		triple.Subject = bits[0]
		triple.Predicate = bits[1]
		triple.Object = bits[2]
		p.Out <- triple
	}
}

// --------------------------------------------------------------------------------
// TurtleToTriples
// --------------------------------------------------------------------------------

type TurtleToTriples struct {
	In  chan string
	Out chan turtle.Triple
}

func NewTurtleToTriples() *TurtleToTriples {
	return &TurtleToTriples{
		In:  make(chan string, BUFSIZE),
		Out: make(chan turtle.Triple, BUFSIZE),
	}
}

func (p *TurtleToTriples) Run() {
	defer close(p.Out)
	for line := range p.In {
		triples, err := turtle.Parse([]byte(line))
		if err != nil {
			flowbase.Error.Printf("Error parsing turtle: %v", err)
			os.Exit(1)
		}
		for _, triple := range triples {
			p.Out <- triple
		}
	}
}

// --------------------------------------------------------------------------------
// TriplePrinter
// --------------------------------------------------------------------------------

type TriplePrinter struct {
	In chan turtle.Triple
}

func NewTriplePrinter() *TriplePrinter {
	return &TriplePrinter{
		In: make(chan turtle.Triple, BUFSIZE),
	}
}

func (p *TriplePrinter) Run() {
	for tr := range p.In {
		fmt.Printf("S: %s\nP: %s\nO: %s\n\n", tr.Subj, tr.Pred, tr.Obj)
	}
}

// --------------------------------------------------------------------------------
// IP: RDFTriple
// --------------------------------------------------------------------------------

type RDFTriple struct {
	Subject   string
	Predicate string
	Object    string
}

func NewRDFTriple() *RDFTriple {
	return &RDFTriple{}
}
<commit_msg>Using knakk's rdf parser to parse triples. Currently with mega-wasteful excessive allocations, but hey...<commit_after>// Workflow written in SciPipe.
// For more information about SciPipe, see: http://scipipe.org
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	str "strings"

	"github.com/flowbase/flowbase"
	"github.com/knakk/rdf"
)

const (
	BUFSIZE = 16
)

func main() {
	flowbase.InitLogInfo()

	inFileName := flag.String("infile", "", "The input file name")
	flag.Parse()
	if *inFileName == "" {
		fmt.Println("No filename specified to --infile")
		os.Exit(1)
	}

	// Create a pipeline runner
	pipeRunner := flowbase.NewPipelineRunner()

	// Initialize processes and add to runner
	fileReader := NewFileReader()
	pipeRunner.AddProcess(fileReader)

	//tripleParser := NewTripleParser()
	//pipeRunner.AddProcess(tripleParser)

	tripleParser := NewTripleParser()
	pipeRunner.AddProcess(tripleParser)

	triplePrinter := NewTriplePrinter()
	pipeRunner.AddProcess(triplePrinter)

	// Connect workflow dependency network
	tripleParser.In = fileReader.OutLine
	triplePrinter.In = tripleParser.Out

	// Run the pipeline!
	go func() {
		defer close(fileReader.InFileName)
		fileReader.InFileName <- *inFileName
	}()

	pipeRunner.Run()

}

// ================================================================================
// Components
// ================================================================================

// --------------------------------------------------------------------------------
// FileReader
// --------------------------------------------------------------------------------

type FileReader struct {
	InFileName chan string
	OutLine    chan string
}

func NewFileReader() *FileReader {
	return &FileReader{
		InFileName: make(chan string, BUFSIZE),
		OutLine:    make(chan string, BUFSIZE),
	}
}

func (p *FileReader) Run() {
	defer close(p.OutLine)

	flowbase.Debug.Println("Starting loop")
	for fileName := range p.InFileName {
		flowbase.Debug.Printf("Starting processing file %s\n", fileName)
		fh, err := os.Open(fileName)
		if err != nil {
			log.Fatal(err)
		}
		defer fh.Close()

		sc := bufio.NewScanner(fh)
		for sc.Scan() {
			if err := sc.Err(); err != nil {
				log.Fatal(err)
			}
			p.OutLine <- sc.Text()
		}
	}
}

// --------------------------------------------------------------------------------
// TripleParser
// --------------------------------------------------------------------------------

type TripleParser struct {
	In  chan string
	Out chan *RDFTriple
}

func NewTripleParser() *TripleParser {
	return &TripleParser{
		In:  make(chan string, BUFSIZE),
		Out: make(chan *RDFTriple, BUFSIZE),
	}
}

func (p *TripleParser) Run() {
	defer close(p.Out)
	for line := range p.In {
		lineReader := str.NewReader(line)
		dec := rdf.NewTripleDecoder(lineReader, rdf.Turtle)
		for triple, err := dec.Decode(); err != io.EOF; triple, err = dec.Decode() {
			rdfTriple := &RDFTriple{Subject: triple.Subj.String(), Predicate: triple.Pred.String(), Object: triple.Obj.String()}
			p.Out <- rdfTriple
		}
	}
}

// --------------------------------------------------------------------------------
// TriplePrinter
// --------------------------------------------------------------------------------

type TriplePrinter struct {
	In chan *RDFTriple
}

func NewTriplePrinter() *TriplePrinter {
	return &TriplePrinter{
		In: make(chan *RDFTriple, BUFSIZE),
	}
}

func (p *TriplePrinter) Run() {
	for tr := range p.In {
		fmt.Printf("S: %s\nP: %s\nO: %s\n\n", tr.Subject, tr.Predicate, tr.Object)
	}
}

// --------------------------------------------------------------------------------
// IP: RDFTriple
// --------------------------------------------------------------------------------

type RDFTriple struct {
	Subject   string
	Predicate string
	Object    string
}

func NewRDFTriple() *RDFTriple {
	return &RDFTriple{}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"

	"github.com/joho/godotenv"
	"github.com/rs/zerolog/log"
	tb "gopkg.in/tucnak/telebot.v3"

	"github.com/devmattrick/emotesbot/handler"
)

func main() {
	if err := run(); err != nil {
		log.Fatal().Err(err).Send()
		os.Exit(1)
	}
}

func run() error {
	// Load .env file
	err := godotenv.Load()
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	// Set up Telegram bot
	botToken := os.Getenv("TELEGRAM_BOT_TOKEN")
	b, err := tb.NewBot(tb.Settings{
		Token:  botToken,
		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
	})
	if err != nil {
		return err
	}

	b.OnError = func(e error, c tb.Context) {
		log.Error().
			Err(e).
			Str("event", "error").
			Msg("Telegram Bot API error occurred")
	}

	b.Handle(tb.OnQuery, handler.InlineQuery)
	b.Handle(tb.OnInlineResult, handler.InlineQueryFeedback)
	b.Start()

	return nil
}
<commit_msg>Log formatting for fatal errors<commit_after>package main

import (
	"os"
	"time"

	"github.com/joho/godotenv"
	"github.com/rs/zerolog/log"
	tb "gopkg.in/tucnak/telebot.v3"

	"github.com/devmattrick/emotesbot/handler"
)

func main() {
	if err := run(); err != nil {
		log.
			Fatal().
			Str("event", "error").
			Err(err).
			Msg("A fatal error occured")
		os.Exit(1)
	}
}

func run() error {
	// Load .env file
	err := godotenv.Load()
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	// Set up Telegram bot
	botToken := os.Getenv("TELEGRAM_BOT_TOKEN")
	b, err := tb.NewBot(tb.Settings{
		Token:  botToken,
		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
	})
	if err != nil {
		return err
	}

	b.OnError = func(e error, c tb.Context) {
		log.Error().
			Err(e).
			Str("event", "error").
			Msg("A Telegram Bot API error occurred")
	}

	b.Handle(tb.OnQuery, handler.InlineQuery)
	b.Handle(tb.OnInlineResult, handler.InlineQueryFeedback)
	b.Start()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/kr/fs"
	"github.com/wtolson/go-taglib"
)

type Playlist struct {
	Name             string    `json:"name"`
	Path             string    `json:"path"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Status           string    `json:"status"`
	Stats            struct {
		Tracks int `json:"tracks"`
	} `json:"stats"`
	Tracks map[string]*Track `json:"-"`
}

type Track struct {
	Status           string    `json:"status"`
	Title            string    `json:"title"`
	RelPath          string    `json:"relative_path"`
	Path             string    `json:"path"`
	FileName         string    `json:"file_name"`
	FileSize         int64     `json:"file_size"`
	FileModTime      time.Time `json:"file_modification_time"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Tag              struct {
		Length   time.Duration `json:"length"`
		Title    string        `json:"title"`
		Artist   string        `json:"artist"`
		Album    string        `json:"album"`
		Genre    string        `json:"genre"`
		Bitrate  int           `json:"bitrate"`
		Year     int           `json:"year"`
		Channels int           `json:"channels"`
	} `json:"tag"`
}

type Radio struct {
	Name             string    `json:"name"`
	DefaultPlaylist  *Playlist `json:"default_playlist"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Stats            struct {
		Playlists int `json:"playlists"`
		Tracks    int `json:"tracks"`
	} `json:"stats"`
	Playlists []*Playlist `json:"-"`
}

var R *Radio

func (p *Playlist) NewLocalTrack(path string) (*Track, error) {
	if track, err := p.GetTrackByPath(path); err == nil {
		return track, nil
	}

	relPath := path
	if strings.Index(path, p.Path) == 0 {
		relPath = path[len(p.Path):]
	}

	stat, err := os.Stat(path)
	if err != nil {
		return nil, err
	}

	track := &Track{
		Path:             path,
		RelPath:          relPath,
		FileName:         stat.Name(),
		FileSize:         stat.Size(),
		FileModTime:      stat.ModTime(),
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
		// Mode:          stat.Mode(),
	}

	file, err := taglib.Read(path)
	if err != nil {
		logrus.Warnf("Failed to read taglib %q: %v", path, err)
	} else {
		defer file.Close()
		track.Tag.Length = file.Length()
		track.Tag.Artist = file.Artist()
		track.Tag.Title = file.Title()
		track.Tag.Album = file.Album()
		track.Tag.Genre = file.Genre()
		track.Tag.Bitrate = file.Bitrate()
		track.Tag.Year = file.Year()
		track.Tag.Channels = file.Channels()
		// fmt.Println(file.Title(), file.Artist(), file.Album(), file.Comment(), file.Genre(), file.Year(), file.Track(), file.Length(), file.Bitrate(), file.Samplerate(), file.Channels())
	}

	p.Tracks[path] = track
	p.Stats.Tracks++
	return track, nil
}

func (p *Playlist) GetTrackByPath(path string) (*Track, error) {
	if track, found := p.Tracks[path]; found {
		return track, nil
	}
	return nil, fmt.Errorf("no such track")
}

func NewRadio(name string) *Radio {
	return &Radio{
		Name:             name,
		Playlists:        make([]*Playlist, 0),
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
	}
}

func init() {
	R = NewRadio("RadioMan")

	R.NewPlaylist("manual")
	R.NewDirectoryPlaylist("iTunes Music", "~/Music/iTunes/iTunes Media/Music/")
	R.NewDirectoryPlaylist("iTunes Podcasts", "~/Music/iTunes/iTunes Media/Podcasts/")
	dir, err := os.Getwd()
	if err == nil {
		R.NewDirectoryPlaylist("local directory", dir)
	}
	playlistsDir := path.Join(dir, "playlists")
	walker := fs.Walk(playlistsDir)
	for walker.Step() {
		if walker.Path() == playlistsDir {
			continue
		}
		if err := walker.Err(); err != nil {
			logrus.Warnf("walker error: %v", err)
			continue
		}
		realpath, err := filepath.EvalSymlinks(walker.Path())
		if err != nil {
			logrus.Warnf("filepath.EvalSymlinks error for %q: %v", walker.Path(), err)
			continue
		}

		R.NewDirectoryPlaylist(fmt.Sprintf("playlist: %s", walker.Stat().Name()), realpath)
	}

	playlist, _ := R.GetPlaylistByName("iTunes Music")
	R.DefaultPlaylist = playlist
}

func (r *Radio) NewPlaylist(name string) (*Playlist, error) {
	logrus.Infof("New playlist %q", name)
	playlist := &Playlist{
		Name:             name,
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
		Tracks:           make(map[string]*Track, 0),
		Status:           "New",
	}
	r.Playlists = append(r.Playlists, playlist)
	r.Stats.Playlists++
	return playlist, nil
}

func (r *Radio) NewDirectoryPlaylist(name string, path string) (*Playlist, error) {
	playlist, err := r.NewPlaylist(name)
	if err != nil {
		return nil, err
	}
	expandedPath, err := expandUser(path)
	if err != nil {
		return nil, err
	}
	playlist.Path = expandedPath
	return playlist, nil
}

func (r *Radio) GetPlaylistByName(name string) (*Playlist, error) {
	for _, playlist := range r.Playlists {
		if playlist.Name == name {
			return playlist, nil
		}
	}
	return nil, fmt.Errorf("no such playlist")
}

func main() {
	router := gin.Default()

	radio := R

	// ping
	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})

	// static files
	router.StaticFile("/", "./static/index.html")
	router.Static("/static", "./static")
	router.Static("/bower_components", "./bower_components")

	router.GET("/api/playlists", playlistsEndpoint)
	router.GET("/api/playlists/:name", playlistDetailEndpoint)
	router.GET("/api/playlists/:name/tracks", playlistTracksEndpoint)

	router.GET("/api/radios/default", defaultRadioEndpoint)

	router.GET("/api/liquidsoap/getNextSong", getNextSongEndpoint)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8000"
	}

	go updatePlaylistsRoutine(radio)

	router.Run(fmt.Sprintf(":%s", port))
}

func getNextSongEndpoint(c *gin.Context) {
	playlist := R.DefaultPlaylist
	// FIXME: shuffle playlist instead of getting a random track
	// FIXME: do not iterate over a map
	track, err := playlist.GetRandomTrack()
	if err != nil {
		c.String(http.StatusNotFound, fmt.Sprintf("%v", err))
		return
	}

	c.String(http.StatusOK, track.Path)
}

func (p *Playlist) GetRandomTrack() (*Track, error) {
	i := rand.Intn(len(p.Tracks))

	for _, track := range p.Tracks {
		if i <= 0 {
			return track, nil
		}
		i--
	}
	return nil, fmt.Errorf("cannot get a random track")
}

func updatePlaylistsRoutine(r *Radio) {
	for {
		tracksSum := 0
		for _, playlist := range r.Playlists {
			if playlist.Path == "" {
				logrus.Debugf("Playlist %q is not dynamic, skipping update", playlist.Name)
				continue
			}

			logrus.Infof("Updating playlist %q", playlist.Name)
			playlist.Status = "Updating"

			walker := fs.Walk(playlist.Path)
			for walker.Step() {
				if err := walker.Err(); err != nil {
					logrus.Warnf("walker error: %v", err)
					continue
				}
				stat := walker.Stat()

				if stat.IsDir() {
					switch stat.Name() {
					case ".git", "bower_components":
						walker.SkipDir()
					}
				} else {
					switch stat.Name() {
					case ".DS_Store":
						continue
					}

					playlist.NewLocalTrack(walker.Path())
				}
			}

			logrus.Infof("Playlist %q updated, %d tracks", playlist.Name, len(playlist.Tracks))
			playlist.Status = "Ready"
			playlist.ModificationDate = time.Now()
			tracksSum += playlist.Stats.Tracks
		}
		r.Stats.Tracks = tracksSum
		time.Sleep(5 * time.Minute)
	}
}

func playlistsEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"playlists": R.Playlists,
	})
}

func defaultRadioEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"radio": R,
	})
}

func playlistDetailEndpoint(c *gin.Context) {
	name := c.Param("name")
	playlist, err := R.GetPlaylistByName(name)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": err,
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"playlist": playlist,
	})
}

func playlistTracksEndpoint(c *gin.Context) {
	name := c.Param("name")
	playlist, err := R.GetPlaylistByName(name)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": err,
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"tracks": playlist.Tracks,
	})
}
<commit_msg>Fixed getNextSong against empty playlists<commit_after>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/kr/fs"
	"github.com/wtolson/go-taglib"
)

type Playlist struct {
	Name             string    `json:"name"`
	Path             string    `json:"path"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Status           string    `json:"status"`
	Stats            struct {
		Tracks int `json:"tracks"`
	} `json:"stats"`
	Tracks map[string]*Track `json:"-"`
}

type Track struct {
	Status           string    `json:"status"`
	Title            string    `json:"title"`
	RelPath          string    `json:"relative_path"`
	Path             string    `json:"path"`
	FileName         string    `json:"file_name"`
	FileSize         int64     `json:"file_size"`
	FileModTime      time.Time `json:"file_modification_time"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Tag              struct {
		Length   time.Duration `json:"length"`
		Title    string        `json:"title"`
		Artist   string        `json:"artist"`
		Album    string        `json:"album"`
		Genre    string        `json:"genre"`
		Bitrate  int           `json:"bitrate"`
		Year     int           `json:"year"`
		Channels int           `json:"channels"`
	} `json:"tag"`
}

type Radio struct {
	Name             string    `json:"name"`
	DefaultPlaylist  *Playlist `json:"default_playlist"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Stats            struct {
		Playlists int `json:"playlists"`
		Tracks    int `json:"tracks"`
	} `json:"stats"`
	Playlists []*Playlist `json:"-"`
}

var R *Radio

func (t *Track) IsValid() bool {
	return t.Tag.Bitrate >= 64
}

func (p *Playlist) NewLocalTrack(path string) (*Track, error) {
	if track, err := p.GetTrackByPath(path); err == nil {
		return track, nil
	}

	relPath := path
	if strings.Index(path, p.Path) == 0 {
		relPath = path[len(p.Path):]
	}

	stat, err := os.Stat(path)
	if err != nil {
		return nil, err
	}

	track := &Track{
		Path:             path,
		RelPath:          relPath,
		FileName:         stat.Name(),
		FileSize:         stat.Size(),
		FileModTime:      stat.ModTime(),
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
		// Mode:          stat.Mode(),
	}

	file, err := taglib.Read(path)
	if err != nil {
		logrus.Warnf("Failed to read taglib %q: %v", path, err)
	} else {
		defer file.Close()
		track.Tag.Length = file.Length()
		track.Tag.Artist = file.Artist()
		track.Tag.Title = file.Title()
		track.Tag.Album = file.Album()
		track.Tag.Genre = file.Genre()
		track.Tag.Bitrate = file.Bitrate()
		track.Tag.Year = file.Year()
		track.Tag.Channels = file.Channels()
		// fmt.Println(file.Title(), file.Artist(), file.Album(), file.Comment(), file.Genre(), file.Year(), file.Track(), file.Length(), file.Bitrate(), file.Samplerate(), file.Channels())
	}

	p.Tracks[path] = track
	p.Stats.Tracks++
	return track, nil
}

func (p *Playlist) GetTrackByPath(path string) (*Track, error) {
	if track, found := p.Tracks[path]; found {
		return track, nil
	}
	return nil, fmt.Errorf("no such track")
}

func NewRadio(name string) *Radio {
	return &Radio{
		Name:             name,
		Playlists:        make([]*Playlist, 0),
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
	}
}

func init() {
	R = NewRadio("RadioMan")

	R.NewPlaylist("manual")
	R.NewDirectoryPlaylist("iTunes Music", "~/Music/iTunes/iTunes Media/Music/")
	R.NewDirectoryPlaylist("iTunes Podcasts", "~/Music/iTunes/iTunes Media/Podcasts/")
	dir, err := os.Getwd()
	if err == nil {
		R.NewDirectoryPlaylist("local directory", dir)
	}
	playlistsDir := path.Join(dir, "playlists")
	walker := fs.Walk(playlistsDir)
	for walker.Step() {
		if walker.Path() == playlistsDir {
			continue
		}
		if err := walker.Err(); err != nil {
			logrus.Warnf("walker error: %v", err)
			continue
		}
		realpath, err := filepath.EvalSymlinks(walker.Path())
		if err != nil {
			logrus.Warnf("filepath.EvalSymlinks error for %q: %v", walker.Path(), err)
			continue
		}

		R.NewDirectoryPlaylist(fmt.Sprintf("playlist: %s", walker.Stat().Name()), realpath)
	}

	playlist, _ := R.GetPlaylistByName("iTunes Music")
	R.DefaultPlaylist = playlist
}

func (r *Radio) NewPlaylist(name string) (*Playlist, error) {
	logrus.Infof("New playlist %q", name)
	playlist := &Playlist{
		Name:             name,
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
		Tracks:           make(map[string]*Track, 0),
		Status:           "New",
	}
	r.Playlists = append(r.Playlists, playlist)
	r.Stats.Playlists++
	return playlist, nil
}

func (r *Radio) NewDirectoryPlaylist(name string, path string) (*Playlist, error) {
	playlist, err := r.NewPlaylist(name)
	if err != nil {
		return nil, err
	}
	expandedPath, err := expandUser(path)
	if err != nil {
		return nil, err
	}
	playlist.Path = expandedPath
	return playlist, nil
}

func (r *Radio) GetPlaylistByName(name string) (*Playlist, error) {
	for _, playlist := range r.Playlists {
		if playlist.Name == name {
			return playlist, nil
		}
	}
	return nil, fmt.Errorf("no such playlist")
}

func main() {
	router := gin.Default()

	radio := R

	// ping
	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})

	// static files
	router.StaticFile("/", "./static/index.html")
	router.Static("/static", "./static")
	router.Static("/bower_components", "./bower_components")

	router.GET("/api/playlists", playlistsEndpoint)
	router.GET("/api/playlists/:name", playlistDetailEndpoint)
	router.GET("/api/playlists/:name/tracks", playlistTracksEndpoint)

	router.GET("/api/radios/default", defaultRadioEndpoint)

	router.GET("/api/liquidsoap/getNextSong", getNextSongEndpoint)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8000"
	}

	go updatePlaylistsRoutine(radio)

	router.Run(fmt.Sprintf(":%s", port))
}

func getNextSongEndpoint(c *gin.Context) {
	// FIXME: shuffle playlist instead of getting a random track
	// FIXME: do not iterate over a map

	playlist := R.DefaultPlaylist
	track, err := playlist.GetRandomTrack()
	if err == nil {
		c.String(http.StatusOK, track.Path)
		return
	}

	for _, playlist := range R.Playlists {
		track, err := playlist.GetRandomTrack()
		if err != nil {
			continue
		}
		c.String(http.StatusOK, track.Path)
		return
	}

	c.String(http.StatusNotFound, "# cannot get a random song, are your playlists empty ?")
}

func (p *Playlist) GetRandomTrack() (*Track, error) {
	validFiles := 0
	for _, track := range p.Tracks {
		if track.IsValid() {
			validFiles++
		}
	}

	if validFiles == 0 {
		return nil, fmt.Errorf("there is no available track")
	}

	i := rand.Intn(validFiles)
	for _, track := range p.Tracks {
		if !track.IsValid() {
			continue
		}
		if i <= 0 {
			return track, nil
		}
		i--
	}

	return nil, fmt.Errorf("cannot get a random track")
}

func updatePlaylistsRoutine(r *Radio) {
	for {
		tracksSum := 0
		for _, playlist := range r.Playlists {
			if playlist.Path == "" {
				logrus.Debugf("Playlist %q is not dynamic, skipping update", playlist.Name)
				continue
			}

			logrus.Infof("Updating playlist %q", playlist.Name)
			playlist.Status = "Updating"

			walker := fs.Walk(playlist.Path)
			for walker.Step() {
				if err := walker.Err(); err != nil {
					logrus.Warnf("walker error: %v", err)
					continue
				}
				stat := walker.Stat()

				if stat.IsDir() {
					switch stat.Name() {
					case ".git", "bower_components":
						walker.SkipDir()
					}
				} else {
					switch stat.Name() {
					case ".DS_Store":
						continue
					}

					playlist.NewLocalTrack(walker.Path())
				}
			}

			logrus.Infof("Playlist %q updated, %d tracks", playlist.Name, len(playlist.Tracks))
			playlist.Status = "Ready"
			playlist.ModificationDate = time.Now()
			tracksSum += playlist.Stats.Tracks
		}
		r.Stats.Tracks = tracksSum
		time.Sleep(5 * time.Minute)
	}
}

func playlistsEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"playlists": R.Playlists,
	})
}

func defaultRadioEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"radio": R,
	})
}

func playlistDetailEndpoint(c *gin.Context) {
	name := c.Param("name")
	playlist, err := R.GetPlaylistByName(name)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": err,
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"playlist": playlist,
	})
}

func playlistTracksEndpoint(c *gin.Context) {
	name := c.Param("name")
	playlist, err := R.GetPlaylistByName(name)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": err,
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"tracks": playlist.Tracks,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/jessevdk/go-flags"
)

const VERSION = "1.0.4"

type Options struct{}

var (
	options Options
	cmd     = flags.NewParser(&options, flags.Default)
)

func main() {
	cmd.SubcommandsOptional = true
	_, err := cmd.Parse()
	if err != nil {
		os.Exit(1)
	}

	if cmd.Command.Active == nil {
		cmd.WriteHelp(os.Stdout)
		os.Exit(1)
	}
}
<commit_msg>actually bump the version<commit_after>package main

import (
	"os"

	"github.com/jessevdk/go-flags"
)

const VERSION = "1.1.0"

type Options struct{}

var (
	options Options
	cmd     = flags.NewParser(&options, flags.Default)
)

func main() {
	cmd.SubcommandsOptional = true
	_, err := cmd.Parse()
	if err != nil {
		os.Exit(1)
	}

	if cmd.Command.Active == nil {
		cmd.WriteHelp(os.Stdout)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Package main is used as an entry point of
// the framework. It validates user input parameters
// and runs subcommands (aka tools).
package main

import (
	"os"

	"github.com/colegion/goal/commands/create"
	"github.com/colegion/goal/commands/generate"
	"github.com/colegion/goal/commands/run"
	"github.com/colegion/goal/internal/command"
	"github.com/colegion/goal/log"
)

// handlers is a map of registered subcommands (aka tools)
// the framework supports.
var handlers = command.NewContext()

func main() {
	var trace bool

	// Do not show stacktrace if something goes wrong
	// in case tracing is turned off.
	defer func() {
		if !trace {
			if err := recover(); err != nil {
				// Do nothing, error message has already been printed
				// and we do not need stack trace.
			}
		}
	}()

	// Enabling tracing if that is requested by a user.
	command.Helpers["--trace"] = func(val string) {
		if val == "true" {
			trace = true
		}
	}

	// Try to run the subcommand user requested.
	err := Handlers.Process(os.Args[1:]...)
	if err == command.ErrIncorrectArgs { // The arguments were not correct.
		log.Warn.Printf(unknownCmd, os.Args[1])
		return
	}
	if err != nil { // The arguments were omitted.
		Handlers.Process("help", "info") // Show a help message.
		return
	}
}

func init() {
	// Register the supported subcommands.
	Handlers.Register(create.Handler)
	Handlers.Register(run.Handler)
	Handlers.Register(generate.Handler)
	Handlers.Register(helpHandler)

	// Show header message when using new or help
	// commands.
	command.Helpers["new"] = showHeader
	command.Helpers["run"] = showHeader
	command.Helpers["help"] = showHeader
}

// showHeader prints a header message
// with the name of the project.
func showHeader(val string) {
	log.Trace.Println(header)
}

var unknownCmd = `Unknown command "%s".
Run "goal help" for usage.`
<commit_msg>main.go: reflect the latest changes in internal command subpackage<commit_after>// Package main is used as an entry point of
// the framework. It validates user input parameters
// and runs subcommands (aka tools).
package main

import (
	"os"
	"strings"

	"github.com/colegion/goal/internal/command"
	"github.com/colegion/goal/log"
)

// handlers is a stores information about the registered subcommands (aka tools)
// the framework supports.
var handlers = command.NewContext()

func main() {
	// Try to run the command user requested.
	// Ignoring the first argument as it is name of the executable.
	err := handlers.Run(os.Args[1:])
	switch err {
	case nil: // Handler's entry function has been successfully executed.
		// Do nothing.
	case command.ErrIncorrectArgs: // Incorrect command requested.
		log.Warn.Printf(unknownCmd, strings.Join(os.Args, " "))
	default: // Some other error has been received.
		log.Error.Println(err)
	}
}

var unknownCmd = `Unknown command "%s".
Run "goal help" for usage.`
<|endoftext|>