<commit_before>package main

import (
	"log"
	"time"

	"github.com/wjessop/go-piglow"
)

type PiGlow struct {
	*piglow.Piglow
}

func NewPiGlow() (*PiGlow, error) {
	parent, err := piglow.NewPiglow()
	if err != nil {
		return nil, err
	}
	return &PiGlow{parent}, nil
}

func (p *PiGlow) Glow(fnSetLeds func(uint8)) {
	for i := 0; i <= 255; i++ {
		fnSetLeds(uint8(i))
		p.Apply()
		time.Sleep(10 * time.Millisecond)
	}
}

func (p *PiGlow) Gloom(fnSetLeds func(uint8)) {
	for i := 255; i >= 0; i-- {
		fnSetLeds(uint8(i))
		p.Apply()
		time.Sleep(10 * time.Millisecond)
	}
}

func main() {
	p, err := NewPiGlow()
	if err != nil {
		log.Fatal("Couldn't create a Piglow: ", err)
	}

	p.Glow(func(brightness uint8) {
		p.SetAll(brightness)
	})
	p.Gloom(func(brightness uint8) {
		p.SetAll(brightness)
	})
}
<commit_msg>Small tweaks<commit_after>package main

import (
	"log"
	"time"

	"github.com/wjessop/go-piglow"
)

const (
	BrtMax   = 155
	BrtMin   = 0
	Interval = 10 * time.Millisecond
)

type PiGlow struct {
	*piglow.Piglow
}

func NewPiGlow() (*PiGlow, error) {
	parent, err := piglow.NewPiglow()
	if err != nil {
		return nil, err
	}
	return &PiGlow{parent}, nil
}

func (p *PiGlow) Glow(fnSetLeds func(uint8)) {
	for i := BrtMin; i <= BrtMax; i++ {
		fnSetLeds(uint8(i))
		if err := p.Apply(); err != nil {
			p.Error(err)
		}
		time.Sleep(Interval)
	}
}

func (p *PiGlow) Gloom(fnSetLeds func(uint8)) {
	for i := BrtMax; i >= BrtMin; i-- {
		fnSetLeds(uint8(i))
		if err := p.Apply(); err != nil {
			p.Error(err)
		}
		time.Sleep(Interval)
	}
}

func (p *PiGlow) Error(err error) {
	p.SetAll(0)
	p.Apply()
	log.Fatal("Couldn't apply changes: ", err)
}

func main() {
	p, err := NewPiGlow()
	if err != nil {
		log.Fatal("Couldn't create a Piglow: ", err)
	}
	p.Glow(func(brightness uint8) {
		p.SetAll(brightness)
	})
	p.Gloom(func(brightness uint8) {
		p.SetAll(brightness)
	})
}
<|endoftext|>


<commit_before>package main

//go:generate esc -o templates.go ./templates

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/bukalapak/snowboard/adapter/drafter"
	"github.com/bukalapak/snowboard/adapter/drafterc"
	"github.com/bukalapak/snowboard/mock"
	snowboard "github.com/bukalapak/snowboard/parser"
	"github.com/bukalapak/snowboard/render"
	"github.com/fsnotify/fsnotify"
	xerrors "github.com/pkg/errors"
	cli "gopkg.in/urfave/cli.v1"
)

var versionStr string
var (
	engine  snowboard.Parser
	engineC snowboard.Parser
)

func main() {
	engine = drafter.Engine{}
	engineC = drafterc.Engine{}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Fprintf(c.App.Writer, "Snowboard version: %s\n", c.App.Version)
		fmt.Fprintf(c.App.Writer, "Drafter version: %s\n", engine.Version())
	}

	if versionStr == "" {
		versionStr = "HEAD"
	}

	app := cli.NewApp()
	app.Name = "snowboard"
	app.Usage = "API blueprint toolkit"
	app.Version = versionStr
	app.Before = func(c *cli.Context) error {
		if c.Args().Present() && c.Args().Get(1) == "" {
			cli.ShowCommandHelp(c, c.Args().Get(0))
		}

		return nil
	}
	app.Commands = []cli.Command{
		{
			Name:  "lint",
			Usage: "Validate API blueprint",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "c",
					Usage: "Use character index instead of line and row number",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := validate(c, c.Args().Get(0), c.Bool("c")); err != nil {
					if strings.Contains(err.Error(), "read failed") {
						return xerrors.Cause(err)
					}

					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "html",
			Usage: "Render HTML documentation",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Usage: "HTML file",
				},
				cli.StringFlag{
					Name:  "t",
					Value: "alpha",
					Usage: "Template for HTML documentation",
				},
				cli.BoolFlag{
					Name:  "s",
					Usage: "Serve HTML via HTTP server",
				},
				cli.StringFlag{
					Name:  "b",
					Value: "127.0.0.1:8088",
					Usage: "HTTP server listen address",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if c.Bool("s") {
					if err := watchHTML(c, c.Args().Get(0), c.String("o"), c.String("t"), c.String("b")); err != nil {
						return cli.NewExitError(err.Error()+"\n", 1)
					}
				}

				if err := renderHTML(c, c.Args().Get(0), c.String("o"), c.String("t")); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "apib",
			Usage: "Render API blueprint",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Usage: "API blueprint output file",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := renderAPIB(c, c.Args().Get(0), c.String("o")); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "json",
			Usage: "Render API element json",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Usage: "API element output file",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := renderJSON(c, c.Args().Get(0), c.String("o")); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "mock",
			Usage: "Run Mock server",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "b",
					Value: "127.0.0.1:8087",
					Usage: "HTTP server listen address",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := serveMock(c, c.String("b"), c.Args().Get(0)); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
	}

	app.Run(os.Args)
}

func readFile(fn string) ([]byte, error) {
	info, err := os.Stat(fn)
	if err != nil {
		return nil, errors.New("File is not exist")
	}

	if info.IsDir() {
		return nil, errors.New("File is a directory")
	}

	return ioutil.ReadFile(fn)
}

func readTemplate(fn string) ([]byte, error) {
	tf, err := readFile(fn)
	if err == nil {
		return tf, nil
	}

	fs := FS(false)
	ff, err := fs.Open("/templates/" + fn + ".html")
	if err != nil {
		return nil, err
	}

	defer ff.Close()
	return ioutil.ReadAll(ff)
}

func renderHTML(c *cli.Context, input, output, tplFile string) error {
	bp, err := snowboard.Load(input, engine)
	if err != nil {
		return err
	}

	tf, err := readTemplate(tplFile)
	if err != nil {
		return err
	}

	if output == "" {
		var bf bytes.Buffer

		if err := render.HTML(string(tf), &bf, bp); err != nil {
			return err
		}

		fmt.Fprintln(c.App.Writer, bf.String())
		return nil
	}

	of, err := os.Create(output)
	if err != nil {
		return err
	}
	defer of.Close()

	err = render.HTML(string(tf), of, bp)
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "%s: HTML has been generated!\n", of.Name())
	return nil
}

func renderAPIB(c *cli.Context, input, output string) error {
	b, err := snowboard.Read(input)
	if err != nil {
		return err
	}

	if output == "" {
		fmt.Fprintln(c.App.Writer, string(b))
		return nil
	}

	of, err := os.Create(output)
	if err != nil {
		return err
	}
	defer of.Close()

	_, err = io.Copy(of, bytes.NewReader(b))
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "%s: API blueprint has been generated!\n", of.Name())
	return nil
}

func renderJSON(c *cli.Context, input, output string) error {
	b, err := snowboard.LoadAsJSON(input, engine)
	if err != nil {
		return err
	}

	if output == "" {
		fmt.Fprintln(c.App.Writer, string(b))
		return nil
	}

	of, err := os.Create(output)
	if err != nil {
		return err
	}
	defer of.Close()

	_, err = io.Copy(of, bytes.NewReader(b))
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "%s: API element JSON has been generated!\n", of.Name())
	return nil
}

func validate(c *cli.Context, input string, charIdx bool) error {
	b, err := readFile(input)
	if err != nil {
		return xerrors.Wrap(err, "read failed")
	}

	bf := bytes.NewReader(b)

	if charIdx {
		out, err := snowboard.Validate(bf, engine)
		if err != nil {
			return err
		}

		if out == nil {
			fmt.Fprintln(c.App.Writer, "OK")
			return nil
		}

		var buf bytes.Buffer

		s := "--------"
		w := tabwriter.NewWriter(&buf, 8, 0, 0, ' ', tabwriter.Debug)
		fmt.Fprintln(w, "Char Index\tDescription")
		fmt.Fprintf(w, "%s\t%s\n", s, strings.Repeat(s, 8))

		for _, n := range out.Annotations {
			for _, m := range n.SourceMaps {
				fmt.Fprintf(w, "%d:%d\t%s\n", m.Row, m.Col, n.Description)
			}
		}

		w.Flush()

		if len(out.Annotations) > 0 {
			return errors.New(buf.String())
		}
	} else {
		b, err := engineC.Validate(bf)
		if err != nil {
			return err
		}

		var buf bytes.Buffer

		w := tabwriter.NewWriter(&buf, 8, 0, 0, ' ', tabwriter.Debug)
		fmt.Fprintln(w, "Location\tSeverity\tDescription")

		fmt.Fprintf(w, "%s\t%s\t%s\n", dash(42), dash(16), dash(80))

		if string(b) == "" {
			fmt.Fprintln(c.App.Writer, "OK")
			return nil
		}

		ns := strings.Split(string(b), "\n")

		for _, n := range ns {
			nn := strings.SplitN(n, "; ", 2)
			mm := strings.SplitN(nn[0], "  ", 2)
			fmt.Fprintf(w, "%s\t%s\t%s\n", nn[1], mm[0], mm[1])
		}

		w.Flush()
		return errors.New(buf.String())
	}

	return nil
}

func dash(n int) string {
	return strings.Repeat("-", n)
}

func watchHTML(c *cli.Context, input, output, tplFile, bind string) error {
	if output == "" {
		output = "index.html"
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					if err := renderHTML(c, input, output, tplFile); err != nil {
						fmt.Fprintln(c.App.Writer, err)
					}
				}
			case err := <-watcher.Errors:
				if err != nil {
					fmt.Fprintln(c.App.Writer, err)
				}
			}
		}
	}()

	if err = watcher.Add(input); err != nil {
		return err
	}

	if _, err = os.Stat(tplFile); err == nil {
		if err = watcher.Add(tplFile); err != nil {
			return err
		}
	}

	if err := renderHTML(c, input, output, tplFile); err != nil {
		return err
	}

	for _, s := range snowboard.Seeds(input) {
		if err = watcher.Add(s); err != nil {
			return err
		}
	}

	fmt.Fprintf(c.App.Writer, "snowboard: listening on %s\n", bind)

	if err := serveHTML(bind, output); err != nil {
		return err
	}

	<-done

	return nil
}

func serveHTML(bind, output string) error {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, output)
	})

	return http.ListenAndServe(bind, nil)
}

func serveMock(c *cli.Context, bind, input string) error {
	bp, err := snowboard.Load(input, engine)
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "Mock server is ready. Use %s\n", bind)
	fmt.Fprintln(c.App.Writer, "Available Routes:")

	ms := mock.Mock(bp)
	for _, m := range ms {
		fmt.Fprintf(c.App.Writer, "%s\t%d\t%s\n", m.Method, m.StatusCode, m.Pattern)
	}

	h := mock.MockHandler(ms)
	return http.ListenAndServe(bind, h)
}
<commit_msg>added polling support<commit_after>package main

//go:generate esc -o templates.go ./templates

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/bukalapak/snowboard/adapter/drafter"
	"github.com/bukalapak/snowboard/adapter/drafterc"
	"github.com/bukalapak/snowboard/mock"
	snowboard "github.com/bukalapak/snowboard/parser"
	"github.com/bukalapak/snowboard/render"
	"github.com/fsnotify/fsnotify"
	xerrors "github.com/pkg/errors"
	pWatcher "github.com/radovskyb/watcher"
	cli "gopkg.in/urfave/cli.v1"
)

var versionStr string
var (
	engine  snowboard.Parser
	engineC snowboard.Parser
)

func main() {
	engine = drafter.Engine{}
	engineC = drafterc.Engine{}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Fprintf(c.App.Writer, "Snowboard version: %s\n", c.App.Version)
		fmt.Fprintf(c.App.Writer, "Drafter version: %s\n", engine.Version())
	}

	if versionStr == "" {
		versionStr = "HEAD"
	}

	app := cli.NewApp()
	app.Name = "snowboard"
	app.Usage = "API blueprint toolkit"
	app.Version = versionStr
	app.Before = func(c *cli.Context) error {
		if c.Args().Present() && c.Args().Get(1) == "" {
			cli.ShowCommandHelp(c, c.Args().Get(0))
		}

		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "watch-interval",
			Usage: "Set watch interval. This activates polling watcher. Accepted format like: 100ms, 1s, etc",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:  "lint",
			Usage: "Validate API blueprint",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "c",
					Usage: "Use character index instead of line and row number",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := validate(c, c.Args().Get(0), c.Bool("c")); err != nil {
					if strings.Contains(err.Error(), "read failed") {
						return xerrors.Cause(err)
					}

					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "html",
			Usage: "Render HTML documentation",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Usage: "HTML file",
				},
				cli.StringFlag{
					Name:  "t",
					Value: "alpha",
					Usage: "Template for HTML documentation",
				},
				cli.BoolFlag{
					Name:  "s",
					Usage: "Serve HTML via HTTP server",
				},
				cli.StringFlag{
					Name:  "b",
					Value: "127.0.0.1:8088",
					Usage: "HTTP server listen address",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if n := c.GlobalString("watch-interval"); n != "" {
					d, err := time.ParseDuration(c.GlobalString("watch-interval"))
					if err != nil {
						return cli.NewExitError(fmt.Errorf("invalid value for `watch-interval`: %s", err), 1)
					}

					if c.Bool("s") {
						if err := watchIntervalHTML(c, c.Args().Get(0), c.String("o"), c.String("t"), c.String("b"), d); err != nil {
							return cli.NewExitError(err.Error()+"\n", 1)
						}
					}

					return nil
				}

				if c.Bool("s") {
					if err := watchHTML(c, c.Args().Get(0), c.String("o"), c.String("t"), c.String("b")); err != nil {
						return cli.NewExitError(err.Error()+"\n", 1)
					}
				}

				if err := renderHTML(c, c.Args().Get(0), c.String("o"), c.String("t")); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "apib",
			Usage: "Render API blueprint",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Usage: "API blueprint output file",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := renderAPIB(c, c.Args().Get(0), c.String("o")); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "json",
			Usage: "Render API element json",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o",
					Usage: "API element output file",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := renderJSON(c, c.Args().Get(0), c.String("o")); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
		{
			Name:  "mock",
			Usage: "Run Mock server",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "b",
					Value: "127.0.0.1:8087",
					Usage: "HTTP server listen address",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "" {
					return nil
				}

				if err := serveMock(c, c.String("b"), c.Args().Get(0)); err != nil {
					return cli.NewExitError(err.Error(), 1)
				}

				return nil
			},
		},
	}

	app.Run(os.Args)
}

func readFile(fn string) ([]byte, error) {
	info, err := os.Stat(fn)
	if err != nil {
		return nil, errors.New("File is not exist")
	}

	if info.IsDir() {
		return nil, errors.New("File is a directory")
	}

	return ioutil.ReadFile(fn)
}

func readTemplate(fn string) ([]byte, error) {
	tf, err := readFile(fn)
	if err == nil {
		return tf, nil
	}

	fs := FS(false)
	ff, err := fs.Open("/templates/" + fn + ".html")
	if err != nil {
		return nil, err
	}

	defer ff.Close()
	return ioutil.ReadAll(ff)
}

func renderHTML(c *cli.Context, input, output, tplFile string) error {
	bp, err := snowboard.Load(input, engine)
	if err != nil {
		return err
	}

	tf, err := readTemplate(tplFile)
	if err != nil {
		return err
	}

	if output == "" {
		var bf bytes.Buffer

		if err := render.HTML(string(tf), &bf, bp); err != nil {
			return err
		}

		fmt.Fprintln(c.App.Writer, bf.String())
		return nil
	}

	of, err := os.Create(output)
	if err != nil {
		return err
	}
	defer of.Close()

	err = render.HTML(string(tf), of, bp)
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "[%s] %s: HTML has been generated!\n", time.Now().Format(time.RFC3339), of.Name())
	return nil
}

func renderAPIB(c *cli.Context, input, output string) error {
	b, err := snowboard.Read(input)
	if err != nil {
		return err
	}

	if output == "" {
		fmt.Fprintln(c.App.Writer, string(b))
		return nil
	}

	of, err := os.Create(output)
	if err != nil {
		return err
	}
	defer of.Close()

	_, err = io.Copy(of, bytes.NewReader(b))
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "%s: API blueprint has been generated!\n", of.Name())
	return nil
}

func renderJSON(c *cli.Context, input, output string) error {
	b, err := snowboard.LoadAsJSON(input, engine)
	if err != nil {
		return err
	}

	if output == "" {
		fmt.Fprintln(c.App.Writer, string(b))
		return nil
	}

	of, err := os.Create(output)
	if err != nil {
		return err
	}
	defer of.Close()

	_, err = io.Copy(of, bytes.NewReader(b))
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "%s: API element JSON has been generated!\n", of.Name())
	return nil
}

func validate(c *cli.Context, input string, charIdx bool) error {
	b, err := readFile(input)
	if err != nil {
		return xerrors.Wrap(err, "read failed")
	}

	bf := bytes.NewReader(b)

	if charIdx {
		out, err := snowboard.Validate(bf, engine)
		if err != nil {
			return err
		}

		if out == nil {
			fmt.Fprintln(c.App.Writer, "OK")
			return nil
		}

		var buf bytes.Buffer

		s := "--------"
		w := tabwriter.NewWriter(&buf, 8, 0, 0, ' ', tabwriter.Debug)
		fmt.Fprintln(w, "Char Index\tDescription")
		fmt.Fprintf(w, "%s\t%s\n", s, strings.Repeat(s, 8))

		for _, n := range out.Annotations {
			for _, m := range n.SourceMaps {
				fmt.Fprintf(w, "%d:%d\t%s\n", m.Row, m.Col, n.Description)
			}
		}

		w.Flush()

		if len(out.Annotations) > 0 {
			return errors.New(buf.String())
		}
	} else {
		b, err := engineC.Validate(bf)
		if err != nil {
			return err
		}

		var buf bytes.Buffer

		w := tabwriter.NewWriter(&buf, 8, 0, 0, ' ', tabwriter.Debug)
		fmt.Fprintln(w, "Location\tSeverity\tDescription")

		fmt.Fprintf(w, "%s\t%s\t%s\n", dash(42), dash(16), dash(80))

		if string(b) == "" {
			fmt.Fprintln(c.App.Writer, "OK")
			return nil
		}

		ns := strings.Split(string(b), "\n")

		for _, n := range ns {
			nn := strings.SplitN(n, "; ", 2)
			mm := strings.SplitN(nn[0], "  ", 2)
			fmt.Fprintf(w, "%s\t%s\t%s\n", nn[1], mm[0], mm[1])
		}

		w.Flush()
		return errors.New(buf.String())
	}

	return nil
}

func dash(n int) string {
	return strings.Repeat("-", n)
}

type fsWatcher interface {
	Add(string) error
}

func watchHTML(c *cli.Context, input, output, tplFile, bind string) error {
	if output == "" {
		output = "index.html"
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					if err := renderHTML(c, input, output, tplFile); err != nil {
						fmt.Fprintln(c.App.Writer, err)
					}
				}
			case err := <-watcher.Errors:
				if err != nil {
					fmt.Fprintln(c.App.Writer, err)
				}
			}
		}
	}()

	if err := watchFiles(c, watcher, input, output, tplFile, bind); err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "snowboard: listening on %s\n", bind)

	if err := serveHTML(bind, output); err != nil {
		return err
	}

	<-done

	return nil
}

func watchIntervalHTML(c *cli.Context, input, output, tplFile, bind string, interval time.Duration) error {
	if output == "" {
		output = "index.html"
	}

	watcher := pWatcher.New()

	go func() {
		for {
			select {
			case event := <-watcher.Event:
				if event.Op&pWatcher.Write == pWatcher.Write {
					if err := renderHTML(c, input, output, tplFile); err != nil {
						fmt.Fprintln(c.App.Writer, err)
					}
				}
			case err := <-watcher.Error:
				if err != nil {
					fmt.Fprintln(c.App.Writer, err)
				}
			case <-watcher.Closed:
				return
			}
		}
	}()

	if err := watchFiles(c, watcher, input, output, tplFile, bind); err != nil {
		return err
	}

	go func() {
		watcher.Wait()
	}()

	if err := watcher.Start(interval); err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "snowboard: listening on %s\n", bind)

	if err := serveHTML(bind, output); err != nil {
		return err
	}

	return nil
}

func watchFiles(c *cli.Context, watcher fsWatcher, input, output, tplFile, bind string) error {
	if err := watcher.Add(input); err != nil {
		return err
	}

	if _, err := os.Stat(tplFile); err == nil {
		if err = watcher.Add(tplFile); err != nil {
			return err
		}
	}

	if err := renderHTML(c, input, output, tplFile); err != nil {
		return err
	}

	for _, s := range snowboard.Seeds(input) {
		if err := watcher.Add(s); err != nil {
			return err
		}
	}

	return nil
}

func serveHTML(bind, output string) error {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, output)
	})

	return http.ListenAndServe(bind, nil)
}

func serveMock(c *cli.Context, bind, input string) error {
	bp, err := snowboard.Load(input, engine)
	if err != nil {
		return err
	}

	fmt.Fprintf(c.App.Writer, "Mock server is ready. Use %s\n", bind)
	fmt.Fprintln(c.App.Writer, "Available Routes:")

	ms := mock.Mock(bp)
	for _, m := range ms {
		fmt.Fprintf(c.App.Writer, "%s\t%d\t%s\n", m.Method, m.StatusCode, m.Pattern)
	}

	h := mock.MockHandler(ms)
	return http.ListenAndServe(bind, h)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	"github.com/BurntSushi/toml"
	_ "github.com/PuerkitoBio/goquery"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/urfave/cli"
)

var (
	Version   string
	Revision  string
	useragent string = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1)"
)

type configFile struct {
	Urls []string `toml:"urls"`
}

func main() {
	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
	}
	home, err := homedir.Dir()
	if err != nil {
		return
	}
	dir := filepath.Join(home, ".config", "gocrawsan")
	if err = os.MkdirAll(dir, 0700); err != nil {
		return
	}
	app := cli.NewApp()
	app.Name = "gocrawsan"
	app.Usage = "web crawling command utility"
	app.Version = Version
	app.Usage = ""
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "urls",
		},
		cli.StringFlag{
			Name:  "config, C",
			Value: filepath.Join(dir, "config.toml"),
		},
		cli.BoolFlag{
			Name: "debug, D",
		},
	}
	app.Action = crawling
	app.Run(os.Args)
}

func crawling(c *cli.Context) error {
	wg := new(sync.WaitGroup)
	m := new(sync.Mutex)
	buf, err := ioutil.ReadFile(c.String("config"))
	if err != nil {
		return err
	}
	file := &configFile{}
	err = toml.Unmarshal(buf, file)

	for _, url := range file.Urls {
		wg.Add(1)
		go getUrl(wg, m, url)
	}
	wg.Wait()
	return nil
}

func getUrl(wg *sync.WaitGroup, m *sync.Mutex, url string) {
	client := &http.Client{}
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
	}
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", useragent)

	resp, _ := client.Do(req)
	status := strings.Split(resp.Status, " ")
	code, _ := strconv.Atoi(status[0])

	m.Lock()
	defer m.Unlock()
	fmt.Print(url + "\t")

	switch code / 100 {
	case 2:
		color.Cyan(resp.Status)
	case 3:
		color.Yellow(resp.Status)
	case 4:
		color.Red(resp.Status)
	default:
		fmt.Println(resp.Status)
	}
	wg.Done()
}
<commit_msg>Add getLink function<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/BurntSushi/toml"
	"github.com/PuerkitoBio/goquery"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/urfave/cli"
)

var (
	Version  string
	Revision string
)

type configFile struct {
	Urls      []string `toml:"urls"`
	UserAgent string   `toml:"useragent"`
}

type requestSetting struct {
	url       string
	useragent string
}

func main() {
	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
	}
	home, err := homedir.Dir()
	if err != nil {
		return
	}
	dir := filepath.Join(home, ".config", "gocrawsan")
	if err = os.MkdirAll(dir, 0700); err != nil {
		return
	}
	app := cli.NewApp()
	app.Name = "gocrawsan"
	app.Usage = "web crawling command utility"
	app.Version = Version
	app.Usage = ""
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "useragent, U",
		},
		cli.StringFlag{
			Name:  "config, C",
			Value: filepath.Join(dir, "config.toml"),
		},
		cli.BoolFlag{
			Name: "debug, D",
		},
	}
	app.Action = crawling
	app.Run(os.Args)
}

func crawling(c *cli.Context) error {
	wg := new(sync.WaitGroup)
	m := new(sync.Mutex)

	ua := c.String("useragent")
	buf, err := ioutil.ReadFile(c.String("config"))
	if err != nil {
		return err
	}
	file := &configFile{}
	err = toml.Unmarshal(buf, file)
	if err != nil {
		return err
	}

	for _, url := range file.Urls {
		wg.Add(1)
		s := &requestSetting{
			url:       url,
			useragent: ua,
		}
		go getUrl(wg, m, s)
	}
	wg.Wait()
	return nil
}

func getUrl(wg *sync.WaitGroup, m *sync.Mutex, s *requestSetting) {
	client := &http.Client{}
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
	}
	req, _ := http.NewRequest("GET", s.url, nil)
	req.Header.Set("User-Agent", s.useragent)

	resp, _ := client.Do(req)
	status := strings.Split(resp.Status, " ")
	code, _ := strconv.Atoi(status[0])

	m.Lock()
	fmt.Print(s.url + "\t")

	switch code / 100 {
	case 2:
		color.Cyan(resp.Status)
	case 3:
		color.Yellow(resp.Status)
	case 4:
		color.Red(resp.Status)
	default:
		fmt.Println(resp.Status)
	}
	m.Unlock()
	_, _ := getLinks(resp)
	wg.Done()
}

func getLinks(res *http.Response) ([]string, error) {
	urls := []string{}
	doc, _ := goquery.NewDocumentFromResponse(res)
	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
		url, _ := s.Attr("href")
		r := regexp.MustCompile(`^(https|http)://(.*)`)
		if !r.MatchString(url) {
			url = res.Request.URL.Scheme + "://" + res.Request.URL.Host + url
		}
		urls = append(urls, url)
	})
	return urls, nil
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"

	tl "github.com/JoelOtter/termloop"
	"github.com/andoco/gotermlife/sim"
)

type SimLevel struct {
	*tl.BaseLevel
	sim *sim.S
}

var liveCell *tl.Cell

func (sl *SimLevel) Tick(event tl.Event) {
	if event.Type == tl.EventKey { // Is it a keyboard event?
		switch event.Key { // If so, switch on the pressed key.
		case tl.KeySpace:
			sl.sim.Tick()
		}
	}
}

func (sl *SimLevel) Draw(s *tl.Screen) {
	for _, c := range sl.sim.Cells {
		if c.Live {
			s.RenderCell(c.Pos.X, c.Pos.Y, liveCell)
		}
	}
}

func main() {
	liveCell = &tl.Cell{Ch: '◼'}

	s := sim.New()
	//s.Seed([]sim.P{{5, 5}, {6, 5}, {7, 5}})

	seed := []sim.P{}
	for i := 0; i < 1000; i++ {
		seed = append(seed, sim.P{rand.Intn(80), rand.Intn(80)})
	}
	s.Seed(seed)

	game := tl.NewGame()

	level := tl.NewBaseLevel(tl.Cell{})

	simLevel := &SimLevel{level, s}

	game.Screen().SetLevel(simLevel)
	game.Start()
}
<commit_msg>Support scrolling the sim space<commit_after>package main

import (
	"math/rand"

	tl "github.com/JoelOtter/termloop"
	"github.com/andoco/gotermlife/sim"
)

type SimLevel struct {
	*tl.BaseLevel
	sim     *sim.S
	offsetX int
	offsetY int
}

var liveCell *tl.Cell

func (sl *SimLevel) Tick(event tl.Event) {
	if event.Type == tl.EventKey { // Is it a keyboard event?
		switch event.Key { // If so, switch on the pressed key.
		case tl.KeySpace:
			sl.sim.Tick()
		case tl.KeyArrowUp:
			sl.offsetY += 1
		case tl.KeyArrowDown:
			sl.offsetY -= 1
		case tl.KeyArrowLeft:
			sl.offsetX += 1
		case tl.KeyArrowRight:
			sl.offsetX -= 1
		}
	}

	sl.sim.Tick()
}

func (sl *SimLevel) Draw(s *tl.Screen) {
	for _, c := range sl.sim.Cells {
		if c.Live {
			s.RenderCell(sl.offsetX+c.Pos.X, sl.offsetY+c.Pos.Y, liveCell)
		}
	}
}

func main() {
	liveCell = &tl.Cell{Ch: '◼'}

	s := sim.New()
	//s.Seed([]sim.P{{5, 5}, {6, 5}, {7, 5}})

	seed := []sim.P{}
	for i := 0; i < 1000; i++ {
		seed = append(seed, sim.P{rand.Intn(80), rand.Intn(80)})
	}
	s.Seed(seed)

	game := tl.NewGame()

	level := tl.NewBaseLevel(tl.Cell{})

	simLevel := &SimLevel{level, s, 0, 0}

	game.Screen().SetLevel(simLevel)
	game.Screen().SetFps(10)
	game.Start()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	cspb "github.com/grobian/carbonserver/carbonserverpb"
)

type zipper string

var Zipper zipper

// FIXME(dgryski): extract the http.Get + unproto code into its own function

func (z zipper) Find(metric string) (cspb.GlobResponse, error) {

	u, _ := url.Parse(string(z) + "/metrics/find/")

	u.RawQuery = url.Values{
		"query":  []string{metric},
		"format": []string{"protobuf"},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Find: http.Get: %+v\n", err)
		return cspb.GlobResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Find: ioutil.ReadAll: %+v\n", err)
		return cspb.GlobResponse{}, err
	}

	var pbresp cspb.GlobResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Find: proto.Unmarshal: %+v\n", err)
		return cspb.GlobResponse{}, err
	}

	return pbresp, nil
}

func (z zipper) Render(metric, from, until string) (cspb.FetchResponse, error) {

	u, _ := url.Parse(string(z) + "/render/")

	u.RawQuery = url.Values{
		"target": []string{metric},
		"format": []string{"protobuf"},
		"from":   []string{from},
		"until":  []string{until},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Render: http.Get: %s: %+v\n", metric, err)
		return cspb.FetchResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Render: ioutil.ReadAll: %s: %+v\n", metric, err)
		return cspb.FetchResponse{}, err
	}

	var pbresp cspb.FetchResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Render: proto.Unmarshal: %s: %+v\n", metric, err)
		return cspb.FetchResponse{}, err
	}

	return pbresp, nil
}

type limiter chan struct{}

func (l limiter) enter() { l <- struct{}{} }
func (l limiter) leave() { <-l }

var Limiter limiter

func renderHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	targets := r.Form["target"]
	from := r.FormValue("from")
	until := r.FormValue("until")
	if until == "" {
		until = strconv.Itoa(int(time.Now().Unix()))
	}
	var results []*cspb.FetchResponse
	// query zipper for find
	for _, target := range targets {
		glob, err := Zipper.Find(target)
		if err != nil {
			continue
		}

		// for each server in find response query render
		rch := make(chan *cspb.FetchResponse, len(glob.GetMatches()))
		for _, m := range glob.GetMatches() {
			go func(m *cspb.GlobMatch) {
				Limiter.enter()
				if m.GetIsLeaf() {
					r, err := Zipper.Render(m.GetPath(), from, until)
					if err != nil {
						rch <- nil
					} else {
						rch <- &r
					}
				} else {
					rch <- nil
				}
				Limiter.leave()
			}(m)
		}

		for i := 0; i < len(glob.GetMatches()); i++ {
			r := <-rch
			if r != nil {
				results = append(results, r)
			}
		}
	}

	w.Header().Set("Content-Type", "application/json")
	jEnc := json.NewEncoder(w)
	jEnc.Encode(results)
}

func main() {

	z := flag.String("z", "", "zipper")
	port := flag.Int("p", 8080, "port")
	l := flag.Int("l", 20, "concurrency limit")

	flag.Parse()

	if *z == "" {
		log.Fatal("no zipper (-z) provided")
	}

	Limiter = make(chan struct{}, *l)

	if _, err := url.Parse(*z); err != nil {
		log.Fatal("unable to parze zipper:", err)
	}

	Zipper = zipper(*z)

	http.HandleFunc("/render/", renderHandler)

	log.Println("listening on port", *port)
	log.Fatalln(http.ListenAndServe(":"+strconv.Itoa(*port), nil))

}
<commit_msg>Better handling of from and until parameters:  - parsing Graphite date formats (HH:MM_YYYYMMDD, YYYYMMDD, MM/DD/YY)  - if not specified, until defaults to now  - if not specified, from defaults to now - 24 hours<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	cspb "github.com/grobian/carbonserver/carbonserverpb"
)

type zipper string

var Zipper zipper

var timeFormats = []string{"15:04 20060102", "20060102", "01/02/06"}

func ParseDate(s string, d int64) string {
	// Function to parse from and until parameters.
	_, err := strconv.Atoi(s)
	if err == nil && len(s) > 8 {
		return s // We got a timestamp so returning it
	} else if strings.Contains(s, "_") {
		s = strings.Replace(s, "_", " ", 1) // Go can't parse _ in date strings
	}
	for _, format := range timeFormats {
		t, err := time.Parse(format, s)
		if err == nil {
			return strconv.Itoa(int(t.Unix()))
		}
	}
	return strconv.Itoa(int(d))
}

// FIXME(dgryski): extract the http.Get + unproto code into its own function

func (z zipper) Find(metric string) (cspb.GlobResponse, error) {

	u, _ := url.Parse(string(z) + "/metrics/find/")

	u.RawQuery = url.Values{
		"query":  []string{metric},
		"format": []string{"protobuf"},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Find: http.Get: %+v\n", err)
		return cspb.GlobResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Find: ioutil.ReadAll: %+v\n", err)
		return cspb.GlobResponse{}, err
	}

	var pbresp cspb.GlobResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Find: proto.Unmarshal: %+v\n", err)
		return cspb.GlobResponse{}, err
	}

	return pbresp, nil
}

func (z zipper) Render(metric, from, until string) (cspb.FetchResponse, error) {

	u, _ := url.Parse(string(z) + "/render/")

	u.RawQuery = url.Values{
		"target": []string{metric},
		"format": []string{"protobuf"},
		"from":   []string{from},
		"until":  []string{until},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Render: http.Get: %s: %+v\n", metric, err)
		return cspb.FetchResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Render: ioutil.ReadAll: %s: %+v\n", metric, err)
		return cspb.FetchResponse{}, err
	}

	var pbresp cspb.FetchResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Render: proto.Unmarshal: %s: %+v\n", metric, err)
		return cspb.FetchResponse{}, err
	}

	return pbresp, nil
}

type limiter chan struct{}

func (l limiter) enter() { l <- struct{}{} }
func (l limiter) leave() { <-l }

var Limiter limiter

func renderHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	targets := r.Form["target"]
	from := r.FormValue("from")
	until := r.FormValue("until")

	// normalize from and until values
	from = ParseDate(from, time.Now().Add(-24 * time.Hour).Unix())
	until = ParseDate(until, time.Now().Unix())

	var results []*cspb.FetchResponse
	// query zipper for find
	for _, target := range targets {
		glob, err := Zipper.Find(target)
		if err != nil {
			continue
		}

		// for each server in find response query render
		rch := make(chan *cspb.FetchResponse, len(glob.GetMatches()))
		for _, m := range glob.GetMatches() {
			go func(m *cspb.GlobMatch) {
				Limiter.enter()
				if m.GetIsLeaf() {
					r, err := Zipper.Render(m.GetPath(), from, until)
					if err != nil {
						rch <- nil
					} else {
						rch <- &r
					}
				} else {
					rch <- nil
				}
				Limiter.leave()
			}(m)
		}

		for i := 0; i < len(glob.GetMatches()); i++ {
			r := <-rch
			if r != nil {
				results = append(results, r)
			}
		}
	}

	w.Header().Set("Content-Type", "application/json")
	jEnc := json.NewEncoder(w)
	jEnc.Encode(results)
}

func main() {

	z := flag.String("z", "", "zipper")
	port := flag.Int("p", 8080, "port")
	l := flag.Int("l", 20, "concurrency limit")

	flag.Parse()

	if *z == "" {
		log.Fatal("no zipper (-z) provided")
	}

	Limiter = make(chan struct{}, *l)

	if _, err := url.Parse(*z); err != nil {
		log.Fatal("unable to parze zipper:", err)
	}

	Zipper = zipper(*z)

	http.HandleFunc("/render/", renderHandler)

	log.Println("listening on port", *port)
	log.Fatalln(http.ListenAndServe(":"+strconv.Itoa(*port), nil))

}
<|endoftext|>


<commit_before>/* The MIT License (MIT)

Copyright (c) 2015 Alexandre Lion <contact@alexandrelion.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"regexp"
)

type Line struct {
	Type    map[string]string
	Content string
	Chords  map[int]string
}

// Chord format reference
var types = map[string]string{
	"t":   "title",
	"st":  "subtitle",
	"soc": "start_of_chorus",
	"eoc": "end_of_chorus",
	"c":   "comment",
	"sot": "start_of_tab",
	"eot": "end_of_tab",
}

// readLines gets content of a file using its path.
// Using bufio Scanner to append each line into an array.
// It returns the array with lines and any Scanner error encountered.
func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

// parseLine defines the type of the string following the ChordPro convention.
// Using regexp to determine the type.
// It returns the type of the string, its content without directives and any error encountered.
func parseLine(rawLine string) (map[string]string, string, error) {

	if rawLine == "" {
		return map[string]string{"b": "blank"}, "", nil // Blank line
	}

	for key, value := range types {
		matchKey, err := regexp.MatchString(fmt.Sprint("^{(", key, ")"), rawLine)
		matchValue, err := regexp.MatchString(fmt.Sprint("^{(", value, ")"), rawLine)

		if matchKey {
			return map[string]string{key: value}, rawLine[1+len(key)+1 : len(rawLine)-1], err
		} else if matchValue {
			return map[string]string{key: value}, rawLine[1+len(value)+1 : len(rawLine)-1], err
		}
	}
	return map[string]string{"v": "verse"}, rawLine, nil // Simple line: verse
}

// getChords gets position of every chords in the line.
// Using regexp to determine the positions.
// It returns the map of chords and their position.
func getChords(rawLine string) (map[int]string, error) {
	mappedChords := make(map[int]string)
	r, err := regexp.Compile("\\[[A-Za-z0-9#\\/]+\\]")
	matchChordIndex := r.FindAllStringIndex(rawLine, -1)
	matchChord := r.FindAllString(rawLine, -1)

	for i, position := range matchChordIndex {
		mappedChords[position[0]] = string(matchChord[i])
	}

	return mappedChords, err
}

// truncChords remove chord from line.
// It browses the line char by char to exclude chords.
// It returns the cleaned line.
func truncChords(rawLine string) string {
	var buf bytes.Buffer
	var insertMode bool = false

	for i := 0; i < len(rawLine); i++ {
		if string(rawLine[i]) == "[" {
			insertMode = true
		} else if string(rawLine[i]) == "]" {
			insertMode = false
		}

		if insertMode == false {
			if string(rawLine[i]) != "]" {
				buf.WriteString(string(rawLine[i]))
			}
		}
	}

	return buf.String()
}

func main() {

	rawLines, err := readLines("./test.chordpro")
	var lineArray []Line
	if err != nil {
		panic(err)
	}

	for _, rawLine := range rawLines {
		rawLineType, rawLineContent, _ := parseLine(rawLine)
		line := Line{rawLineType, rawLineContent, map[int]string{}}
		lineArray = append(lineArray, line)
		fmt.Println(getChords(rawLine))
	}

	for _, line := range lineArray {
		fmt.Printf("%v: %v \n", line.Type, line.Content)
	}

}
<commit_msg>Add Json convertion support<commit_after>/* The MIT License (MIT)

Copyright (c) 2015 Alexandre Lion <contact@alexandrelion.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
)

type Root struct {
	Lines []Line `json:"lines"`
}

type Line struct {
	Type    map[string]string `json:"type"`
	Content string            `json:"content,omitempty"`
	Chords  map[string]string `json:"chords,omitempty"`
}

// Chord format reference
var types = map[string]string{
	"t":   "title",
	"st":  "subtitle",
	"soc": "start_of_chorus",
	"eoc": "end_of_chorus",
	"c":   "comment",
	"sot": "start_of_tab",
	"eot": "end_of_tab",
}

// readLines gets content of a file using its path.
// Using bufio Scanner to append each line into an array.
// It returns the array with lines and any Scanner error encountered.
func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

// parseLine defines the type of the string following the ChordPro convention.
// Using regexp to determine the type.
// It returns the type of the string, its content without directives and any error encountered.
func parseLine(rawLine string) (map[string]string, string, error) {

	if rawLine == "" {
		return map[string]string{"b": "blank"}, "", nil // Blank line
	}

	for key, value := range types {
		matchKey, err := regexp.MatchString(fmt.Sprint("^{(", key, ")"), rawLine)
		matchValue, err := regexp.MatchString(fmt.Sprint("^{(", value, ")"), rawLine)

		if matchKey {
			return map[string]string{key: value}, rawLine[1+len(key)+1 : len(rawLine)-1], err
		} else if matchValue {
			return map[string]string{key: value}, rawLine[1+len(value)+1 : len(rawLine)-1], err
		}
	}
	return map[string]string{"v": "verse"}, rawLine, nil // Simple line: verse
}

// getChords gets position of every chords in the line.
// Using regexp to determine the positions.
// It returns the map of chords and their position.
func getChords(rawLine string) (map[string]string, error) {
	mappedChords := make(map[string]string)
	r, err := regexp.Compile("\\[[A-Za-z0-9#\\/]+\\]")
	matchChordIndex := r.FindAllStringIndex(rawLine, -1)
	matchChord := r.FindAllString(rawLine, -1)

	for i, position := range matchChordIndex {
		mappedChords[strconv.Itoa(position[0])] = string(matchChord[i])
	}

	return mappedChords, err
}

// truncChords remove chord from line.
// It browses the line char by char to exclude chords.
// It returns the cleaned line.
func truncChords(rawLine string) string {
	var buf bytes.Buffer
	var insertMode bool = false

	for i := 0; i < len(rawLine); i++ {
		if string(rawLine[i]) == "[" {
			insertMode = true
		} else if string(rawLine[i]) == "]" {
			insertMode = false
		}

		if insertMode == false {
			if string(rawLine[i]) != "]" {
				buf.WriteString(string(rawLine[i]))
			}
		}
	}

	return buf.String()
}

func main() {

	rawLines, err := readLines("./test.chordpro")

	var lineArray []Line
	if err != nil {
		panic(err)
	}

	// Line struct constructor
	for _, rawLine := range rawLines {
		rawLineType, rawLineContent, _ := parseLine(rawLine)
		lineChords, _ := getChords(rawLine)
		line := Line{rawLineType, truncChords(rawLineContent), lineChords}
		lineArray = append(lineArray, line)
	}

	root := &Root{lineArray}

	obj, _ := json.MarshalIndent(root, "", " ")
	//obj, _ := json.Marshal(root)
	fmt.Println(string(obj))

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/mesos/mesos-go/executor"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/newrelic/sidecar/service"
	"github.com/nitro/sidecar-executor/container"
	"github.com/relistan/envconfig"
	"github.com/relistan/go-director"
)

const (
	TaskRunning  = 0
	TaskFinished = iota
	TaskFailed   = iota
	TaskKilled   = iota
)

var (
	config Config
)

type Config struct {
	KillTaskTimeout     uint          `split_words:"true" default:"5"` // Seconds
	HttpTimeout         time.Duration `split_words:"true" default:"2s"`
	SidecarRetryCount   int           `split_words:"true" default:"5"`
	SidecarRetryDelay   time.Duration `split_words:"true" default:"3s"`
	SidecarUrl          string        `split_words:"true" default:"http://localhost:7777/state.json"`
	SidecarBackoff      time.Duration `split_words:"true" default:"1m"`
	SidecarPollInterval time.Duration `split_words:"true" default:"30s"`
	SidecarMaxFails     int           `split_words:"true" default:"3"`
}

type sidecarExecutor struct {
	driver      *executor.MesosExecutorDriver
	client      *docker.Client
	httpClient  *http.Client
	watchLooper director.Looper
	failCount   int
}

type SidecarServices struct {
	Servers map[string]struct {
		Services map[string]service.Service
	}
}

func newSidecarExecutor(client *docker.Client) *sidecarExecutor {
	return &sidecarExecutor{
		client:     client,
		httpClient: &http.Client{Timeout: config.HttpTimeout},
	}
}

func logConfig() {
	log.Infof("Executor Config -----------------------")
	log.Infof(" * KillTaskTimeout:     %d", config.KillTaskTimeout)
	log.Infof(" * HttpTimeout:         %s", config.HttpTimeout.String())
	log.Infof(" * SidecarRetryCount:   %d", config.SidecarRetryCount)
	log.Infof(" * SidecarRetryDelay:   %s", config.SidecarRetryDelay.String())
	log.Infof(" * SidecarUrl:          %s", config.SidecarUrl)
	log.Infof(" * SidecarBackoff:      %s", config.SidecarBackoff.String())
	log.Infof(" * SidecarPollInterval: %s", config.SidecarPollInterval.String())
	log.Infof(" * SidecarMaxFails:     %d", config.SidecarMaxFails)

	log.Infof("Environment ---------------------------")
	for _, setting := range os.Environ() {
		if (len(setting) >= 5 && setting[:5] == "MESOS") ||
				((len(setting) >= 8) && setting[:8] == "EXECUTOR") {
			pair := strings.Split(setting, "=")
			log.Infof(" * %-25s: %s", pair[0], pair[1])
		}
	}
	log.Infof("---------------------------------------")
}

func logTaskEnv(taskInfo *mesos.TaskInfo) {
	env := container.EnvForTask(taskInfo)
	if len(env) < 1 {
		log.Info("No Docker environment provided")
		return
	}

	log.Infof("Docker Environment --------------------")
	for _, setting := range env {
		log.Infof(" * %s", setting)
	}
	log.Infof("---------------------------------------")
}

// Send task status updates to Mesos via the executor driver
func (exec *sidecarExecutor) sendStatus(status int64, taskId *mesos.TaskID) {
	var mesosStatus *mesos.TaskState
	switch status {
	case TaskRunning:
		mesosStatus = mesos.TaskState_TASK_RUNNING.Enum()
	case TaskFinished:
		mesosStatus = mesos.TaskState_TASK_FINISHED.Enum()
	case TaskFailed:
		mesosStatus = mesos.TaskState_TASK_FAILED.Enum()
	case TaskKilled:
		mesosStatus = mesos.TaskState_TASK_KILLED.Enum()
	}

	update := &mesos.TaskStatus{
		TaskId: taskId,
		State:  mesosStatus,
	}

	if _, err := exec.driver.SendStatusUpdate(update); err != nil {
		log.Errorf("Error sending status update %s", err.Error())
		panic(err.Error())
	}
}

// Tell Mesos and thus the framework that the task finished. Shutdown driver.
func (exec *sidecarExecutor) finishTask(taskInfo *mesos.TaskInfo) {
	exec.sendStatus(TaskFinished, taskInfo.GetTaskId())
	time.Sleep(1 * time.Second)
	exec.driver.Stop()
}

// Tell Mesos and thus the framework that the task failed. Shutdown driver.
func (exec *sidecarExecutor) failTask(taskInfo *mesos.TaskInfo) {
	exec.sendStatus(TaskFailed, taskInfo.GetTaskId())

	// Unfortunately the status updates are sent async and we can't
	// get a handle on the channel used to send them. So we wait
	time.Sleep(1 * time.Second)

	// We're done with this executor, so let's stop now.
	exec.driver.Stop()
}

// Loop on a timed basis and check the health of the process in Sidecar.
// Note that because of the way the retries work, the loop timing is a
// lower bound on the delay.
func (exec *sidecarExecutor) watchContainer(container *docker.Container) {
	time.Sleep(config.SidecarBackoff)

	exec.watchLooper.Loop(func() error {
		containers, err := exec.client.ListContainers(
			docker.ListContainersOptions{
				All: true,
			},
		)
		if err != nil {
			return err
		}

		// Loop through all the containers, looking for a running
		// container with our Id.
		ok := false
		for _, entry := range containers {
			if entry.ID == container.ID {
				ok = true
			}
		}
		if !ok {
			return errors.New("Container " + container.ID + " not running!")
		}

		// Validate health status with Sidecar
		if err = exec.sidecarStatus(container); err != nil {
			return err
		}

		return nil
	})
}

// Validate the status of this task with Sidecar
func (exec *sidecarExecutor) sidecarStatus(container *docker.Container) error {
	fetch := func() ([]byte, error) {
		resp, err := exec.httpClient.Get(config.SidecarUrl)
		defer resp.Body.Close()
		if err != nil {
			return nil, err
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}

		return body, nil
	}

	// Try to connect to Sidecar, with some retries
	var err error
	var data []byte
	for i := 0; i < config.SidecarRetryCount; i++ {
		data, err = fetch()
		if err != nil {
			time.Sleep(config.SidecarRetryDelay)
			continue
		}
	}

	// We really really don't want to shut off all the jobs if Sidecar
	// is down. That would make it impossible to deploy Sidecar, and
	// would make the entire system dependent on it for services to
	// even start.
	if err != nil {
		log.Error("Can't contact Sidecar! Assuming healthy...")
		return nil
	}

	// We got a successful result from Sidecar, so let's parse it!
	var services SidecarServices
	err = json.Unmarshal(data, &services)
	if err != nil {
		log.Error("Can't parse Sidecar results! Assuming healthy...")
		return nil
	}

	// Don't know WTF is going on to get here, probably a race condition
	// with container startup and Sidecar status
	hostname := os.Getenv("TASK_HOST") // Mesos supplies this
	if _, ok := services.Servers[hostname]; !ok {
		log.Errorf("Can't find this server ('%s') in the Sidecar state! Assuming healthy...", hostname)
		return nil
	}

	svc, ok := services.Servers[hostname].Services[container.ID[:12]]
	if !ok {
		log.Errorf("Can't find this service in Sidecar yet! Assuming healthy...")
		return nil
	}

	// This is the one and only place where we're going to raise our hand
	// and say something is wrong with this service and it needs to be
	// shot by Mesos.
	if svc.Status == service.UNHEALTHY || svc.Status == service.TOMBSTONE {
		// Only bail out if we've exceed the setting for number of failures
		if exec.failCount < config.SidecarMaxFails {
			exec.failCount += 1
			log.Warnf("Failed Sidecar health check, but below fail limit")
			return nil
		}

		log.Errorf("Health failure count exceeded %d", config.SidecarMaxFails)

		exec.failCount = 0
		return errors.New("Unhealthy container: " + container.ID + " failing task!")
	}

	return nil
}

func init() {
	flag.Parse()
	err := envconfig.Process("executor", &config)
	if err != nil {
		log.Fatal(err.Error())
	}

	log.SetOutput(os.Stdout)
	log.SetLevel(log.DebugLevel)
}

func main() {
	log.Info("Starting Sidecar Executor")
	logConfig()

	// Get a Docker client. Without one, we can't do anything.
	dockerClient, err := docker.NewClientFromEnv()
	if err != nil {
		log.Error(err.Error())
		os.Exit(1)
	}

	scExec := newSidecarExecutor(dockerClient)

	dconfig := executor.DriverConfig{
		Executor: scExec,
	}

	driver, err := executor.NewMesosExecutorDriver(dconfig)
	if err != nil || driver == nil {
		log.Info("Unable to create an ExecutorDriver ", err.Error())
	}

	// Give the executor a reference to the driver
	scExec.driver = driver

	_, err = driver.Start()
	if err != nil {
		log.Info("Got error:", err)
		return
	}

	log.Info("Executor process has started")

	_, err = driver.Join()
	if err != nil {
		log.Info("driver failed:", err)
	}

	log.Info("Sidecar Executor exiting")
}
<commit_msg>A bit of refactoring/clean up<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/mesos/mesos-go/executor"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/newrelic/sidecar/service"
	"github.com/nitro/sidecar-executor/container"
	"github.com/relistan/envconfig"
	"github.com/relistan/go-director"
)

const (
	TaskRunning  = 0
	TaskFinished = iota
	TaskFailed   = iota
	TaskKilled   = iota
)

const (
	StatusSleepTime = 1*time.Second // How long we wait for final status updates to hit Mesos worker
)

var (
	config Config
)

type Config struct {
	KillTaskTimeout     uint          `split_words:"true" default:"5"` // Seconds
	HttpTimeout         time.Duration `split_words:"true" default:"2s"`
	SidecarRetryCount   int           `split_words:"true" default:"5"`
	SidecarRetryDelay   time.Duration `split_words:"true" default:"3s"`
	SidecarUrl          string        `split_words:"true" default:"http://localhost:7777/state.json"`
	SidecarBackoff      time.Duration `split_words:"true" default:"1m"`
	SidecarPollInterval time.Duration `split_words:"true" default:"30s"`
	SidecarMaxFails     int           `split_words:"true" default:"3"`
}

type sidecarExecutor struct {
	driver      *executor.MesosExecutorDriver
	client      *docker.Client
	httpClient  *http.Client
	watchLooper director.Looper
	failCount   int
}

type SidecarServices struct {
	Servers map[string]struct {
		Services map[string]service.Service
	}
}

func newSidecarExecutor(client *docker.Client) *sidecarExecutor {
	return &sidecarExecutor{
		client:     client,
		httpClient: &http.Client{Timeout: config.HttpTimeout},
	}
}

func logConfig() {
	log.Infof("Executor Config -----------------------")
	log.Infof(" * KillTaskTimeout:     %d", config.KillTaskTimeout)
	log.Infof(" * HttpTimeout:         %s", config.HttpTimeout.String())
	log.Infof(" * SidecarRetryCount:   %d", config.SidecarRetryCount)
	log.Infof(" * SidecarRetryDelay:   %s", config.SidecarRetryDelay.String())
	log.Infof(" * SidecarUrl:          %s", config.SidecarUrl)
	log.Infof(" * SidecarBackoff:      %s", config.SidecarBackoff.String())
	log.Infof(" * SidecarPollInterval: %s", config.SidecarPollInterval.String())
	log.Infof(" * SidecarMaxFails:     %d", config.SidecarMaxFails)

	log.Infof("Environment ---------------------------")
	for _, setting := range os.Environ() {
		if (len(setting) >= 5 && setting[:5] == "MESOS") ||
				((len(setting) >= 8) && setting[:8] == "EXECUTOR") {
			pair := strings.Split(setting, "=")
			log.Infof(" * %-25s: %s", pair[0], pair[1])
		}
	}
	log.Infof("---------------------------------------")
}

func logTaskEnv(taskInfo *mesos.TaskInfo) {
	env := container.EnvForTask(taskInfo)
	if len(env) < 1 {
		log.Info("No Docker environment provided")
		return
	}

	log.Infof("Docker Environment --------------------")
	for _, setting := range env {
		log.Infof(" * %s", setting)
	}
	log.Infof("---------------------------------------")
}

// Send task status updates to Mesos via the executor driver
func (exec *sidecarExecutor) sendStatus(status int64, taskId *mesos.TaskID) {
	var mesosStatus *mesos.TaskState
	switch status {
	case TaskRunning:
		mesosStatus = mesos.TaskState_TASK_RUNNING.Enum()
	case TaskFinished:
		mesosStatus = mesos.TaskState_TASK_FINISHED.Enum()
	case TaskFailed:
		mesosStatus = mesos.TaskState_TASK_FAILED.Enum()
	case TaskKilled:
		mesosStatus = mesos.TaskState_TASK_KILLED.Enum()
	}

	update := &mesos.TaskStatus{
		TaskId: taskId,
		State:  mesosStatus,
	}

	if _, err := exec.driver.SendStatusUpdate(update); err != nil {
		log.Errorf("Error sending status update %s", err.Error())
		panic(err.Error())
	}
}

// Tell Mesos and thus the framework that the task finished. Shutdown driver.
func (exec *sidecarExecutor) finishTask(taskInfo *mesos.TaskInfo) {
	exec.sendStatus(TaskFinished, taskInfo.GetTaskId())
	time.Sleep(StatusSleepTime)
	exec.driver.Stop()
}

// Tell Mesos and thus the framework that the task failed. Shutdown driver.
func (exec *sidecarExecutor) failTask(taskInfo *mesos.TaskInfo) {
	exec.sendStatus(TaskFailed, taskInfo.GetTaskId())

	// Unfortunately the status updates are sent async and we can't
	// get a handle on the channel used to send them. So we wait
	time.Sleep(StatusSleepTime)

	// We're done with this executor, so let's stop now.
	exec.driver.Stop()
}

// Loop on a timed basis and check the health of the process in Sidecar.
// Note that because of the way the retries work, the loop timing is a
// lower bound on the delay.
func (exec *sidecarExecutor) watchContainer(container *docker.Container) {
	time.Sleep(config.SidecarBackoff)

	exec.watchLooper.Loop(func() error {
		containers, err := exec.client.ListContainers(
			docker.ListContainersOptions{
				All: true,
			},
		)
		if err != nil {
			return err
		}

		// Loop through all the containers, looking for a running
		// container with our Id.
		ok := false
		for _, entry := range containers {
			if entry.ID == container.ID {
				ok = true
			}
		}
		if !ok {
			return errors.New("Container " + container.ID + " not running!")
		}

		// Validate health status with Sidecar
		if err = exec.sidecarStatus(container); err != nil {
			return err
		}

		return nil
	})
}

// Lookup a container in a service list
func sidecarLookup(containerId string, services SidecarServices) (*service.Service, bool) {
	hostname := os.Getenv("TASK_HOST") // Mesos supplies this
	if _, ok := services.Servers[hostname]; !ok {
		// Don't even have this host!
		return nil, ok
	}

	svc, ok := services.Servers[hostname].Services[containerId[:12]]

	return &svc, ok
}

// We only want to kill things that are definitely unhealthy
func shouldBeKilled(svc *service.Service) bool {
	return svc.Status == service.UNHEALTHY || svc.Status == service.TOMBSTONE
}

func (exec *sidecarExecutor) exceededFailCount() bool {
	return exec.failCount < config.SidecarMaxFails
}

// Validate the status of this task with Sidecar
func (exec *sidecarExecutor) sidecarStatus(container *docker.Container) error {
	fetch := func() ([]byte, error) {
		resp, err := exec.httpClient.Get(config.SidecarUrl)
		defer resp.Body.Close()
		if err != nil {
			return nil, err
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}

		return body, nil
	}

	// Try to connect to Sidecar, with some retries
	var err error
	var data []byte
	for i := 0; i < config.SidecarRetryCount; i++ {
		data, err = fetch()
		if err != nil {
			time.Sleep(config.SidecarRetryDelay)
			continue
		}
	}

	// We really really don't want to shut off all the jobs if Sidecar
	// is down. That would make it impossible to deploy Sidecar, and
	// would make the entire system dependent on it for services to
	// even start.
	if err != nil {
		log.Error("Can't contact Sidecar! Assuming healthy...")
		return nil
	}

	// We got a successful result from Sidecar, so let's parse it!
	var services SidecarServices
	err = json.Unmarshal(data, &services)
	if err != nil {
		log.Error("Can't parse Sidecar results! Assuming healthy...")
		return nil
	}

	svc, ok := sidecarLookup(container.ID, services)
	if !ok {
		log.Errorf("Can't find this service in Sidecar yet! Assuming healthy...")
		return nil
	}

	// This is the one and only place where we're going to raise our hand
	// and say something is wrong with this service and it needs to be
	// shot by Mesos.
	if shouldBeKilled(svc) {
		// Only bail out if we've exceed the setting for number of failures
		if !exec.exceededFailCount() {
			exec.failCount += 1
			log.Warnf("Failed Sidecar health check, but below fail limit")
			return nil
		}

		log.Errorf("Health failure count exceeded %d", config.SidecarMaxFails)

		exec.failCount = 0
		return errors.New("Unhealthy container: " + container.ID + " failing task!")
	}

	return nil
}

func init() {
	flag.Parse()
	err := envconfig.Process("executor", &config)
	if err != nil {
		log.Fatal(err.Error())
	}

	log.SetOutput(os.Stdout)
	log.SetLevel(log.DebugLevel)
}

func main() {
	log.Info("Starting Sidecar Executor")
	logConfig()

	// Get a Docker client. Without one, we can't do anything.
	dockerClient, err := docker.NewClientFromEnv()
	if err != nil {
		log.Error(err.Error())
		os.Exit(1)
	}

	scExec := newSidecarExecutor(dockerClient)

	dconfig := executor.DriverConfig{
		Executor: scExec,
	}

	driver, err := executor.NewMesosExecutorDriver(dconfig)
	if err != nil || driver == nil {
		log.Info("Unable to create an ExecutorDriver ", err.Error())
	}

	// Give the executor a reference to the driver
	scExec.driver = driver

	_, err = driver.Start()
	if err != nil {
		log.Info("Got error:", err)
		return
	}

	log.Info("Executor process has started")

	_, err = driver.Join()
	if err != nil {
		log.Info("driver failed:", err)
	}

	log.Info("Sidecar Executor exiting")
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Nan Deng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"

	"github.com/uniqush/uniqush-push/srv"
)

var uniqushPushConfFlags = flag.String("config", "/etc/uniqush/uniqush-push.conf", "Config file path")
var uniqushPushShowVersionFlag = flag.Bool("version", false, "Version info")

var uniqushPushVersion = "uniqush-push 2.3.0"

func installPushServices() {
	srv.InstallGCM()
	srv.InstallFCM()
	srv.InstallAPNS()
	srv.InstallADM()
}

func main() {
	flag.Parse()
	if *uniqushPushShowVersionFlag {
		fmt.Printf("%v\n", uniqushPushVersion)
		return
	}
	runtime.GOMAXPROCS(runtime.NumCPU() + 1)
	installPushServices()

	err := Run(*uniqushPushConfFlags, uniqushPushVersion)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot start: %v\n", err)
	}
}
<commit_msg>Update uniqush's version to 2.4.0<commit_after>/*
 * Copyright 2011 Nan Deng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"

	"github.com/uniqush/uniqush-push/srv"
)

var uniqushPushConfFlags = flag.String("config", "/etc/uniqush/uniqush-push.conf", "Config file path")
var uniqushPushShowVersionFlag = flag.Bool("version", false, "Version info")

var uniqushPushVersion = "uniqush-push 2.4.0"

func installPushServices() {
	srv.InstallGCM()
	srv.InstallFCM()
	srv.InstallAPNS()
	srv.InstallADM()
}

func main() {
	flag.Parse()
	if *uniqushPushShowVersionFlag {
		fmt.Printf("%v\n", uniqushPushVersion)
		return
	}
	runtime.GOMAXPROCS(runtime.NumCPU() + 1)
	installPushServices()

	err := Run(*uniqushPushConfFlags, uniqushPushVersion)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot start: %v\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"errors"
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"path"

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

const databaseFile = "purchases.db"

var listen = flag.String("listen", "localhost:8080", "host and port to listen on")

func backup() error {
	src, err := os.Open(databaseFile)
	defer src.Close()
	if err != nil {
		return errors.New("could not open database to backup")
	}

	err = os.MkdirAll("backup", 0755)
	if err != nil {
		return errors.New("could not create backup")
	}

	destFile := path.Join("backup", databaseFile+".gz")
	dest, err := os.Create(destFile)
	defer dest.Close()
	if err != nil {
		return err
	}

	gzipWriter := gzip.NewWriter(dest)
	_, err = io.Copy(gzipWriter, src)
	if err != nil {
		return err
	}

	return gzipWriter.Close()
}

func main() {
	flag.Parse()

	err := backup()
	if err != nil {
		log.Panicln(err)
	}
	log.Println("backup complete")

	app := newApp()
	defer app.destroy()
	log.Println("database opened")

	r := mux.NewRouter()
	purchasesSubRouter := r.PathPrefix("/purchases").Subrouter()
	purchasesSubRouter.HandleFunc("/", app.handlePurchases).Methods("GET")
	purchasesSubRouter.HandleFunc("/{id:[0-9]+}", app.handlePurchase).Methods("GET")
	purchasesSubRouter.HandleFunc("/", app.handleAddPurchase).Methods("POST")

	http.Handle("/", httpgzip.NewHandler(r))

	log.Printf("Serving on %s\n", *listen)
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<commit_msg>log requests<commit_after>package main

import (
	"compress/gzip"
	"errors"
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"time"

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

const databaseFile = "purchases.db"

var listen = flag.String("listen", "localhost:8080", "host and port to listen on")

func backup() error {
	src, err := os.Open(databaseFile)
	defer src.Close()
	if err != nil {
		return errors.New("could not open database to backup")
	}

	err = os.MkdirAll("backup", 0755)
	if err != nil {
		return errors.New("could not create backup")
	}

	destFile := path.Join("backup", databaseFile+".gz")
	dest, err := os.Create(destFile)
	defer dest.Close()
	if err != nil {
		return err
	}

	gzipWriter := gzip.NewWriter(dest)
	_, err = io.Copy(gzipWriter, src)
	if err != nil {
		return err
	}

	return gzipWriter.Close()
}

func logger(inner http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		inner.ServeHTTP(w, r)

		log.Printf(
			"%s\t%s\t%s",
			r.Method,
			r.RequestURI,
			time.Since(start),
		)
	})
}

func main() {
	flag.Parse()

	err := backup()
	if err != nil {
		log.Panicln(err)
	}
	log.Println("backup complete")

	app := newApp()
	defer app.destroy()
	log.Println("database opened")

	r := mux.NewRouter()
	purchasesSubRouter := r.PathPrefix("/purchases").Subrouter()
	purchasesSubRouter.HandleFunc("/", app.handlePurchases).Methods("GET")
	purchasesSubRouter.HandleFunc("/{id:[0-9]+}", app.handlePurchase).Methods("GET")
	purchasesSubRouter.HandleFunc("/", app.handleAddPurchase).Methods("POST")

	http.Handle("/", logger(httpgzip.NewHandler(r)))

	log.Printf("Serving on %s\n", *listen)
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/FederationOfFathers/dashboard/api"
	"github.com/FederationOfFathers/dashboard/events"
	"github.com/FederationOfFathers/dashboard/slack"
	"github.com/apokalyptik/cfg"
	"github.com/uber-go/zap"
)

var slackApiKey = "xox...."
var logger = zap.NewJSON()

func init() {
	scfg := cfg.New("cfg-slack")
	scfg.StringVar(&slackApiKey, "apiKey", slackApiKey, "Slack API Key (env: SLACK_APIKEY)")
	scfg.StringVar(&bot.CdnPrefix, "cdnPrefix", bot.CdnPrefix, "http url base from which to store saved uploads")
	scfg.StringVar(&bot.CdnPath, "cdnPath", bot.CdnPath, "Filesystem path to store uploads in")

	acfg := cfg.New("cfg-api")
	acfg.StringVar(&api.ListenOn, "listen", api.ListenOn, "API bind address (env: API_LISTEN)")

	ecfg := cfg.New("cfg-events")
	ecfg.StringVar(&events.SaveFile, "savefile", events.SaveFile, "path to the file in which events should be persisted")
	ecfg.DurationVar(&events.SaveInterval, "saveinterval", events.SaveInterval, "how often to check and see if we need to save data")

}

func main() {
	cfg.Parse()
	data, err := bot.SlackConnect(slackApiKey)
	events.Start(data)
	if err != nil {
		logger.Fatal("Unable to contact the slack API", zap.Error(err))
	}
	api.Run(data, events.Data)
}
<commit_msg>configuration updates<commit_after>package main

import (
	"github.com/FederationOfFathers/dashboard/api"
	"github.com/FederationOfFathers/dashboard/events"
	"github.com/FederationOfFathers/dashboard/slack"
	"github.com/apokalyptik/cfg"
	"github.com/uber-go/zap"
)

var slackAPIKey = "xox...."
var logger = zap.NewJSON()

func init() {
	scfg := cfg.New("cfg-slack")
	scfg.StringVar(&slackAPIKey, "apiKey", slackAPIKey, "Slack API Key (env: SLACK_APIKEY)")
	scfg.StringVar(&bot.CdnPrefix, "cdnPrefix", bot.CdnPrefix, "http url base from which to store saved uploads")
	scfg.StringVar(&bot.CdnPath, "cdnPath", bot.CdnPath, "Filesystem path to store uploads in")

	acfg := cfg.New("cfg-api")
	acfg.StringVar(&api.ListenOn, "listen", api.ListenOn, "API bind address (env: API_LISTEN)")
	acfg.StringVar(&api.AuthSecret, "secret", api.AuthSecret, "Authentication secret for use in generating login tokens")
	acfg.StringVar(&api.JWTSecret, "hmac", api.JWTSecret, "Authentication secret used for JWT tokens")

	ecfg := cfg.New("cfg-events")
	ecfg.StringVar(&events.SaveFile, "savefile", events.SaveFile, "path to the file in which events should be persisted")
	ecfg.DurationVar(&events.SaveInterval, "saveinterval", events.SaveInterval, "how often to check and see if we need to save data")

}

func main() {
	cfg.Parse()
	data, err := bot.SlackConnect(slackAPIKey)
	events.Start(data)
	if err != nil {
		logger.Fatal("Unable to contact the slack API", zap.Error(err))
	}
	api.Run(data, events.Data)
}
<|endoftext|>


<commit_before>package main

/**
 * Imports
 */
import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	// "runtime"
	"strings"
	"time"
)

/**
 * Dependency type
 */
type Dependency struct {
	Name, Version string
}

/**
 * Options type
 */
type Options struct {
	GopasFile  string
	Watches    []string
	Ignores    []string
	Extensions []string
}

/**
 * Runner type
 */
type Runner struct {
	name    string
	args    []string
	command *exec.Cmd
}

/**
 * ScanCallback type
 */
type ScanCallback func(path string)

/**
 * Global variables
 */
var (
	options      Options
	modifiedTime time.Time = time.Now()
	cwd          string    = getCwd()
)

/**
 * Run command
 *
 * @return {*exec.Cmd}
 * @return {error}
 */
func (r *Runner) Run() (*exec.Cmd, error) {
	if r.command == nil || r.IsExited() {
		err := r.runBin()
		// time.Sleep(250 * time.Millisecond)
		return r.command, err
	} else {
		return r.command, nil
	}

}

/**
 * Check wether runner command already exit
 *
 * @return {bool}
 */
func (r *Runner) IsExited() bool {
	return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
}

/**
 * Run bin
 *
 * @return {error}
 */
func (r *Runner) runBin() error {
	// cwd := cwd()
	fmt.Println(">> Running", r.name, r.args)

	r.command = exec.Command(r.name, r.args...)
	// r.command = exec.Command("go", "env")
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	r.command.Env = env
	r.command.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)

	stdout, err := r.command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := r.command.StderrPipe()
	if err != nil {
		return err
	}

	err = r.command.Start()
	if err != nil {
		return err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	go r.command.Wait()

	return nil
}

/**
 * Kill runner command
 *
 * @return {error}
 */
func (r *Runner) Kill() error {
	if r.command != nil && r.command.Process != nil {
		done := make(chan error)
		go func() {
			r.command.Wait()
			close(done)
		}()

		//Trying a "soft" kill first
		// if runtime.GOOS == "windows" {
		if err := r.command.Process.Kill(); err != nil {
			return err
		}
		// } else if err := r.command.Process.Signal(os.Interrupt); err != nil {
		// 	return err
		// }

		//Wait for our process to die before we return or hard kill after 3 sec
		select {
		case <-time.After(3 * time.Second):
			if err := r.command.Process.Kill(); err != nil {
				fmt.Fprintln(os.Stderr, ">> Kill Error: ", err)
				os.Exit(1)
			}
		case <-done:
		}
		r.command = nil
	}

	return nil
}

/**
 * Read dependencies listed on filename
 *
 * @param {string} filename
 * @return {[]Dependency}
 */
func readDependencies(filename string) []Dependency {
	dependencies := []Dependency{}

	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return dependencies
	}

	fileLines := strings.Split(string(fileBytes), "\n")
	for _, line := range fileLines {
		if line != "" {
			token := strings.Split(line, "=")

			dependencies = append(dependencies, Dependency{
				Name:    strings.Trim(token[0], " \t"),
				Version: strings.Trim(token[1], " \t"),
			})
		}
	}

	return dependencies
}

/**
 * Get current working directory
 *
 * @return {string}
 */
func getCwd() string {
	cwd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, ">> Getwd Error: %s\n", err.Error())
		os.Exit(1)
	}
	return cwd
}

/**
 * Check whether path is ignorable
 *
 * @param {string} path
 */
func isIgnorable(path string) bool {
	ignorable := false
	for _, ignore := range options.Ignores {
		if matched, _ := filepath.Match(ignore, path); matched {
			ignorable = true
			break
		}
	}
	return ignorable
}

/**
 * Check whether path having acceptable extension
 *
 * @param {string} path
 */
func isAcceptable(path string) bool {
	for _, ext := range options.Extensions {
		if strings.HasSuffix(path, "."+ext) {
			return true
		}
	}

	return false
}

/**
 * Scan changes and invoke callback on file changes
 *
 * @param  {ScanCallback}   cb ScanCallback
 */
func scanChanges(cb ScanCallback) {
	for {
		for _, dir := range options.Watches {
			filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
				if isIgnorable(path) {
					return filepath.SkipDir
				}

				if isAcceptable(path) && info.ModTime().After(modifiedTime) {
					modifiedTime = time.Now()
					cb(path)
				}
				return nil
			})
		}

		time.Sleep(500 * time.Millisecond)
	}
}

/**
 * List action
 */
func actionList() {
	depCount := 0
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		fmt.Printf("%s %s\n", dep.Name, dep.Version)
		depCount++
	}
	fmt.Printf("dependencies(%d)\n", depCount)
}

/**
 * Install action
 */
func actionInstall() {
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		cmd := exec.Command("go", "get", dep.Name)
		env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
		for _, v := range os.Environ() {
			if !strings.HasPrefix(v, "GOPATH=") {
				env = append(env, v)
			}
		}
		cmd.Env = env
		cmd.Start()
		if err := cmd.Wait(); err != nil {
			fmt.Fprintf(os.Stderr, ">> Install Error: %s\n", err.Error())
			os.Exit(1)
		}
		fmt.Printf(">> Installing \"go get %s\"\n", dep.Name)
	}
}

/**
 * Help action
 */
func actionHelp() {
	fmt.Println("Gopas is a tool to build Go outside GOPATH")
	fmt.Println("")
	fmt.Println("Usage:")
	fmt.Println("")
	fmt.Println("  gopas <action> [<args...>]")
	fmt.Println("")
	fmt.Println("The actions are:")
	fmt.Println("")
	fmt.Println("  build    compile packages and dependencies")
	fmt.Println("  help     show help")
	fmt.Println("  install  compile and install packages and dependencies")
	fmt.Println("  list     list dependencies")
	fmt.Println("  run      compile and run Go program")
	fmt.Println("  test     test packages")
	fmt.Println("")
}

/**
 * Run action
 */
func actionRun() {
	if len(os.Args) < 3 {
		fmt.Fprintln(os.Stderr, "Usage: gopas run <file>")
		os.Exit(1)
	}

	for _, file := range os.Args[2:] {
		if _, err := os.Stat(file); os.IsNotExist(err) {
			fmt.Fprintf(os.Stderr, "File \"%s\" is not exists\n", file)
			os.Exit(1)
		}
	}

	runner := &Runner{
		name: "go",
		args: append([]string{"run"}, os.Args[2:]...),
	}

	fmt.Println(">> Watches    ", strings.Join(options.Watches, ", "))
	fmt.Println(">> Extensions ", strings.Join(options.Extensions, ", "))
	fmt.Println(">> Ignores    ", strings.Join(options.Ignores, ", "))

	runner.Run()
	go scanChanges(func(path string) {
		runner.Kill()
		bootstrap()
		time.Sleep(250 * time.Millisecond)
		runner.Run()
	})

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if scanner.Text() == "rs" {
			time.Sleep(700 * time.Millisecond)
			runner.Kill()
			bootstrap()
			time.Sleep(250 * time.Millisecond)
			runner.Run()
		}
	}
}

/**
 * Test action
 */
func actionTest() {
	// cwd := cwd()
	cmd := exec.Command("go", "test")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Build action
 */
func actionBuild() {
	// cwd := cwd()
	cmd := exec.Command("go", "build")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Bootstrap project options and gopath dir
 */
func bootstrap() {
	fmt.Println(">> Bootstrapping...")
	if _, err := os.Stat(".gopath"); os.IsNotExist(err) {
		if os.MkdirAll(".gopath/src", 0755) != nil {
			fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
			os.Exit(1)
		}
	}

	if err := os.RemoveAll(cwd + "/.gopath/src/" + filepath.Base(cwd)); err != nil {
		fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
	}
	copy_folder(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))

	// os.Symlink(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))
	// // FIXME workaround, cannot read module on vendor dir
	// if files, err := ioutil.ReadDir("vendor"); err == nil {
	// 	for _, file := range files {
	// 		name := file.Name()
	// 		// os.RemoveAll(cwd + "/.gopath/src/" + name)
	// 		os.Symlink(cwd+"/vendor/"+name, cwd+"/.gopath/src/"+name)
	// 	}
	// }
}

/**
 * main function
 */
func main() {
	options = Options{
		GopasFile:  "gopasfile",
		Watches:    []string{"."},
		Ignores:    []string{".git", ".gopath"},
		Extensions: []string{"go"},
	}

	bootstrap()

	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "list":
			actionList()
			return
		case "install":
			actionInstall()
			return
		case "run":
			actionRun()
			return
		case "build":
			actionBuild()
			return
		case "test":
			actionTest()
			return
		}
	}
	actionHelp()
}

func copy_folder(source string, dest string) (err error) {

	sourceinfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, sourceinfo.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)

	objects, err := directory.Readdir(-1)

	for _, obj := range objects {

		sourcefilepointer := source + "/" + obj.Name()

		destinationfilepointer := dest + "/" + obj.Name()

		if obj.IsDir() {
			if obj.Name() != ".gopath" {
				err = copy_folder(sourcefilepointer, destinationfilepointer)
				if err != nil {
					fmt.Println(err)
				}
			}
		} else {
			err = copy_file(sourcefilepointer, destinationfilepointer)
			if err != nil {
				fmt.Println(err)
			}
		}

	}
	return
}

func copy_file(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}

	return
}
<commit_msg>FIXED go run not working on rs restart<commit_after>package main

/**
 * Imports
 */
import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	// "runtime"
	"strings"
	"time"
)

/**
 * Dependency type
 */
type Dependency struct {
	Name, Version string
}

/**
 * Options type
 */
type Options struct {
	GopasFile  string
	Watches    []string
	Ignores    []string
	Extensions []string
}

/**
 * Runner type
 */
type Runner struct {
	name    string
	args    []string
	command *exec.Cmd
}

/**
 * ScanCallback type
 */
type ScanCallback func(path string)

/**
 * Global variables
 */
var (
	options      Options
	modifiedTime time.Time = time.Now()
	cwd          string    = getCwd()
)

/**
 * Run command
 *
 * @return {*exec.Cmd}
 * @return {error}
 */
func (r *Runner) Run() (*exec.Cmd, error) {
	if r.command == nil || r.IsExited() {
		err := r.runBin()
		// time.Sleep(250 * time.Millisecond)
		return r.command, err
	} else {
		return r.command, nil
	}

}

/**
 * Check wether runner command already exit
 *
 * @return {bool}
 */
func (r *Runner) IsExited() bool {
	return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
}

/**
 * Run bin
 *
 * @return {error}
 */
func (r *Runner) runBin() error {
	// cwd := cwd()
	fmt.Printf(">> Running \"%s %s\"\n", r.name, strings.Join(r.args, " "))

	r.command = exec.Command(r.name, r.args...)
	// r.command = exec.Command("go", "env")
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	r.command.Env = env
	r.command.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)

	stdout, err := r.command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := r.command.StderrPipe()
	if err != nil {
		return err
	}

	err = r.command.Start()
	if err != nil {
		return err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	go r.command.Wait()

	return nil
}

/**
 * Kill runner command
 *
 * @return {error}
 */
func (r *Runner) Kill() error {
	if r.command != nil && r.command.Process != nil {
		done := make(chan error)
		go func() {
			r.command.Wait()
			close(done)
		}()

		//Trying a "soft" kill first
		// if runtime.GOOS == "windows" {
		if err := r.command.Process.Kill(); err != nil {
			return err
		}
		// } else if err := r.command.Process.Signal(os.Interrupt); err != nil {
		// 	return err
		// }

		//Wait for our process to die before we return or hard kill after 3 sec
		select {
		case <-time.After(3 * time.Second):
			if err := r.command.Process.Kill(); err != nil {
				fmt.Fprintln(os.Stderr, ">> Kill Error: ", err)
				os.Exit(1)
			}
		case <-done:
		}
		r.command = nil
	}

	return nil
}

/**
 * Read dependencies listed on filename
 *
 * @param {string} filename
 * @return {[]Dependency}
 */
func readDependencies(filename string) []Dependency {
	dependencies := []Dependency{}

	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return dependencies
	}

	fileLines := strings.Split(string(fileBytes), "\n")
	for _, line := range fileLines {
		if line != "" {
			token := strings.Split(line, "=")

			dependencies = append(dependencies, Dependency{
				Name:    strings.Trim(token[0], " \t"),
				Version: strings.Trim(token[1], " \t"),
			})
		}
	}

	return dependencies
}

/**
 * Get current working directory
 *
 * @return {string}
 */
func getCwd() string {
	cwd, err := os.Getwd()
	if err != nil {
		fmt.Fprintf(os.Stderr, ">> Getwd Error: %s\n", err.Error())
		os.Exit(1)
	}
	return cwd
}

/**
 * Check whether path is ignorable
 *
 * @param {string} path
 */
func isIgnorable(path string) bool {
	ignorable := false
	for _, ignore := range options.Ignores {
		if matched, _ := filepath.Match(ignore, path); matched {
			ignorable = true
			break
		}
	}
	return ignorable
}

/**
 * Check whether path having acceptable extension
 *
 * @param {string} path
 */
func isAcceptable(path string) bool {
	for _, ext := range options.Extensions {
		if strings.HasSuffix(path, "."+ext) {
			return true
		}
	}

	return false
}

/**
 * Scan changes and invoke callback on file changes
 *
 * @param  {ScanCallback}   cb ScanCallback
 */
func scanChanges(cb ScanCallback) {
	for {
		for _, dir := range options.Watches {
			filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
				if isIgnorable(path) {
					return filepath.SkipDir
				}

				if isAcceptable(path) && info.ModTime().After(modifiedTime) {
					modifiedTime = time.Now()
					cb(path)
				}
				return nil
			})
		}

		time.Sleep(1000 * time.Millisecond)
	}
}

/**
 * List action
 */
func actionList() {
	depCount := 0
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		fmt.Printf("%s %s\n", dep.Name, dep.Version)
		depCount++
	}
	fmt.Printf("dependencies(%d)\n", depCount)
}

/**
 * Install action
 */
func actionInstall() {
	dependencies := readDependencies(options.GopasFile)
	for _, dep := range dependencies {
		cmd := exec.Command("go", "get", dep.Name)
		env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
		for _, v := range os.Environ() {
			if !strings.HasPrefix(v, "GOPATH=") {
				env = append(env, v)
			}
		}
		cmd.Env = env
		cmd.Start()
		if err := cmd.Wait(); err != nil {
			fmt.Fprintf(os.Stderr, ">> Install Error: %s\n", err.Error())
			os.Exit(1)
		}
		fmt.Printf(">> Installing \"go get %s\"\n", dep.Name)
	}
}

/**
 * Help action
 */
func actionHelp() {
	fmt.Println("Gopas is a tool to build Go outside GOPATH")
	fmt.Println("")
	fmt.Println("Usage:")
	fmt.Println("")
	fmt.Println("  gopas <action> [<args...>]")
	fmt.Println("")
	fmt.Println("The actions are:")
	fmt.Println("")
	fmt.Println("  build    compile packages and dependencies")
	fmt.Println("  help     show help")
	fmt.Println("  install  compile and install packages and dependencies")
	fmt.Println("  list     list dependencies")
	fmt.Println("  run      compile and run Go program")
	fmt.Println("  test     test packages")
	fmt.Println("")
}

/**
 * Run action
 */
func actionRun() {
	if len(os.Args) < 3 {
		fmt.Fprintln(os.Stderr, "Usage: gopas run <file>")
		os.Exit(1)
	}

	for _, file := range os.Args[2:] {
		if _, err := os.Stat(file); os.IsNotExist(err) {
			fmt.Fprintf(os.Stderr, "File \"%s\" is not exists\n", file)
			os.Exit(1)
		}
	}

	runner := &Runner{
		name: "go",
		args: append([]string{"run"}, os.Args[2:]...),
	}

	fmt.Println(">> Watches    ", strings.Join(options.Watches, ", "))
	fmt.Println(">> Extensions ", strings.Join(options.Extensions, ", "))
	fmt.Println(">> Ignores    ", strings.Join(options.Ignores, ", "))

	runner.Run()
	go scanChanges(func(path string) {
		runner.Kill()
		bootstrap()
		time.Sleep(500 * time.Millisecond)
		runner.Run()
	})

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if scanner.Text() == "rs" {
			time.Sleep(500 * time.Millisecond)
			runner.Kill()
			bootstrap()
			time.Sleep(500 * time.Millisecond)
			runner.Run()
		}
	}
}

/**
 * Test action
 */
func actionTest() {
	// cwd := cwd()
	cmd := exec.Command("go", "test")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Build action
 */
func actionBuild() {
	// cwd := cwd()
	cmd := exec.Command("go", "build")
	cmd.Dir = cwd + "/.gopath/src/" + filepath.Base(cwd)
	env := []string{fmt.Sprintf("GOPATH=%s", cwd+"/.gopath")}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOPATH=") {
			env = append(env, v)
		}
	}
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Start()
	cmd.Wait()
}

/**
 * Bootstrap project options and gopath dir
 */
func bootstrap() {
	fmt.Println(">> Bootstrapping...")
	if _, err := os.Stat(".gopath"); os.IsNotExist(err) {
		if os.MkdirAll(".gopath/src", 0755) != nil {
			fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
			os.Exit(1)
		}
	}

	if err := os.RemoveAll(cwd + "/.gopath/src/" + filepath.Base(cwd)); err != nil {
		fmt.Fprintf(os.Stderr, ">> Bootstrap Error: %s\n", err.Error())
	}
	copy_folder(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))

	// os.Symlink(cwd, cwd+"/.gopath/src/"+filepath.Base(cwd))
	// // FIXME workaround, cannot read module on vendor dir
	// if files, err := ioutil.ReadDir("vendor"); err == nil {
	// 	for _, file := range files {
	// 		name := file.Name()
	// 		// os.RemoveAll(cwd + "/.gopath/src/" + name)
	// 		os.Symlink(cwd+"/vendor/"+name, cwd+"/.gopath/src/"+name)
	// 	}
	// }
}

/**
 * main function
 */
func main() {
	options = Options{
		GopasFile:  "gopasfile",
		Watches:    []string{"."},
		Ignores:    []string{".git", ".gopath"},
		Extensions: []string{"go"},
	}

	bootstrap()

	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "list":
			actionList()
			return
		case "install":
			actionInstall()
			return
		case "run":
			actionRun()
			return
		case "build":
			actionBuild()
			return
		case "test":
			actionTest()
			return
		}
	}
	actionHelp()
}

func copy_folder(source string, dest string) (err error) {

	sourceinfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	err = os.MkdirAll(dest, sourceinfo.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)

	objects, err := directory.Readdir(-1)

	for _, obj := range objects {

		sourcefilepointer := source + "/" + obj.Name()

		destinationfilepointer := dest + "/" + obj.Name()

		if obj.IsDir() {
			if obj.Name() != ".gopath" {
				err = copy_folder(sourcefilepointer, destinationfilepointer)
				if err != nil {
					fmt.Println(err)
				}
			}
		} else {
			err = copy_file(sourcefilepointer, destinationfilepointer)
			if err != nil {
				fmt.Println(err)
			}
		}

	}
	return
}

func copy_file(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

	destfile, err := os.Create(dest)
	if err != nil {
		return err
	}

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}

	return
}
<|endoftext|>