<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/signal"
	"strings"
	"unicode/utf8"
)

const (
	VERSION                   = "0.0.1"
	defaultPlaceholder        = "{{}}"
	keyBackspace              = 8
	keyDelete                 = 127
	keyEndOfTransmission      = 4
	keyLineFeed               = 10
	keyCarriageReturn         = 13
	keyEndOfTransmissionBlock = 23
	keyEscape                 = 27
)

var placeholder string

var usage = `fzz allows you to run a command interactively.

Usage:

	fzz command

The command MUST include the placeholder '{{}}'.

Arguments:

	-v		Print version and exit
`

func printUsage() {
	fmt.Printf(usage)
}

func isPipe(f *os.File) bool {
	s, err := f.Stat()
	if err != nil {
		return false
	}

	return s.Mode()&os.ModeNamedPipe != 0
}

func containsPlaceholder(s []string, ph string) bool {
	for _, v := range s {
		if strings.Contains(v, ph) {
			return true
		}
	}
	return false
}

func validPlaceholder(p string) bool {
	return len(p)%2 == 0
}

func removeLastWord(s []byte) []byte {
	fields := bytes.Fields(s)
	if len(fields) > 0 {
		r := bytes.Join(fields[:len(fields)-1], []byte{' '})
		if len(r) > 1 {
			r = append(r, ' ')
		}
		return r
	}
	return []byte{}
}

func mainLoop(tty *TTY, printer *Printer, stdinbuf *bytes.Buffer) {
	f, err := os.Create("trace.log")
	if err != nil {
		log.Fatal(err)
	}

	var stdoutbuf bytes.Buffer
	var currentRunner *Runner

	input := make([]byte, 0)
	ttych := make(chan []byte)

	go func() {
		rs := bufio.NewScanner(tty)
		rs.Split(bufio.ScanRunes)

		for rs.Scan() {
			b := rs.Bytes()
			ttych <- b
		}

		tty.resetScreen()
		log.Fatal(rs.Err())
	}()

	tty.resetScreen()
	tty.printPrompt(input[:len(input)])

	for {
		f.WriteString("select\n")
		select {
		case b := <-ttych:
			fmt.Fprintf(f, "ttych: %x\n", b)
			switch b[0] {
			case keyBackspace, keyDelete:
				if len(input) > 1 {
					r, rsize := utf8.DecodeLastRune(input)
					if r == utf8.RuneError {
						input = input[:len(input)-1]
					} else {
						input = input[:len(input)-rsize]
					}
				} else if len(input) == 1 {
					input = nil
				}
			case keyEndOfTransmission, keyLineFeed, keyCarriageReturn:
				currentRunner.Wait()
				tty.resetScreen()
				io.Copy(os.Stdout, &stdoutbuf)
				return
			case keyEscape:
				tty.resetScreen()
				return
			case keyEndOfTransmissionBlock:
				input = removeLastWord(input)
			default:
				// TODO: Default is wrong here. Only append printable characters to
				// input
				input = append(input, b...)
			}

			fmt.Fprintf(f, "after select. currentRunner: %p\n", currentRunner)

			if currentRunner != nil {
				go func(runner *Runner) {
					fmt.Fprintf(f, "calling KillWait(). runner: %p\n", runner)
					runner.KillWait()
					fmt.Fprintf(f, "killwait finished\n")
				}(currentRunner)
			}

			tty.resetScreen()
			tty.printPrompt(input)

			printer.Reset()

			if len(input) > 0 {
				currentRunner = &Runner{template: flag.Args(), placeholder: placeholder}
				fmt.Fprintf(f, "starting new runner. runner: %p\n", currentRunner)
				outch, errch, err := currentRunner.runWithInput(input)
				if err != nil {
					log.Fatal(err)
				}
				stdoutbuf.Reset()
				go func() {
					for line := range outch {
						printer.Print(line)
						stdoutbuf.WriteString(line)
					}
					fmt.Fprintf(f, "outch finished. cursorAfterPrompt now. runecount: %d\n", utf8.RuneCount(input))
					tty.cursorAfterPrompt(utf8.RuneCount(input))
				}()

				go func() {
					for line := range errch {
						stdoutbuf.WriteString(line)
					}
					fmt.Fprintf(f, "errch finished. cursorAfterPrompt now. runecount: %d\n", utf8.RuneCount(input))
					// tty.cursorAfterPrompt(utf8.RuneCount(input))
				}()
			}
		}
	}
}

func main() {
	flVersion := flag.Bool("v", false, "Print fzz version and quit")
	flag.Usage = printUsage
	flag.Parse()

	if *flVersion {
		fmt.Printf("fzz %s\n", VERSION)
		os.Exit(0)
	}

	if len(flag.Args()) < 2 {
		fmt.Fprintf(os.Stderr, usage)
		os.Exit(1)
	}

	if placeholder = os.Getenv("FZZ_PLACEHOLDER"); placeholder == "" {
		placeholder = defaultPlaceholder
	}

	if !validPlaceholder(placeholder) {
		fmt.Fprintln(os.Stderr, "Placeholder is not valid, needs even number of characters")
		os.Exit(1)
	}

	if !containsPlaceholder(flag.Args(), placeholder) {
		fmt.Fprintln(os.Stderr, "No placeholder in arguments")
		os.Exit(1)
	}

	tty, err := NewTTY()
	if err != nil {
		log.Fatal(err)
	}
	defer tty.resetState()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		tty.resetState()
		os.Exit(1)
	}()
	tty.setSttyState("cbreak", "-echo")

	stdinbuf := bytes.Buffer{}
	if isPipe(os.Stdin) {
		io.Copy(&stdinbuf, os.Stdin)
	}

	printer := NewPrinter(tty, tty.cols, tty.rows-1) // prompt is one row
	mainLoop(tty, printer, &stdinbuf)
}
<commit_msg>Remove useless line, print errors with Printer<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/signal"
	"strings"
	"unicode/utf8"
)

const (
	VERSION                   = "0.0.1"
	defaultPlaceholder        = "{{}}"
	keyBackspace              = 8
	keyDelete                 = 127
	keyEndOfTransmission      = 4
	keyLineFeed               = 10
	keyCarriageReturn         = 13
	keyEndOfTransmissionBlock = 23
	keyEscape                 = 27
)

var placeholder string

var usage = `fzz allows you to run a command interactively.

Usage:

	fzz command

The command MUST include the placeholder '{{}}'.

Arguments:

	-v		Print version and exit
`

func printUsage() {
	fmt.Printf(usage)
}

func isPipe(f *os.File) bool {
	s, err := f.Stat()
	if err != nil {
		return false
	}

	return s.Mode()&os.ModeNamedPipe != 0
}

func containsPlaceholder(s []string, ph string) bool {
	for _, v := range s {
		if strings.Contains(v, ph) {
			return true
		}
	}
	return false
}

func validPlaceholder(p string) bool {
	return len(p)%2 == 0
}

func removeLastWord(s []byte) []byte {
	fields := bytes.Fields(s)
	if len(fields) > 0 {
		r := bytes.Join(fields[:len(fields)-1], []byte{' '})
		if len(r) > 1 {
			r = append(r, ' ')
		}
		return r
	}
	return []byte{}
}

func mainLoop(tty *TTY, printer *Printer, stdinbuf *bytes.Buffer) {
	f, err := os.Create("trace.log")
	if err != nil {
		log.Fatal(err)
	}

	var stdoutbuf bytes.Buffer
	var currentRunner *Runner

	input := make([]byte, 0)
	ttych := make(chan []byte)

	go func() {
		rs := bufio.NewScanner(tty)
		rs.Split(bufio.ScanRunes)

		for rs.Scan() {
			b := rs.Bytes()
			ttych <- b
		}

		log.Fatal(rs.Err())
	}()

	tty.resetScreen()
	tty.printPrompt(input[:len(input)])

	for {
		f.WriteString("select\n")
		select {
		case b := <-ttych:
			fmt.Fprintf(f, "ttych: %x\n", b)
			switch b[0] {
			case keyBackspace, keyDelete:
				if len(input) > 1 {
					r, rsize := utf8.DecodeLastRune(input)
					if r == utf8.RuneError {
						input = input[:len(input)-1]
					} else {
						input = input[:len(input)-rsize]
					}
				} else if len(input) == 1 {
					input = nil
				}
			case keyEndOfTransmission, keyLineFeed, keyCarriageReturn:
				currentRunner.Wait()
				tty.resetScreen()
				io.Copy(os.Stdout, &stdoutbuf)
				return
			case keyEscape:
				tty.resetScreen()
				return
			case keyEndOfTransmissionBlock:
				input = removeLastWord(input)
			default:
				// TODO: Default is wrong here. Only append printable characters to
				// input
				input = append(input, b...)
			}

			fmt.Fprintf(f, "after select. currentRunner: %p\n", currentRunner)

			if currentRunner != nil {
				go func(runner *Runner) {
					fmt.Fprintf(f, "calling KillWait(). runner: %p\n", runner)
					runner.KillWait()
					fmt.Fprintf(f, "killwait finished\n")
				}(currentRunner)
			}

			tty.resetScreen()
			tty.printPrompt(input)

			printer.Reset()

			if len(input) > 0 {
				currentRunner = &Runner{template: flag.Args(), placeholder: placeholder}
				fmt.Fprintf(f, "starting new runner. runner: %p\n", currentRunner)
				outch, errch, err := currentRunner.runWithInput(input)
				if err != nil {
					log.Fatal(err)
				}

				stdoutbuf.Reset()

				go func() {
					for line := range outch {
						printer.Print(line)
						stdoutbuf.WriteString(line)
					}
					fmt.Fprintf(f, "outch finished. cursorAfterPrompt now. runecount: %d\n", utf8.RuneCount(input))
					tty.cursorAfterPrompt(utf8.RuneCount(input))
				}()

				go func() {
					for line := range errch {
						printer.Print(line)
						stdoutbuf.WriteString(line)
					}
					fmt.Fprintf(f, "errch finished. cursorAfterPrompt now. runecount: %d\n", utf8.RuneCount(input))
					// tty.cursorAfterPrompt(utf8.RuneCount(input))
				}()
			}
		}
	}
}

func main() {
	flVersion := flag.Bool("v", false, "Print fzz version and quit")
	flag.Usage = printUsage
	flag.Parse()

	if *flVersion {
		fmt.Printf("fzz %s\n", VERSION)
		os.Exit(0)
	}

	if len(flag.Args()) < 2 {
		fmt.Fprintf(os.Stderr, usage)
		os.Exit(1)
	}

	if placeholder = os.Getenv("FZZ_PLACEHOLDER"); placeholder == "" {
		placeholder = defaultPlaceholder
	}

	if !validPlaceholder(placeholder) {
		fmt.Fprintln(os.Stderr, "Placeholder is not valid, needs even number of characters")
		os.Exit(1)
	}

	if !containsPlaceholder(flag.Args(), placeholder) {
		fmt.Fprintln(os.Stderr, "No placeholder in arguments")
		os.Exit(1)
	}

	tty, err := NewTTY()
	if err != nil {
		log.Fatal(err)
	}
	defer tty.resetState()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		tty.resetState()
		os.Exit(1)
	}()
	tty.setSttyState("cbreak", "-echo")

	stdinbuf := bytes.Buffer{}
	if isPipe(os.Stdin) {
		io.Copy(&stdinbuf, os.Stdin)
	}

	printer := NewPrinter(tty, tty.cols, tty.rows-1) // prompt is one row
	mainLoop(tty, printer, &stdinbuf)
}
<|endoftext|>


<commit_before>package g

import "path/filepath"

var Modules map[string]bool
var BinOf map[string]string
var cfgOf map[string]string
var ModuleApps map[string]string
var logpathOf map[string]string
var PidOf map[string]string
var AllModulesInOrder []string

func init() {
	//	dirs, _ := ioutil.ReadDir("./modules")
	//	for _, dir := range dirs {
	//		Modules[dir.Name()] = true
	//	}
	Modules = map[string]bool{
		"agent":      true,
		"aggregator": true,
		"alarm":      true,
		"fe":         true,
		"graph":      true,
		"hbs":        true,
		"judge":      true,
		"nodata":     true,
		"mysqlapi":    true,
		"query":      true,
		"sender":     true,
		"task":       true,
		"transfer":   true,
		"f2e-api":    true,
	}

	BinOf = map[string]string{
		"agent":      "./agent/bin/falcon-agent",
		"aggregator": "./aggregator/bin/falcon-aggregator",
		"alarm":      "./alarm/bin/falcon-alarm",
		"fe":         "./fe/bin/falcon-fe",
		"graph":      "./graph/bin/falcon-graph",
		"hbs":        "./hbs/bin/falcon-hbs",
		"judge":      "./judge/bin/falcon-judge",
		"nodata":     "./nodata/bin/falcon-nodata",
		"mysqlapi":    "./mysqlapi/bin/falcon-mysqlapi",
		"query":      "./query/bin/falcon-query",
		"sender":     "./sender/bin/falcon-sender",
		"task":       "./task/bin/falcon-task",
		"transfer":   "./transfer/bin/falcon-transfer",
		"f2e-api":    "./f2e-api/bin/falcon-f2e-api",
	}

	cfgOf = map[string]string{
		"agent":      "./agent/config/cfg.json",
		"aggregator": "./aggregator/config/cfg.json",
		"alarm":      "./alarm/config/cfg.json",
		"fe":         "./fe/config/cfg.json",
		"graph":      "./graph/config/cfg.json",
		"hbs":        "./hbs/config/cfg.json",
		"judge":      "./judge/config/cfg.json",
		"nodata":     "./nodata/config/cfg.json",
		"mysqlapi":    "./mysqlapi/config/cfg.json",
		"query":      "./query/config/cfg.json",
		"sender":     "./sender/config/cfg.json",
		"task":       "./task/config/cfg.json",
		"transfer":   "./transfer/config/cfg.json",
		"f2e-api":    "./f2e-api/config/cfg.json",
	}

	ModuleApps = map[string]string{
		"agent":      "falcon-agent",
		"aggregator": "falcon-aggregator",
		"alarm":      "falcon-alarm",
		"graph":      "falcon-graph",
		"fe":         "falcon-fe",
		"hbs":        "falcon-hbs",
		"judge":      "falcon-judge",
		"nodata":     "falcon-nodata",
		"mysqlapi":    "falcon-mysqlapi",
		"query":      "falcon-query",
		"sender":     "falcon-sender",
		"task":       "falcon-task",
		"transfer":   "falcon-transfer",
		"f2e-api":    "falcon-f2e-api",
	}

	logpathOf = map[string]string{
		"agent":      "./agent/logs/agent.log",
		"aggregator": "./aggregator/logs/aggregator.log",
		"alarm":      "./alarm/logs/alarm.log",
		"fe":         "./fe/logs/fe.log",
		"graph":      "./graph/logs/graph.log",
		"hbs":        "./hbs/logs/hbs.log",
		"judge":      "./judge/logs/judge.log",
		"nodata":     "./nodata/logs/nodata.log",
		"mysqlapi":    "./mysqlapi/logs/mysqlapi.log",
		"query":      "./query/logs/query.log",
		"sender":     "./sender/logs/sender.log",
		"task":       "./task/logs/task.log",
		"transfer":   "./transfer/logs/transfer.log",
		"f2e-api":    "./f2e-api/logs/f2e-api.log",
	}

	PidOf = map[string]string{
		"agent":      "<NOT SET>",
		"aggregator": "<NOT SET>",
		"alarm":      "<NOT SET>",
		"graph":      "<NOT SET>",
		"fe":         "<NOT SET>",
		"hbs":        "<NOT SET>",
		"judge":      "<NOT SET>",
		"nodata":     "<NOT SET>",
		"mysqlapi":    "<NOT SET>",
		"query":      "<NOT SET>",
		"sender":     "<NOT SET>",
		"task":       "<NOT SET>",
		"transfer":   "<NOT SET>",
		"f2e-api":    "<NOT SET>",
	}

	// Modules are deployed in this order
	AllModulesInOrder = []string{
		"graph",
		"hbs",
		"fe",
		"alarm",
		"sender",
		"query",
		"judge",
		"transfer",
		"nodata",
		"mysqlapi",
		"task",
		"aggregator",
		"agent",
		"f2e-api",
	}
}

func Bin(name string) string {
	p, _ := filepath.Abs(BinOf[name])
	return p
}

func Cfg(name string) string {
	p, _ := filepath.Abs(cfgOf[name])
	return p
}

func LogPath(name string) string {
	p, _ := filepath.Abs(logpathOf[name])
	return p
}

func LogDir(name string) string {
	d, _ := filepath.Abs(filepath.Dir(logpathOf[name]))
	return d
}
<commit_msg>Run `make fmt`<commit_after>package g

import "path/filepath"

var Modules map[string]bool
var BinOf map[string]string
var cfgOf map[string]string
var ModuleApps map[string]string
var logpathOf map[string]string
var PidOf map[string]string
var AllModulesInOrder []string

func init() {
	//	dirs, _ := ioutil.ReadDir("./modules")
	//	for _, dir := range dirs {
	//		Modules[dir.Name()] = true
	//	}
	Modules = map[string]bool{
		"agent":      true,
		"aggregator": true,
		"alarm":      true,
		"fe":         true,
		"graph":      true,
		"hbs":        true,
		"judge":      true,
		"nodata":     true,
		"mysqlapi":   true,
		"query":      true,
		"sender":     true,
		"task":       true,
		"transfer":   true,
		"f2e-api":    true,
	}

	BinOf = map[string]string{
		"agent":      "./agent/bin/falcon-agent",
		"aggregator": "./aggregator/bin/falcon-aggregator",
		"alarm":      "./alarm/bin/falcon-alarm",
		"fe":         "./fe/bin/falcon-fe",
		"graph":      "./graph/bin/falcon-graph",
		"hbs":        "./hbs/bin/falcon-hbs",
		"judge":      "./judge/bin/falcon-judge",
		"nodata":     "./nodata/bin/falcon-nodata",
		"mysqlapi":   "./mysqlapi/bin/falcon-mysqlapi",
		"query":      "./query/bin/falcon-query",
		"sender":     "./sender/bin/falcon-sender",
		"task":       "./task/bin/falcon-task",
		"transfer":   "./transfer/bin/falcon-transfer",
		"f2e-api":    "./f2e-api/bin/falcon-f2e-api",
	}

	cfgOf = map[string]string{
		"agent":      "./agent/config/cfg.json",
		"aggregator": "./aggregator/config/cfg.json",
		"alarm":      "./alarm/config/cfg.json",
		"fe":         "./fe/config/cfg.json",
		"graph":      "./graph/config/cfg.json",
		"hbs":        "./hbs/config/cfg.json",
		"judge":      "./judge/config/cfg.json",
		"nodata":     "./nodata/config/cfg.json",
		"mysqlapi":   "./mysqlapi/config/cfg.json",
		"query":      "./query/config/cfg.json",
		"sender":     "./sender/config/cfg.json",
		"task":       "./task/config/cfg.json",
		"transfer":   "./transfer/config/cfg.json",
		"f2e-api":    "./f2e-api/config/cfg.json",
	}

	ModuleApps = map[string]string{
		"agent":      "falcon-agent",
		"aggregator": "falcon-aggregator",
		"alarm":      "falcon-alarm",
		"graph":      "falcon-graph",
		"fe":         "falcon-fe",
		"hbs":        "falcon-hbs",
		"judge":      "falcon-judge",
		"nodata":     "falcon-nodata",
		"mysqlapi":   "falcon-mysqlapi",
		"query":      "falcon-query",
		"sender":     "falcon-sender",
		"task":       "falcon-task",
		"transfer":   "falcon-transfer",
		"f2e-api":    "falcon-f2e-api",
	}

	logpathOf = map[string]string{
		"agent":      "./agent/logs/agent.log",
		"aggregator": "./aggregator/logs/aggregator.log",
		"alarm":      "./alarm/logs/alarm.log",
		"fe":         "./fe/logs/fe.log",
		"graph":      "./graph/logs/graph.log",
		"hbs":        "./hbs/logs/hbs.log",
		"judge":      "./judge/logs/judge.log",
		"nodata":     "./nodata/logs/nodata.log",
		"mysqlapi":   "./mysqlapi/logs/mysqlapi.log",
		"query":      "./query/logs/query.log",
		"sender":     "./sender/logs/sender.log",
		"task":       "./task/logs/task.log",
		"transfer":   "./transfer/logs/transfer.log",
		"f2e-api":    "./f2e-api/logs/f2e-api.log",
	}

	PidOf = map[string]string{
		"agent":      "<NOT SET>",
		"aggregator": "<NOT SET>",
		"alarm":      "<NOT SET>",
		"graph":      "<NOT SET>",
		"fe":         "<NOT SET>",
		"hbs":        "<NOT SET>",
		"judge":      "<NOT SET>",
		"nodata":     "<NOT SET>",
		"mysqlapi":   "<NOT SET>",
		"query":      "<NOT SET>",
		"sender":     "<NOT SET>",
		"task":       "<NOT SET>",
		"transfer":   "<NOT SET>",
		"f2e-api":    "<NOT SET>",
	}

	// Modules are deployed in this order
	AllModulesInOrder = []string{
		"graph",
		"hbs",
		"fe",
		"alarm",
		"sender",
		"query",
		"judge",
		"transfer",
		"nodata",
		"mysqlapi",
		"task",
		"aggregator",
		"agent",
		"f2e-api",
	}
}

func Bin(name string) string {
	p, _ := filepath.Abs(BinOf[name])
	return p
}

func Cfg(name string) string {
	p, _ := filepath.Abs(cfgOf[name])
	return p
}

func LogPath(name string) string {
	p, _ := filepath.Abs(logpathOf[name])
	return p
}

func LogDir(name string) string {
	d, _ := filepath.Abs(filepath.Dir(logpathOf[name]))
	return d
}
<|endoftext|>


<commit_before>package schematic

import (
	"bytes"
	"fmt"
	"go/format"
	"strings"
	"text/template"

	bundle "github.com/interagent/schematic/templates"
)

var templates *template.Template

func init() {
	templates = template.New("package.tmpl").Funcs(helpers)
	templates = template.Must(bundle.Parse(templates))
}

// Generate generates code according to the schema.
func (s *Schema) Generate() ([]byte, error) {
	var buf bytes.Buffer

	s.Resolve(nil)

	name := strings.ToLower(strings.Split(s.Title, " ")[0])
	templates.ExecuteTemplate(&buf, "package.tmpl", name)

	// TODO: Check if we need time.
	templates.ExecuteTemplate(&buf, "imports.tmpl", []string{
		"encoding/json", "fmt", "io", "reflect",
		"net/http", "runtime", "time", "bytes",
	})
	templates.ExecuteTemplate(&buf, "service.tmpl", struct {
		Name    string
		URL     string
		Version string
	}{
		Name:    name,
		URL:     s.URL(),
		Version: s.Version,
	})

	for _, name := range sortedKeys(s.Properties) {
		schema := s.Properties[name]
		// Skipping definitions because there is no links, nor properties.
		if schema.Links == nil && schema.Properties == nil {
			continue
		}

		context := struct {
			Name       string
			Definition *Schema
		}{
			Name:       name,
			Definition: schema,
		}

		templates.ExecuteTemplate(&buf, "struct.tmpl", context)
		templates.ExecuteTemplate(&buf, "funcs.tmpl", context)
	}

	// Remove blank lines added by text/template
	bytes := newlines.ReplaceAll(buf.Bytes(), []byte(""))

	// Format sources
	clean, err := format.Source(bytes)
	if err != nil {
		return buf.Bytes(), err
	}
	return clean, nil
}

// Resolve resolves reference inside the schema.
func (s *Schema) Resolve(r *Schema) *Schema {
	if r == nil {
		r = s
	}
	for n, d := range s.Definitions {
		s.Definitions[n] = d.Resolve(r)
	}
	for n, p := range s.Properties {
		s.Properties[n] = p.Resolve(r)
	}
	for n, p := range s.PatternProperties {
		s.PatternProperties[n] = p.Resolve(r)
	}
	if s.Items != nil {
		s.Items = s.Items.Resolve(r)
	}
	if s.Ref != nil {
		s = s.Ref.Resolve(r)
	}
	if len(s.OneOf) > 0 {
		s = s.OneOf[0].Ref.Resolve(r)
	}
	if len(s.AnyOf) > 0 {
		s = s.AnyOf[0].Ref.Resolve(r)
	}
	for _, l := range s.Links {
		l.Resolve(r)
	}
	return s
}

// Types returns the array of types described by this schema.
func (s *Schema) Types() (types []string) {
	if arr, ok := s.Type.([]interface{}); ok {
		for _, v := range arr {
			types = append(types, v.(string))
		}
	} else if str, ok := s.Type.(string); ok {
		types = append(types, str)
	} else {
		panic(fmt.Sprintf("unknown type %v", s.Type))
	}
	return types
}

// GoType returns the Go type for the given schema as string.
func (s *Schema) GoType() string {
	return s.goType(true, true)
}

// IsCustomType returns true if the schema declares a custom type.
func (s *Schema) IsCustomType() bool {
	return len(s.Properties) > 0
}

func (s *Schema) goType(required bool, force bool) (goType string) {
	// Resolve JSON reference/pointer
	types := s.Types()
	for _, kind := range types {
		switch kind {
		case "boolean":
			goType = "bool"
		case "string":
			switch s.Format {
			case "date-time":
				goType = "time.Time"
			default:
				goType = "string"
			}
		case "number":
			goType = "float64"
		case "integer":
			goType = "int"
		case "any":
			goType = "interface{}"
		case "array":
			if s.Items != nil {
				goType = "[]" + s.Items.goType(required, force)
			} else {
				goType = "[]interface{}"
			}
		case "object":
			// Check if patternProperties exists.
			if s.PatternProperties != nil {
				for _, prop := range s.PatternProperties {
					goType = fmt.Sprintf("map[string]%s", prop.GoType())
					break // We don't support more than one pattern for now.
				}
				continue
			}
			buf := bytes.NewBufferString("struct {")
			for _, name := range sortedKeys(s.Properties) {
				prop := s.Properties[name]
				req := contains(name, s.Required) || force
				templates.ExecuteTemplate(buf, "field.tmpl", struct {
					Definition *Schema
					Name       string
					Required   bool
					Type       string
				}{
					Definition: prop,
					Name:       name,
					Required:   req,
					Type:       prop.goType(req, force),
				})
			}
			buf.WriteString("}")
			goType = buf.String()
		case "null":
			continue
		default:
			panic(fmt.Sprintf("unknown type %s", kind))
		}
	}
	if goType == "" {
		panic(fmt.Sprintf("type not found : %s", types))
	}
	// Types allow null
	if contains("null", types) || !(required || force) {
		return "*" + goType
	}
	return goType
}

// Values returns function return values types.
func (s *Schema) Values(name string, l *Link) []string {
	var values []string
	name = returnType(name, s, l)
	switch l.Rel {
	case "destroy", "empty":
		values = append(values, "error")
	case "instances":
		if l.TargetSchema == nil || s.ReturnsCustomType(l) {
			values = append(values, fmt.Sprintf("[]*%s", name), "error")
		} else {
			values = append(values, fmt.Sprintf("[]*%s", s.ReturnedGoType(l)), "error")
		}
	default:
		if s.ReturnsCustomType(l) {
			values = append(values, fmt.Sprintf("*%s", name), "error")
		} else {
			values = append(values, s.ReturnedGoType(l), "error")
		}
	}
	return values
}

// URL returns schema base URL.
func (s *Schema) URL() string {
	for _, l := range s.Links {
		if l.Rel == "self" {
			return l.HRef.String()
		}
	}
	return ""
}

// ReturnsCustomType returns true if the link returns a custom type.
func (s *Schema) ReturnsCustomType(l *Link) bool {
	if l.TargetSchema != nil {
		return len(l.TargetSchema.Properties) > 0
	}
	return len(s.Properties) > 0
}

// ReturnedGoType returns Go type returned by the given link as a string.
func (s *Schema) ReturnedGoType(l *Link) string {
	if l.TargetSchema != nil {
		return l.TargetSchema.goType(true, false)
	}
	return s.goType(true, false)
}

// Parameters returns function parameters names and types.
func (l *Link) Parameters() ([]string, map[string]string) {
	if l.HRef == nil {
		// No HRef property
		panic(fmt.Errorf("no href property declared for %s", l.Title))
	}
	var order []string
	params := make(map[string]string)
	for _, name := range l.HRef.Order {
		def := l.HRef.Schemas[name]
		order = append(order, name)
		params[name] = def.GoType()
	}
	switch l.Rel {
	case "update", "create":
		order = append(order, "o")
		params["o"] = l.GoType()
	case "instances":
		order = append(order, "lr")
		params["lr"] = "*ListRange"
	}
	return order, params
}

// Resolve resolve link schema and href.
func (l *Link) Resolve(r *Schema) {
	if l.Schema != nil {
		l.Schema = l.Schema.Resolve(r)
	}
	if l.TargetSchema != nil {
		l.TargetSchema = l.TargetSchema.Resolve(r)
	}
	l.HRef.Resolve(r)
}

// GoType returns Go type for the given schema as string.
func (l *Link) GoType() string {
	return l.Schema.goType(true, false)
}
<commit_msg>Workaround to prevent failure when there are references between resources<commit_after>package schematic

import (
	"bytes"
	"fmt"
	"go/format"
	"strings"
	"text/template"

	bundle "github.com/interagent/schematic/templates"
)

var templates *template.Template

func init() {
	templates = template.New("package.tmpl").Funcs(helpers)
	templates = template.Must(bundle.Parse(templates))
}

// Generate generates code according to the schema.
func (s *Schema) Generate() ([]byte, error) {
	var buf bytes.Buffer

	for i := 0; i < 2; i++ {
		s.Resolve(nil)
	}

	name := strings.ToLower(strings.Split(s.Title, " ")[0])
	templates.ExecuteTemplate(&buf, "package.tmpl", name)

	// TODO: Check if we need time.
	templates.ExecuteTemplate(&buf, "imports.tmpl", []string{
		"encoding/json", "fmt", "io", "reflect",
		"net/http", "runtime", "time", "bytes",
	})
	templates.ExecuteTemplate(&buf, "service.tmpl", struct {
		Name    string
		URL     string
		Version string
	}{
		Name:    name,
		URL:     s.URL(),
		Version: s.Version,
	})

	for _, name := range sortedKeys(s.Properties) {
		schema := s.Properties[name]
		// Skipping definitions because there is no links, nor properties.
		if schema.Links == nil && schema.Properties == nil {
			continue
		}

		context := struct {
			Name       string
			Definition *Schema
		}{
			Name:       name,
			Definition: schema,
		}

		templates.ExecuteTemplate(&buf, "struct.tmpl", context)
		templates.ExecuteTemplate(&buf, "funcs.tmpl", context)
	}

	// Remove blank lines added by text/template
	bytes := newlines.ReplaceAll(buf.Bytes(), []byte(""))

	// Format sources
	clean, err := format.Source(bytes)
	if err != nil {
		return buf.Bytes(), err
	}
	return clean, nil
}

// Resolve resolves reference inside the schema.
func (s *Schema) Resolve(r *Schema) *Schema {
	if r == nil {
		r = s
	}
	for n, d := range s.Definitions {
		s.Definitions[n] = d.Resolve(r)
	}
	for n, p := range s.Properties {
		s.Properties[n] = p.Resolve(r)
	}
	for n, p := range s.PatternProperties {
		s.PatternProperties[n] = p.Resolve(r)
	}
	if s.Items != nil {
		s.Items = s.Items.Resolve(r)
	}
	if s.Ref != nil {
		s = s.Ref.Resolve(r)
	}
	if len(s.OneOf) > 0 {
		s = s.OneOf[0].Ref.Resolve(r)
	}
	if len(s.AnyOf) > 0 {
		s = s.AnyOf[0].Ref.Resolve(r)
	}
	for _, l := range s.Links {
		l.Resolve(r)
	}
	return s
}

// Types returns the array of types described by this schema.
func (s *Schema) Types() (types []string) {
	if arr, ok := s.Type.([]interface{}); ok {
		for _, v := range arr {
			types = append(types, v.(string))
		}
	} else if str, ok := s.Type.(string); ok {
		types = append(types, str)
	} else {
		panic(fmt.Sprintf("unknown type %v", s.Type))
	}
	return types
}

// GoType returns the Go type for the given schema as string.
func (s *Schema) GoType() string {
	return s.goType(true, true)
}

// IsCustomType returns true if the schema declares a custom type.
func (s *Schema) IsCustomType() bool {
	return len(s.Properties) > 0
}

func (s *Schema) goType(required bool, force bool) (goType string) {
	// Resolve JSON reference/pointer
	types := s.Types()
	for _, kind := range types {
		switch kind {
		case "boolean":
			goType = "bool"
		case "string":
			switch s.Format {
			case "date-time":
				goType = "time.Time"
			default:
				goType = "string"
			}
		case "number":
			goType = "float64"
		case "integer":
			goType = "int"
		case "any":
			goType = "interface{}"
		case "array":
			if s.Items != nil {
				goType = "[]" + s.Items.goType(required, force)
			} else {
				goType = "[]interface{}"
			}
		case "object":
			// Check if patternProperties exists.
			if s.PatternProperties != nil {
				for _, prop := range s.PatternProperties {
					goType = fmt.Sprintf("map[string]%s", prop.GoType())
					break // We don't support more than one pattern for now.
				}
				continue
			}
			buf := bytes.NewBufferString("struct {")
			for _, name := range sortedKeys(s.Properties) {
				prop := s.Properties[name]
				req := contains(name, s.Required) || force
				templates.ExecuteTemplate(buf, "field.tmpl", struct {
					Definition *Schema
					Name       string
					Required   bool
					Type       string
				}{
					Definition: prop,
					Name:       name,
					Required:   req,
					Type:       prop.goType(req, force),
				})
			}
			buf.WriteString("}")
			goType = buf.String()
		case "null":
			continue
		default:
			panic(fmt.Sprintf("unknown type %s", kind))
		}
	}
	if goType == "" {
		panic(fmt.Sprintf("type not found : %s", types))
	}
	// Types allow null
	if contains("null", types) || !(required || force) {
		return "*" + goType
	}
	return goType
}

// Values returns function return values types.
func (s *Schema) Values(name string, l *Link) []string {
	var values []string
	name = returnType(name, s, l)
	switch l.Rel {
	case "destroy", "empty":
		values = append(values, "error")
	case "instances":
		if l.TargetSchema == nil || s.ReturnsCustomType(l) {
			values = append(values, fmt.Sprintf("[]*%s", name), "error")
		} else {
			values = append(values, fmt.Sprintf("[]*%s", s.ReturnedGoType(l)), "error")
		}
	default:
		if s.ReturnsCustomType(l) {
			values = append(values, fmt.Sprintf("*%s", name), "error")
		} else {
			values = append(values, s.ReturnedGoType(l), "error")
		}
	}
	return values
}

// URL returns schema base URL.
func (s *Schema) URL() string {
	for _, l := range s.Links {
		if l.Rel == "self" {
			return l.HRef.String()
		}
	}
	return ""
}

// ReturnsCustomType returns true if the link returns a custom type.
func (s *Schema) ReturnsCustomType(l *Link) bool {
	if l.TargetSchema != nil {
		return len(l.TargetSchema.Properties) > 0
	}
	return len(s.Properties) > 0
}

// ReturnedGoType returns Go type returned by the given link as a string.
func (s *Schema) ReturnedGoType(l *Link) string {
	if l.TargetSchema != nil {
		return l.TargetSchema.goType(true, false)
	}
	return s.goType(true, false)
}

// Parameters returns function parameters names and types.
func (l *Link) Parameters() ([]string, map[string]string) {
	if l.HRef == nil {
		// No HRef property
		panic(fmt.Errorf("no href property declared for %s", l.Title))
	}
	var order []string
	params := make(map[string]string)
	for _, name := range l.HRef.Order {
		def := l.HRef.Schemas[name]
		order = append(order, name)
		params[name] = def.GoType()
	}
	switch l.Rel {
	case "update", "create":
		order = append(order, "o")
		params["o"] = l.GoType()
	case "instances":
		order = append(order, "lr")
		params["lr"] = "*ListRange"
	}
	return order, params
}

// Resolve resolve link schema and href.
func (l *Link) Resolve(r *Schema) {
	if l.Schema != nil {
		l.Schema = l.Schema.Resolve(r)
	}
	if l.TargetSchema != nil {
		l.TargetSchema = l.TargetSchema.Resolve(r)
	}
	l.HRef.Resolve(r)
}

// GoType returns Go type for the given schema as string.
func (l *Link) GoType() string {
	return l.Schema.goType(true, false)
}
<|endoftext|>


<commit_before>package wuxia

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/robertkrimen/otto"
	"github.com/spf13/afero"
)

const (
	scriptsDir = "scripts"
	initDir    = "init"
	planDir    = "plan"
	configFile = "config.json"
	indexFile  = "index.js"
)

type buildStage int

const (
	stageInit buildStage = iota
	stageConfig
	stagePlan
	stageExec
)

func (s buildStage) String() string {
	var rst string
	switch s {
	case stageInit:
		rst = "init"
	case stageConfig:
		rst = "config"
	case stagePlan:
		rst = "plan"
	case stageExec:
		rst = "exec"
	default:
		rst = "unkown stage"
	}
	return rst
}

//buildError error returned when building the static website. The error string
//returned is a json string that encodes the build stage and the message.
type buildError struct {
	Stage   string `json:"stage"`
	Message string `json:"msg"`
}

func buildErr(stage buildStage, msg string) error {
	return &buildError{Stage: stage.String(), Message: msg}
}

func (b *buildError) Error() string {
	o, err := json.Marshal(b)
	if err != nil {
		return err.Error()
	}
	return string(o)
}

//Generator provides the static website generation capabilities.This is heavily
//integrated with the otto javascript runtime.
type Generator struct {
	vm  *otto.Otto
	sys *System
	fs  afero.Fs

	// This is the absolute path to the root of the project from which the
	// Generator will be operating.
	workDir string
}

//NewGenerator retrunes a new  Generator.
func NewGenerator(vm *otto.Otto, sys *System, fs afero.Fs) *Generator {
	return &Generator{
		vm:  vm,
		sys: sys,
		fs:  fs,
	}
}

//Build builds a project.
func (g *Generator) Build() error {
	return evaluate(g.init, g.config, g.plan, g.exec, g.down)
}

//SetWorkDir sets dir as wth working directory for the generator
func (g *Generator) SetWorkDir(dir string) error {
	_, err := g.fs.Stat(dir)
	if err != nil {
		return err
	}
	g.workDir = dir
	return nil
}

//initializes the build process. Any stages after this will have the generator
//already bootstraped.
//
// It is possible to bootstrap the generator from the project( User's side) by
// providing an entry javascript file in the default path of
// scripts/init/index.js which will be executed and you can overide the default
// entry script which is evaluated internally
//
// Initialzation is offloaded to the javascript runtine of the generator..Any
// error returned is a build error.
func (g *Generator) init() error {
	if g.sys == nil {
		g.sys = defaultSystem()
	}
	if g.vm == nil {
		g.vm = defaultVM(g.sys)
	}
	_ = g.vm.Set("sys", func(call otto.FunctionCall) otto.Value {
		data, err := json.Marshal(g.sys)
		if err != nil {
			Panic(err)
		}
		val, err := call.Otto.Call("JSON.parse", nil, string(data))
		if err != nil {
			Panic(err)
		}
		return val
	})
	_, err := g.vm.Eval(entryScript())
	if err != nil {
		return buildErr(stageInit, err.Error())
	}

	// evaluate project provided entry script if provided. We ignore if the file
	// is not provided but any errors arsing from evaluating a provided script is
	// a built error.
	entryFile := filepath.Join(g.workDir, scriptsDir, initDir, indexFile)
	err = g.evaluateFile(entryFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return buildErr(stageInit, err.Error())
		}
	}

	// Properly set working directory/
	if g.workDir == "" {
		wd, err := os.Getwd()
		if err != nil {
			return buildErr(stageInit, err.Error())
		}
		g.workDir = wd
	}
	return nil
}

func defaultSystem() *System {
	return &System{
		Boot: &Boot{
			ConfigiFile: configFile,
			PlanFile:    "index.js",
		},
	}
}

// opens the file in the specified path and evaluates it withing the context of
// the javascript runtine.
//
// The evaluated javascript code can mutate the global state. Use execFile to
// execute the javascript without mutating the state of the generato'r
// javascript runtime.
//
// TODO: (gernest) implement callFile if necessary
func (g *Generator) evaluateFile(path string) error {
	f, err := g.fs.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	_, err = g.vm.Eval(data)
	if err != nil {
		return err
	}
	return nil
}

func defaultVM(sys *System) *otto.Otto {
	return otto.New()
}

func (g *Generator) config() error {
	v, err := g.vm.Call("getCurrentSys", nil)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	cSys := &System{}
	str, _ := v.ToString()
	err = json.Unmarshal([]byte(str), cSys)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	cfgFile := filepath.Join(g.workDir, cSys.Boot.ConfigiFile)
	cf, err := g.fs.Open(cfgFile)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	defer func() { _ = cf.Close() }()
	data, err := ioutil.ReadAll(cf)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	c := &Config{}
	err = json.Unmarshal(data, c)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	if cSys.Config == nil {
		cSys.Config = c
	} else {
		updateConfig(cSys.Config, c)
	}
	g.sys = cSys
	return nil
}

func updateConfig(old, new *Config) {
}

func (g *Generator) plan() error {
	return nil
}
func (g *Generator) exec() error {
	return nil
}
func (g *Generator) down() error {
	return nil
}

func evaluate(fn ...func() error) error {
	var err error
	for i := 0; i < len(fn); i++ {
		err = fn[i]()
		if err != nil {
			return err
		}
	}
	return err
}
<commit_msg>Use custom defined configuration object<commit_after>package wuxia

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/robertkrimen/otto"
	"github.com/spf13/afero"
)

const (
	scriptsDir = "scripts"
	initDir    = "init"
	planDir    = "plan"
	configFile = "config.json"
	indexFile  = "index.js"
)

type buildStage int

const (
	stageInit buildStage = iota
	stageConfig
	stagePlan
	stageExec
)

func (s buildStage) String() string {
	var rst string
	switch s {
	case stageInit:
		rst = "init"
	case stageConfig:
		rst = "config"
	case stagePlan:
		rst = "plan"
	case stageExec:
		rst = "exec"
	default:
		rst = "unkown stage"
	}
	return rst
}

//buildError error returned when building the static website. The error string
//returned is a json string that encodes the build stage and the message.
type buildError struct {
	Stage   string `json:"stage"`
	Message string `json:"msg"`
}

func buildErr(stage buildStage, msg string) error {
	return &buildError{Stage: stage.String(), Message: msg}
}

func (b *buildError) Error() string {
	o, err := json.Marshal(b)
	if err != nil {
		return err.Error()
	}
	return string(o)
}

//Generator provides the static website generation capabilities.This is heavily
//integrated with the otto javascript runtime.
type Generator struct {
	vm  *otto.Otto
	sys *System
	fs  afero.Fs

	// This is the absolute path to the root of the project from which the
	// Generator will be operating.
	workDir string
}

//NewGenerator retrunes a new  Generator.
func NewGenerator(vm *otto.Otto, sys *System, fs afero.Fs) *Generator {
	return &Generator{
		vm:  vm,
		sys: sys,
		fs:  fs,
	}
}

//Build builds a project.
func (g *Generator) Build() error {
	return evaluate(g.init, g.config, g.plan, g.exec, g.down)
}

//SetWorkDir sets dir as wth working directory for the generator
func (g *Generator) SetWorkDir(dir string) error {
	_, err := g.fs.Stat(dir)
	if err != nil {
		return err
	}
	g.workDir = dir
	return nil
}

//initializes the build process. Any stages after this will have the generator
//already bootstraped.
//
// It is possible to bootstrap the generator from the project( User's side) by
// providing an entry javascript file in the default path of
// scripts/init/index.js which will be executed and you can overide the default
// entry script which is evaluated internally
//
// Initialzation is offloaded to the javascript runtine of the generator..Any
// error returned is a build error.
func (g *Generator) init() error {
	if g.sys == nil {
		g.sys = defaultSystem()
	}
	if g.vm == nil {
		g.vm = defaultVM(g.sys)
	}
	_ = g.vm.Set("sys", func(call otto.FunctionCall) otto.Value {
		data, err := json.Marshal(g.sys)
		if err != nil {
			Panic(err)
		}
		val, err := call.Otto.Call("JSON.parse", nil, string(data))
		if err != nil {
			Panic(err)
		}
		return val
	})
	_, err := g.vm.Eval(entryScript())
	if err != nil {
		return buildErr(stageInit, err.Error())
	}

	// evaluate project provided entry script if provided. We ignore if the file
	// is not provided but any errors arsing from evaluating a provided script is
	// a built error.
	entryFile := filepath.Join(g.workDir, scriptsDir, initDir, indexFile)
	err = g.evaluateFile(entryFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return buildErr(stageInit, err.Error())
		}
	}

	// Properly set working directory/
	if g.workDir == "" {
		wd, err := os.Getwd()
		if err != nil {
			return buildErr(stageInit, err.Error())
		}
		g.workDir = wd
	}
	return nil
}

func defaultSystem() *System {
	return &System{
		Boot: &Boot{
			ConfigiFile: configFile,
			PlanFile:    "index.js",
		},
	}
}

// opens the file in the specified path and evaluates it withing the context of
// the javascript runtine.
//
// The evaluated javascript code can mutate the global state. Use execFile to
// execute the javascript without mutating the state of the generato'r
// javascript runtime.
//
// TODO: (gernest) implement callFile if necessary
func (g *Generator) evaluateFile(path string) error {
	f, err := g.fs.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	_, err = g.vm.Eval(data)
	if err != nil {
		return err
	}
	return nil
}

func defaultVM(sys *System) *otto.Otto {
	return otto.New()
}

func (g *Generator) config() error {
	v, err := g.vm.Call("getCurrentSys", nil)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	cSys := &System{}
	str, _ := v.ToString()
	err = json.Unmarshal([]byte(str), cSys)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	cfgFile := filepath.Join(g.workDir, cSys.Boot.ConfigiFile)
	cf, err := g.fs.Open(cfgFile)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	defer func() { _ = cf.Close() }()
	data, err := ioutil.ReadAll(cf)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	c := &Config{}
	err = json.Unmarshal(data, c)
	if err != nil {
		return buildErr(stageConfig, err.Error())
	}
	if cSys.Config == nil {
		cSys.Config = c
	}
	g.sys = cSys
	return nil
}

func (g *Generator) plan() error {
	return nil
}
func (g *Generator) exec() error {
	return nil
}
func (g *Generator) down() error {
	return nil
}

func evaluate(fn ...func() error) error {
	var err error
	for i := 0; i < len(fn); i++ {
		err = fn[i]()
		if err != nil {
			return err
		}
	}
	return err
}
<|endoftext|>


<commit_before>package srcscan

import (
	"go/build"
	"os"
	"path/filepath"
)

// Config specifies options for Scan.
type Config struct {
	// Base is the base directory that all source unit paths are made relative to. Paths within the
	// concrete source unit structs are relative to the source unit path, not Base. If Base is the
	// empty string, the current working directory is used.
	Base string

	// Profiles is the list of profiles to use when scanning for source units. If nil,
	// AllProfiles is used.
	Profiles []Profile

	// SkipDirs is a list of names of directories that are skipped while scanning.
	SkipDirs []string

	// PathIndependent, if true, indicates that all filesystem paths should be relativized, if
	// possible, or else cleared.
	PathIndependent bool

	NodeJSPackage NodeJSPackageConfig
	GoPackage     GoPackageConfig
}

func (c Config) skipDir(name string) bool {
	for _, dirToSkip := range c.SkipDirs {
		if name == dirToSkip {
			return true
		}
	}
	return false
}

var Default = Config{
	SkipDirs: []string{"node_modules", "vendor", "testdata", "site-packages"},
	NodeJSPackage: NodeJSPackageConfig{
		TestDirs:          []string{"test", "tests", "spec", "specs", "unit", "mocha", "karma", "testdata"},
		TestSuffixes:      []string{"test.js", "tests.js", "spec.js", "specs.js"},
		SupportDirs:       []string{"build_support"},
		SupportFilenames:  []string{"Gruntfile.js", "build.js", "Makefile.dryice.js", "build.config.js"},
		ExampleDirs:       []string{"example", "examples", "sample", "samples", "doc", "docs", "demo", "demos"},
		ScriptDirs:        []string{"bin", "script", "scripts", "tool", "tools"},
		GeneratedDirs:     []string{"build", "dist"},
		GeneratedSuffixes: []string{".min.js", "-min.js", ".optimized.js", "-optimized.js"},
		VendorDirs:        []string{"vendor", "bower_components", "node_modules", "assets", "public", "static", "resources"},
	},
	GoPackage: GoPackageConfig{
		BuildContext: build.Default,
	},
}

// Scan is shorthand for Default.Scan.
func Scan(dir string) (found []Unit, err error) {
	return Default.Scan(dir)
}

// Scan walks the directory tree at dir, looking for source units that match profiles in the
// configuration. Scan returns a list of all source units found.
func (c Config) Scan(dir string) (found []Unit, err error) {
	var profiles []Profile
	if c.Profiles != nil {
		profiles = c.Profiles
	} else {
		profiles = AllProfiles
	}

	c.Base, _ = filepath.Abs(c.Base)

	for _, profile := range profiles {
		err = filepath.Walk(dir, func(path string, info os.FileInfo, inerr error) (err error) {
			if inerr != nil {
				return inerr
			}
			if info.IsDir() {
				if dir != path && c.skipDir(info.Name()) {
					return filepath.SkipDir
				}

				var dirh *os.File
				dirh, err = os.Open(path)
				if err != nil {
					return
				}
				defer dirh.Close()

				var filenames []string
				filenames, err = dirh.Readdirnames(0)
				if err != nil {
					return
				}

				if profile.Dir != nil && profile.Dir.DirMatches(path, filenames) {
					relpath, abspath := c.relAbsPath(path)
					found = append(found, profile.Unit(abspath, relpath, c, info))
					if profile.TopLevelOnly {
						return filepath.SkipDir
					}
				}
			} else {
				if profile.File != nil && profile.File.FileMatches(path) {
					relpath, abspath := c.relAbsPath(path)
					found = append(found, profile.Unit(abspath, relpath, c, info))
				}
			}
			return
		})
	}

	return
}

func (c Config) relAbsPath(path string) (rel string, abs string) {
	abs, err := filepath.Abs(path)
	if err != nil {
		panic(err.Error())
	}

	rel, err = filepath.Rel(c.Base, abs)
	if err != nil {
		panic(err.Error())
	}
	return
}
<commit_msg>Omit bower_components dirs<commit_after>package srcscan

import (
	"go/build"
	"os"
	"path/filepath"
)

// Config specifies options for Scan.
type Config struct {
	// Base is the base directory that all source unit paths are made relative to. Paths within the
	// concrete source unit structs are relative to the source unit path, not Base. If Base is the
	// empty string, the current working directory is used.
	Base string

	// Profiles is the list of profiles to use when scanning for source units. If nil,
	// AllProfiles is used.
	Profiles []Profile

	// SkipDirs is a list of names of directories that are skipped while scanning.
	SkipDirs []string

	// PathIndependent, if true, indicates that all filesystem paths should be relativized, if
	// possible, or else cleared.
	PathIndependent bool

	NodeJSPackage NodeJSPackageConfig
	GoPackage     GoPackageConfig
}

func (c Config) skipDir(name string) bool {
	for _, dirToSkip := range c.SkipDirs {
		if name == dirToSkip {
			return true
		}
	}
	return false
}

var Default = Config{
	SkipDirs: []string{"node_modules", "vendor", "testdata", "site-packages", "bower_components"},
	NodeJSPackage: NodeJSPackageConfig{
		TestDirs:          []string{"test", "tests", "spec", "specs", "unit", "mocha", "karma", "testdata"},
		TestSuffixes:      []string{"test.js", "tests.js", "spec.js", "specs.js"},
		SupportDirs:       []string{"build_support"},
		SupportFilenames:  []string{"Gruntfile.js", "build.js", "Makefile.dryice.js", "build.config.js"},
		ExampleDirs:       []string{"example", "examples", "sample", "samples", "doc", "docs", "demo", "demos"},
		ScriptDirs:        []string{"bin", "script", "scripts", "tool", "tools"},
		GeneratedDirs:     []string{"build", "dist"},
		GeneratedSuffixes: []string{".min.js", "-min.js", ".optimized.js", "-optimized.js"},
		VendorDirs:        []string{"vendor", "bower_components", "node_modules", "assets", "public", "static", "resources"},
	},
	GoPackage: GoPackageConfig{
		BuildContext: build.Default,
	},
}

// Scan is shorthand for Default.Scan.
func Scan(dir string) (found []Unit, err error) {
	return Default.Scan(dir)
}

// Scan walks the directory tree at dir, looking for source units that match profiles in the
// configuration. Scan returns a list of all source units found.
func (c Config) Scan(dir string) (found []Unit, err error) {
	var profiles []Profile
	if c.Profiles != nil {
		profiles = c.Profiles
	} else {
		profiles = AllProfiles
	}

	c.Base, _ = filepath.Abs(c.Base)

	for _, profile := range profiles {
		err = filepath.Walk(dir, func(path string, info os.FileInfo, inerr error) (err error) {
			if inerr != nil {
				return inerr
			}
			if info.IsDir() {
				if dir != path && c.skipDir(info.Name()) {
					return filepath.SkipDir
				}

				var dirh *os.File
				dirh, err = os.Open(path)
				if err != nil {
					return
				}
				defer dirh.Close()

				var filenames []string
				filenames, err = dirh.Readdirnames(0)
				if err != nil {
					return
				}

				if profile.Dir != nil && profile.Dir.DirMatches(path, filenames) {
					relpath, abspath := c.relAbsPath(path)
					found = append(found, profile.Unit(abspath, relpath, c, info))
					if profile.TopLevelOnly {
						return filepath.SkipDir
					}
				}
			} else {
				if profile.File != nil && profile.File.FileMatches(path) {
					relpath, abspath := c.relAbsPath(path)
					found = append(found, profile.Unit(abspath, relpath, c, info))
				}
			}
			return
		})
	}

	return
}

func (c Config) relAbsPath(path string) (rel string, abs string) {
	abs, err := filepath.Abs(path)
	if err != nil {
		panic(err.Error())
	}

	rel, err = filepath.Rel(c.Base, abs)
	if err != nil {
		panic(err.Error())
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package bolthold

import (
	"errors"
	"reflect"
	"strings"

	bolt "go.etcd.io/bbolt"
)

// ErrNotFound is returned when no data is found for the given key
var ErrNotFound = errors.New("No data found for this key")

// Get retrieves a value from bolthold and puts it into result.  Result must be a pointer
func (s *Store) Get(key, result interface{}) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxGet(tx, key, result)
	})
}

// TxGet allows you to pass in your own bolt transaction to retrieve a value from the bolthold and puts it into result
func (s *Store) TxGet(tx *bolt.Tx, key, result interface{}) error {
	return s.get(tx, key, result)
}

// GetFromBucket allows you to specify the parent bucket for retrieving records
func (s *Store) GetFromBucket(parent *bolt.Bucket, key, result interface{}) error {
	return s.get(parent, key, result)
}

func (s *Store) get(source bucketSource, key, result interface{}) error {
	storer := s.newStorer(result)

	gk, err := s.encode(key)

	if err != nil {
		return err
	}

	bkt := source.Bucket([]byte(storer.Type()))
	if bkt == nil {
		return ErrNotFound
	}

	value := bkt.Get(gk)
	if value == nil {
		return ErrNotFound
	}

	err = s.decode(value, result)
	if err != nil {
		return err
	}

	tp := reflect.TypeOf(result).Elem()
	var keyField string

	for i := 0; i < tp.NumField(); i++ {
		if strings.Contains(string(tp.Field(i).Tag), BoltholdKeyTag) {
			keyField = tp.Field(i).Name
			break
		}
	}

	if keyField != "" {
		structKeyVal := reflect.ValueOf(result).Elem().FieldByName(keyField)
		keyVal := reflect.ValueOf(key)
		// FIXME
		if structKeyVal.Kind() == reflect.Ptr {
			structKeyVal = structKeyVal.Elem()
		}
		if keyVal.Kind() == reflect.Ptr {
			keyVal = keyVal.Elem()
		}

		structKeyVal.Set(keyVal)
	}

	return nil
}

// Find retrieves a set of values from the bolthold that matches the passed in query
// result must be a pointer to a slice.
// The result of the query will be appended to the passed in result slice, rather than the passed in slice being
// emptied.
func (s *Store) Find(result interface{}, query *Query) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxFind(tx, result, query)
	})
}

// TxFind allows you to pass in your own bolt transaction to retrieve a set of values from the bolthold
func (s *Store) TxFind(tx *bolt.Tx, result interface{}, query *Query) error {
	return s.findQuery(tx, result, query)
}

// FindInBucket allows you to specify a parent bucke to search in
func (s *Store) FindInBucket(parent *bolt.Bucket, result interface{}, query *Query) error {
	return s.findQuery(parent, result, query)
}

// FindOne returns a single record, and so result is NOT a slice, but an pointer to a struct, if no record is found
// that matches the query, then it returns ErrNotFound
func (s *Store) FindOne(result interface{}, query *Query) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxFindOne(tx, result, query)
	})
}

// TxFindOne allows you to pass in your own bolt transaction to retrieve a single record from the bolthold
func (s *Store) TxFindOne(tx *bolt.Tx, result interface{}, query *Query) error {
	return s.findOneQuery(tx, result, query)
}

// FindOneInBucket allows you to pass in your own bucket to retrieve a single record from the bolthold
func (s *Store) FindOneInBucket(parent *bolt.Bucket, result interface{}, query *Query) error {
	return s.findOneQuery(parent, result, query)
}

// Count returns the current record count for the passed in datatype
func (s *Store) Count(dataType interface{}, query *Query) (int, error) {
	count := 0
	err := s.Bolt().View(func(tx *bolt.Tx) error {
		var txErr error
		count, txErr = s.TxCount(tx, dataType, query)
		return txErr
	})
	return count, err
}

// TxCount returns the current record count from within the given transaction for the passed in datatype
func (s *Store) TxCount(tx *bolt.Tx, dataType interface{}, query *Query) (int, error) {
	return s.countQuery(tx, dataType, query)
}

// CountInBucket returns the current record count from within the given parent bucket
func (s *Store) CountInBucket(parent *bolt.Bucket, dataType interface{}, query *Query) (int, error) {
	return s.countQuery(parent, dataType, query)
}

// ForEach runs the function fn against every record that matches the query
// Useful for when working with large sets of data that you don't want to hold the entire result
// set in memory, similar to database cursors
// Return an error from fn, will stop the cursor from iterating
func (s *Store) ForEach(query *Query, fn interface{}) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxForEach(tx, query, fn)
	})
}

// TxForEach is the same as ForEach but you get to specify your transaction
func (s *Store) TxForEach(tx *bolt.Tx, query *Query, fn interface{}) error {
	return s.forEach(tx, query, fn)
}

// ForEachInBucket is the same as ForEach but you get to specify your parent bucket
func (s *Store) ForEachInBucket(parent *bolt.Bucket, query *Query, fn interface{}) error {
	return s.forEach(parent, query, fn)
}
<commit_msg>Changed to decode key value on Get<commit_after>// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package bolthold

import (
	"errors"
	"reflect"
	"strings"

	bolt "go.etcd.io/bbolt"
)

// ErrNotFound is returned when no data is found for the given key
var ErrNotFound = errors.New("No data found for this key")

// Get retrieves a value from bolthold and puts it into result.  Result must be a pointer
func (s *Store) Get(key, result interface{}) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxGet(tx, key, result)
	})
}

// TxGet allows you to pass in your own bolt transaction to retrieve a value from the bolthold and puts it into result
func (s *Store) TxGet(tx *bolt.Tx, key, result interface{}) error {
	return s.get(tx, key, result)
}

// GetFromBucket allows you to specify the parent bucket for retrieving records
func (s *Store) GetFromBucket(parent *bolt.Bucket, key, result interface{}) error {
	return s.get(parent, key, result)
}

func (s *Store) get(source bucketSource, key, result interface{}) error {
	storer := s.newStorer(result)

	gk, err := s.encode(key)

	if err != nil {
		return err
	}

	bkt := source.Bucket([]byte(storer.Type()))
	if bkt == nil {
		return ErrNotFound
	}

	value := bkt.Get(gk)
	if value == nil {
		return ErrNotFound
	}

	err = s.decode(value, result)
	if err != nil {
		return err
	}

	tp := reflect.TypeOf(result).Elem()
	var keyField string

	for i := 0; i < tp.NumField(); i++ {
		if strings.Contains(string(tp.Field(i).Tag), BoltholdKeyTag) {
			keyField = tp.Field(i).Name
			break
		}
	}

	if keyField != "" {
		err := s.decode(gk, reflect.ValueOf(result).Elem().FieldByName(keyField).Addr().Interface())
		if err != nil {
			return err
		}
	}

	return nil
}

// Find retrieves a set of values from the bolthold that matches the passed in query
// result must be a pointer to a slice.
// The result of the query will be appended to the passed in result slice, rather than the passed in slice being
// emptied.
func (s *Store) Find(result interface{}, query *Query) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxFind(tx, result, query)
	})
}

// TxFind allows you to pass in your own bolt transaction to retrieve a set of values from the bolthold
func (s *Store) TxFind(tx *bolt.Tx, result interface{}, query *Query) error {
	return s.findQuery(tx, result, query)
}

// FindInBucket allows you to specify a parent bucke to search in
func (s *Store) FindInBucket(parent *bolt.Bucket, result interface{}, query *Query) error {
	return s.findQuery(parent, result, query)
}

// FindOne returns a single record, and so result is NOT a slice, but an pointer to a struct, if no record is found
// that matches the query, then it returns ErrNotFound
func (s *Store) FindOne(result interface{}, query *Query) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxFindOne(tx, result, query)
	})
}

// TxFindOne allows you to pass in your own bolt transaction to retrieve a single record from the bolthold
func (s *Store) TxFindOne(tx *bolt.Tx, result interface{}, query *Query) error {
	return s.findOneQuery(tx, result, query)
}

// FindOneInBucket allows you to pass in your own bucket to retrieve a single record from the bolthold
func (s *Store) FindOneInBucket(parent *bolt.Bucket, result interface{}, query *Query) error {
	return s.findOneQuery(parent, result, query)
}

// Count returns the current record count for the passed in datatype
func (s *Store) Count(dataType interface{}, query *Query) (int, error) {
	count := 0
	err := s.Bolt().View(func(tx *bolt.Tx) error {
		var txErr error
		count, txErr = s.TxCount(tx, dataType, query)
		return txErr
	})
	return count, err
}

// TxCount returns the current record count from within the given transaction for the passed in datatype
func (s *Store) TxCount(tx *bolt.Tx, dataType interface{}, query *Query) (int, error) {
	return s.countQuery(tx, dataType, query)
}

// CountInBucket returns the current record count from within the given parent bucket
func (s *Store) CountInBucket(parent *bolt.Bucket, dataType interface{}, query *Query) (int, error) {
	return s.countQuery(parent, dataType, query)
}

// ForEach runs the function fn against every record that matches the query
// Useful for when working with large sets of data that you don't want to hold the entire result
// set in memory, similar to database cursors
// Return an error from fn, will stop the cursor from iterating
func (s *Store) ForEach(query *Query, fn interface{}) error {
	return s.Bolt().View(func(tx *bolt.Tx) error {
		return s.TxForEach(tx, query, fn)
	})
}

// TxForEach is the same as ForEach but you get to specify your transaction
func (s *Store) TxForEach(tx *bolt.Tx, query *Query, fn interface{}) error {
	return s.forEach(tx, query, fn)
}

// ForEachInBucket is the same as ForEach but you get to specify your parent bucket
func (s *Store) ForEachInBucket(parent *bolt.Bucket, query *Query, fn interface{}) error {
	return s.forEach(parent, query, fn)
}
<|endoftext|>


<commit_before>package gtm

import (
	"log"
	"net"
)

type conn struct {
	netcon            net.Conn
	IncommingMessages chan *[]byte
	InfoChan          chan int
	outgoingMessages  chan *[]byte
	MessagesSent      int
	MessagesReceived  int
	ReaderListening   bool
	WriterListening   bool
}
<commit_msg>conn constructor and fatallog functions<commit_after>package gtm

import (
	"log"
	"net"
)

type conn struct {
	netcon            net.Conn
	IncommingMessages chan *[]byte
	InfoChan          chan int
	internalComsChan  chan int
	outgoingMessages  chan *[]byte
	MessagesSent      int
	MessagesReceived  int
	ReaderListening   bool
	WriterListening   bool
}

func NewConnection(con net.Conn) conn {
	return conn{
		netcon:            con,
		IncommingMessages: make(chan *[]byte, 100),
		InfoChan:          make(chan int, 5),
		internalComsChan:  make(chan int, 5),
		outgoingMessages:  make(chan *[]byte, 100),
		MessagesSent:      0,
		MessagesReceived:  0,
		ReaderListening:   false,
		WriterListening:   false,
	}
}

func fatalLog(v ...interface{}) {
	// later this will use a custom first class
	// logging function specified by the user
	log.Fatalln(v)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"github.com/gorilla/mux"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"github.com/unrolled/render"
	"golang.org/x/crypto/bcrypt"
	"net/mail"
)

var cfg Config

func sendError(rw http.ResponseWriter, status int, message string) {
	envelope := map[string]interface{}{
		"errors": [1]interface{}{
			map[string]interface{}{
				"status": fmt.Sprintf("%d",status),
				"title": message,
				"detail": message,
			},
		},
	}
	render.New().JSON(rw, status, envelope)
}

func sendData(rw http.ResponseWriter, status int, data interface{}) {
	envelope := map[string]interface{}{
		"data": data,
	}
	render.New().JSON(rw, status, envelope)
}

func main() {
	err := LoadConfigInto(&cfg, "config.gcfg")
	if err != nil {
		fmt.Println(err)
		// TODO: die
	}
	// fmt.Println("loaded config", cfg)

	// set up database connection
	db, err := sql.Open("mysql", cfg.Database.User + ":" + cfg.Database.Password + "@/" + cfg.Database.Database)
	if err != nil {
	    panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
	}
	defer db.Close()

	err = db.Ping()
	if err != nil {
	    panic(err.Error()) // proper error handling instead of panic in your app
	}

    // Heroku uses env var to specify port
    port := os.Getenv("PORT")
	if port == "" {
		port = cfg.Server.Port
	}

	// any requests on the regular HTTP port get automatically redirected to the secure home page
	// don't just redirect HTTP to HTTPS, because that doesn't train the user to not use HTTP on the first request
	// TODO: commented out so I can run Apache on 80 on my computer
	// TODO: this didn't work, maybe I have too many web servers running, also Chrome gets pretty confused with port numbers
	// go http.ListenAndServe(cfg.Server.Host + ":80", http.HandlerFunc(func (w http.ResponseWriter, req *http.Request) {
	// 	// TODO: secure app shouldn't have to be at the root of the domain
	// 	http.Redirect(w, req, "https://" + cfg.Server.Host + ":" + port, http.StatusMovedPermanently)
	// }))

    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)

    api := r.PathPrefix("/api").Subrouter()

    api.HandleFunc("/user", func (rw http.ResponseWriter, r *http.Request) {
    	r.ParseForm()

    	handle := r.PostFormValue("handle")
    	if len(handle) == 0 {
			fmt.Println("Missing handle.")
	    	sendError(rw, http.StatusBadRequest, "Missing handle.")
			return
    	}

    	email, err := mail.ParseAddress(r.PostFormValue("email"))
    	if err != nil {
			fmt.Println(err)
	    	sendError(rw, http.StatusBadRequest, "Invalid email address.")
			return
    	}

	    // TODO: check password strength
    	password := r.PostFormValue("password")
    	if len(password) == 0 {
			fmt.Println("Missing password.")
	    	sendError(rw, http.StatusBadRequest, "Missing password.")
			return
    	}

    	// look up handle to see if this user already exists
	    var u User
	    err = u.Fetch(db, handle)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    if u.UserId > 0 {
			fmt.Println("Handle already in use.")
	    	sendError(rw, http.StatusConflict, "That handle is already in use.")
			return
	    }

	    hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    u.UserId = -1
	    u.Handle = handle
	    u.Email = email.Address
	    u.Status = ""
	    u.Biography = ""
	    u.PasswordHash = string(hash)
		fmt.Println(u)

	    err = u.Save(db)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

		// go ahead and log user in
		var t UserToken
		t.UserId = u.UserId

		err = t.Save(db)
		if err != nil {
			fmt.Println(err)
			// something went wrong, but at least we created the user, so don't die here
		}

		resp := map[string]interface{}{
			"user": u,
			"token": t.Token,
		}

		sendData(rw, http.StatusCreated, resp)
	}).Methods("POST")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("GET")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: update user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("PUT")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: delete user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("DELETE")

	http.ListenAndServeTLS(cfg.Server.Host + ":" + port, cfg.Server.Certificate, cfg.Server.Key, r)
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	rend := render.New()
	rend.HTML(rw, http.StatusOK, "login", nil)
}
<commit_msg>delete login tokens<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"github.com/gorilla/mux"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"github.com/unrolled/render"
	"golang.org/x/crypto/bcrypt"
	"net/mail"
)

var cfg Config

func sendError(rw http.ResponseWriter, status int, message string) {
	envelope := map[string]interface{}{
		"errors": [1]interface{}{
			map[string]interface{}{
				"status": fmt.Sprintf("%d",status),
				"title": message,
				"detail": message,
			},
		},
	}
	render.New().JSON(rw, status, envelope)
}

func sendData(rw http.ResponseWriter, status int, data interface{}) {
	envelope := map[string]interface{}{
		"data": data,
	}
	render.New().JSON(rw, status, envelope)
}

func main() {
	err := LoadConfigInto(&cfg, "config.gcfg")
	if err != nil {
		fmt.Println(err)
		// TODO: die
	}
	// fmt.Println("loaded config", cfg)

	// set up database connection
	db, err := sql.Open("mysql", cfg.Database.User + ":" + cfg.Database.Password + "@/" + cfg.Database.Database)
	if err != nil {
	    panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
	}
	defer db.Close()

	err = db.Ping()
	if err != nil {
	    panic(err.Error()) // proper error handling instead of panic in your app
	}

    // Heroku uses env var to specify port
    port := os.Getenv("PORT")
	if port == "" {
		port = cfg.Server.Port
	}

	// any requests on the regular HTTP port get automatically redirected to the secure home page
	// don't just redirect HTTP to HTTPS, because that doesn't train the user to not use HTTP on the first request
	// TODO: commented out so I can run Apache on 80 on my computer
	// TODO: this didn't work, maybe I have too many web servers running, also Chrome gets pretty confused with port numbers
	// go http.ListenAndServe(cfg.Server.Host + ":80", http.HandlerFunc(func (w http.ResponseWriter, req *http.Request) {
	// 	// TODO: secure app shouldn't have to be at the root of the domain
	// 	http.Redirect(w, req, "https://" + cfg.Server.Host + ":" + port, http.StatusMovedPermanently)
	// }))

    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)

    api := r.PathPrefix("/api").Subrouter()

    api.HandleFunc("/user", func (rw http.ResponseWriter, r *http.Request) {
    	r.ParseForm()

    	handle := r.PostFormValue("handle")
    	if len(handle) == 0 {
			fmt.Println("Missing handle.")
	    	sendError(rw, http.StatusBadRequest, "Missing handle.")
			return
    	}

    	email, err := mail.ParseAddress(r.PostFormValue("email"))
    	if err != nil {
			fmt.Println(err)
	    	sendError(rw, http.StatusBadRequest, "Invalid email address.")
			return
    	}

	    // TODO: check password strength
    	password := r.PostFormValue("password")
    	if len(password) == 0 {
			fmt.Println("Missing password.")
	    	sendError(rw, http.StatusBadRequest, "Missing password.")
			return
    	}

    	// look up handle to see if this user already exists
	    var u User
	    err = u.Fetch(db, handle)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    if u.UserId > 0 {
			fmt.Println("Handle already in use.")
	    	sendError(rw, http.StatusConflict, "That handle is already in use.")
			return
	    }

	    hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    u.UserId = -1
	    u.Handle = handle
	    u.Email = email.Address
	    u.Status = ""
	    u.Biography = ""
	    u.PasswordHash = string(hash)
		fmt.Println(u)

	    err = u.Save(db)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

		// go ahead and log user in
		var t UserToken
		t.UserId = u.UserId

		err = t.Save(db)
		if err != nil {
			fmt.Println(err)
			// something went wrong, but at least we created the user, so don't die here
		}

		resp := map[string]interface{}{
			"user": u,
			"token": t.Token,
		}

		sendData(rw, http.StatusCreated, resp)
	}).Methods("POST")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("GET")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: update user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("PUT")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: delete user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("DELETE")

    api.HandleFunc("/token/{token}", func (rw http.ResponseWriter, r *http.Request) {
		var t UserToken
	    t.Token = mux.Vars(r)["token"]

	    err := t.Delete(db)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}
		// TODO: is it silly to send "No Content" along with an evelope?
		sendData(rw, http.StatusNoContent, "")
	}).Methods("DELETE")

	http.ListenAndServeTLS(cfg.Server.Host + ":" + port, cfg.Server.Certificate, cfg.Server.Key, r)
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	rend := render.New()
	rend.HTML(rw, http.StatusOK, "login", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"os"

	statsd "gopkg.in/alexcesaro/statsd.v2"

	"github.com/gorilla/handlers"
	_ "github.com/lib/pq"
	"github.com/mark-adams/inc/backends"
	"github.com/pressly/chi"
)

var app *chi.Mux
var metrics MetricCollector

func getRandomID() (string, error) {
	newID := make([]byte, 16)
	_, err := rand.Read(newID)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(newID), nil
}

// NewToken creates a new counting token
func NewToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.create_token")

	id, err := getRandomID()
	if err != nil {
		log.Printf("Error: %s", err)
		http.Error(w, "😞 Something bad happened... try again?", http.StatusInternalServerError)
		return
	}
	db, err := backends.GetBackend()
	if err != nil {
		log.Printf("Database error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	err = db.CreateToken(id)
	if err != nil {
		log.Printf("Insert error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, id)
}

// IncrementToken increments an existing token
func IncrementToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetToken(chi.URLParam(r, "token"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

// IncrementTokenNamespace increments a specific namespace inside a specific token's context
func IncrementTokenNamespace(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_namespace_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetNamespacedToken(chi.URLParam(r, "token"), chi.URLParam(r, "namespae"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

func init() {
	var err error

	app = chi.NewRouter()
	metrics = &NullMetricsCollector{}
	metrics, err = statsd.New(statsd.Address(os.Getenv("STATSD_HOST")))

	if err != nil && os.Getenv("STATSD_HOST") != "" {
		log.Printf("error initializing metrics: %s", err)
	}

	app.Get("/_healthcheck", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "OK")
	})

	app.Post("/new", NewToken)
	app.Route("/:token", func(r chi.Router) {
		r.Put("/", IncrementToken)
		r.Put("/:namespace", IncrementTokenNamespace)
	})
}

func main() {
	db, err := backends.GetBackend()
	if err != nil {
		log.Fatal(err)
	}

	// Create the database schema if needed
	err = db.CreateSchema()
	if err != nil {
		log.Fatal(err)
	}
	db.Close()

	handler := handlers.CombinedLoggingHandler(os.Stdout, app)
	http.ListenAndServe(":8080", handler)
}
<commit_msg>Fix a typo in a URL parameter so namespaces work properly<commit_after>package main

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"os"

	statsd "gopkg.in/alexcesaro/statsd.v2"

	"github.com/gorilla/handlers"
	_ "github.com/lib/pq"
	"github.com/mark-adams/inc/backends"
	"github.com/pressly/chi"
)

var app *chi.Mux
var metrics MetricCollector

func getRandomID() (string, error) {
	newID := make([]byte, 16)
	_, err := rand.Read(newID)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(newID), nil
}

// NewToken creates a new counting token
func NewToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.create_token")

	id, err := getRandomID()
	if err != nil {
		log.Printf("Error: %s", err)
		http.Error(w, "😞 Something bad happened... try again?", http.StatusInternalServerError)
		return
	}
	db, err := backends.GetBackend()
	if err != nil {
		log.Printf("Database error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	err = db.CreateToken(id)
	if err != nil {
		log.Printf("Insert error: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusCreated)
	fmt.Fprintf(w, id)
}

// IncrementToken increments an existing token
func IncrementToken(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetToken(chi.URLParam(r, "token"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

// IncrementTokenNamespace increments a specific namespace inside a specific token's context
func IncrementTokenNamespace(w http.ResponseWriter, r *http.Request) {
	metrics.Increment("inc.api.increment_namespace_token")
	db, err := backends.GetBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()

	count, err := db.IncrementAndGetNamespacedToken(chi.URLParam(r, "token"), chi.URLParam(r, "namespace"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "%d", count)
}

func init() {
	var err error

	app = chi.NewRouter()
	metrics = &NullMetricsCollector{}
	metrics, err = statsd.New(statsd.Address(os.Getenv("STATSD_HOST")))

	if err != nil && os.Getenv("STATSD_HOST") != "" {
		log.Printf("error initializing metrics: %s", err)
	}

	app.Get("/_healthcheck", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "OK")
	})

	app.Post("/new", NewToken)
	app.Route("/:token", func(r chi.Router) {
		r.Put("/", IncrementToken)
		r.Put("/:namespace", IncrementTokenNamespace)
	})
}

func main() {
	db, err := backends.GetBackend()
	if err != nil {
		log.Fatal(err)
	}

	// Create the database schema if needed
	err = db.CreateSchema()
	if err != nil {
		log.Fatal(err)
	}
	db.Close()

	handler := handlers.CombinedLoggingHandler(os.Stdout, app)
	http.ListenAndServe(":8080", handler)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log/syslog"
	"os"
	//"os/signal"
	//"syscall"
)

var versionFlag = flag.Bool("version", false, "print golbd version and exit")

func main() {
	flag.Parse()

	if versionFlag {
		fmt.Printf("This is a proof of concept golbd version %s \n", "0.000")
		os.Exit(0)
	}

	log, e := syslog.New(syslog.LOG_NOTICE, "lbd")
	if e == nil {
		log.Info("Starting lbd")
	}
}
<commit_msg>trying to finish the goroutines orderly<commit_after>package main

import (
	"flag"
	"fmt"
	"log/syslog"
	"os"
	//"os/signal"
	"sync"
	//"syscall"
	"time"
)

var versionFlag = flag.Bool("version", false, "print golbd version and exit")

func logInfo(log *syslog.Writer, s string) error {
	err := log.Info(s)
	fmt.Println(s)
	return err
}

func main() {
	flag.Parse()

	if *versionFlag {
		fmt.Printf("This is a proof of concept golbd version %s \n", "0.000")
		os.Exit(0)
	}

	log, e := syslog.New(syslog.LOG_NOTICE, "lbd")
	if e == nil {
		logInfo(log, "Starting lbd")
	}

	var wg sync.WaitGroup
	done := make(chan struct{})
	wq := make(chan interface{})
	workerCount := 20
	//installSignalHandler(finish, done, &wg, log)

	for i := 0; i < workerCount; i++ {
		wg.Add(1)
		go doit(i, wq, done, &wg)
	}

	for i := 0; i < workerCount; i++ {
		wq <- i
	}

	finish(done, &wg, log)
}

func doit(workerId int, wq <-chan interface{}, done <-chan struct{}, wg *sync.WaitGroup) {
	fmt.Printf("[%v] is running\n", workerId)
	defer wg.Done()
	for {
		time.Sleep(3 * time.Second)
		select {
		case m := <-wq:
			fmt.Printf("[%v] m => %v\n", workerId, m)
		case <-done:
			fmt.Printf("[%v] is done\n", workerId)
			return
		}
	}
}

//type finishFunc func(chan struct{}, *sync.WaitGroup, *syslog.Writer)

func finish(done chan struct{}, wg *sync.WaitGroup, log *syslog.Writer) {
	close(done)
	wg.Wait()
	logInfo(log, "all done!")
	return
}

//func installSignalHandler(f finishFunc, done chan struct{}, wg *sync.WaitGroup, log *syslog.Writer) {
//	c := make(chan os.Signal, 1)
//	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
//
//	// Block until a signal is received.
//	go func() {
//		sig := <-c
//		mess := fmt.Sprintf("Exiting given signal: %v", sig)
//		logInfo(log, mess)
//		logInfo(log, "before exit")
//		f(done, wg, log)
//		logInfo(log, "about to exit")
//		os.Exit(0)
//	}()
//}
<|endoftext|>


<commit_before>package fiftyonedegrees

/*
#cgo CFLAGS: -I . -Wimplicit-function-declaration
#cgo darwin LDFLAGS: -lm
#cgo linux LDFLAGS: -lm -lrt
#include "51Degrees.h"
*/
import "C"
import (
	"errors"
	"fmt"
	"unsafe"
)

type FiftyoneDegrees struct {
	dataSet *C.fiftyoneDegreesDataSet
}

func NewFiftyoneDegrees(fileName, properties string) (*FiftyoneDegrees, error) {
	item := &FiftyoneDegrees{dataSet: new(C.fiftyoneDegreesDataSet)}
	status := C.fiftyoneDegreesInitWithPropertyString(C.CString(fileName), item.dataSet, C.CString(properties))
	if status != 0 {
		return nil, errors.New(fmt.Sprintln("InitWithPropertyString Error,Status:", status))
	}
	return item, nil
}
func (this *FiftyoneDegrees) Close() {
	C.fiftyoneDegreesDestroy(this.dataSet)
}

func (this *FiftyoneDegrees) Parse(userAgent string) string {
	ws := C.fiftyoneDegreesCreateWorkset(this.dataSet)
	defer C.fiftyoneDegreesFreeWorkset(ws)
	C.fiftyoneDegreesMatch(ws, C.CString(userAgent))
	resultLength := 50000
	buff := make([]byte, resultLength)
	length := int32(C.fiftyoneDegreesProcessDeviceJSON(ws, (*C.char)(unsafe.Pointer(&buff[0]))))
	result := buff[:length]
	return string(result)
}
<commit_msg>breaking out in to provider and dataset code paths AND explicitly freeing CStrings<commit_after>package fiftyonedegrees

/*
#cgo CFLAGS: -I . -Wimplicit-function-declaration
#cgo darwin LDFLAGS: -lm
#cgo linux LDFLAGS: -lm -lrt
#include "51Degrees.h"
#include <stdlib.h>
*/
import "C"
import (
	"errors"
	"fmt"
	"unsafe"
)

type FiftyoneDegreesProvider struct {
	provider *C.fiftyoneDegreesProvider
}

type FiftyoneDegreesDataSet struct {
	dataSet *C.fiftyoneDegreesDataSet
}

func NewFiftyoneDegreesProvider(fileName string, properties string, poolSize int, cacheSize int) (*FiftyoneDegreesProvider, error) {
	item := &FiftyoneDegreesProvider{
		provider: new(C.fiftyoneDegreesProvider),
	}

	if poolSize == 0 {
		poolSize = 10
	}
	if cacheSize == 0 {
		cacheSize = 1000
	}

	var cFileName *C.char = C.CString(fileName)
	defer C.free(unsafe.Pointer(cFileName))

	var cProperties *C.char = C.CString(properties)
	defer C.free(unsafe.Pointer(cProperties))

	status := C.fiftyoneDegreesInitProviderWithPropertyString(cFileName, item.provider, cProperties, C.int(poolSize), C.int(cacheSize))

	// e_fiftyoneDegrees_DataSetInitStatus.DATA_SET_INIT_STATUS_SUCCESS == 0
	if status != 0 {
		return nil, errors.New(fmt.Sprintln("InitWithPropertyString Error,Status:", status))
	}
	return item, nil
}
func (fdp *FiftyoneDegreesProvider) Close() {
	C.fiftyoneDegreesProviderFree(fdp.provider)
}

func (fdp *FiftyoneDegreesProvider) Parse(userAgent string) string {
	ws := C.fiftyoneDegreesProviderWorksetGet(fdp.provider)
	defer C.fiftyoneDegreesWorksetRelease(ws)

	// needs to be done outside of the inline function call so CGo explicitly frees the memory
	var cUserAgent *C.char = C.CString(userAgent)
	defer C.free(unsafe.Pointer(cUserAgent))

	C.fiftyoneDegreesMatch(ws, cUserAgent)
	resultLength := 50000
	buff := make([]byte, resultLength)
	length := int32(C.fiftyoneDegreesProcessDeviceJSON(ws, (*C.char)(unsafe.Pointer(&buff[0]))))
	result := buff[:length]
	return string(result)
}

func NewFiftyoneDegreesDataSet(fileName, properties string) (*FiftyoneDegreesDataSet, error) {
	var cFileName *C.char = C.CString(fileName)
	defer C.free(unsafe.Pointer(cFileName))

	var cProperties *C.char = C.CString(properties)
	defer C.free(unsafe.Pointer(cProperties))

	fdds := &FiftyoneDegreesDataSet{dataSet: new(C.fiftyoneDegreesDataSet)}
	status := C.fiftyoneDegreesInitWithPropertyString(cFileName, fdds.dataSet, cProperties)
	if status != 0 {
		return nil, errors.New(fmt.Sprintln("InitWithPropertyString Error,Status:", status))
	}
	return fdds, nil
}
func (fdds *FiftyoneDegreesDataSet) Close() {
	C.fiftyoneDegreesDestroy(fdds.dataSet)
}

func (fdds *FiftyoneDegreesDataSet) Parse(userAgent string) string {
	ws := C.fiftyoneDegreesCreateWorkset(fdds.dataSet)
	defer C.fiftyoneDegreesFreeWorkset(ws)

	var cUserAgent *C.char = C.CString(userAgent)
	defer C.free(unsafe.Pointer(cUserAgent))

	C.fiftyoneDegreesMatch(ws, cUserAgent)
	resultLength := 50000
	buff := make([]byte, resultLength)
	length := int32(C.fiftyoneDegreesProcessDeviceJSON(ws, (*C.char)(unsafe.Pointer(&buff[0]))))
	result := buff[:length]
	return string(result)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package main

import (
	"time"

	"github.com/ebarkie/davis-station/internal/events"
	"github.com/ebarkie/weatherlink"
)

// Loop is a weatherlink.Loop with a sequence and timestamp
// added in.
type Loop struct {
	Seq       int64     `json:"sequence"`
	Timestamp time.Time `json:"timestamp"`
	weatherlink.Loop
}

func stationEvents(sc serverCtx, device string) (ec chan interface{}, err error) {
	// If a device name of "/dev/null" is specified launch
	// a primitive test server instead of attaching to the
	// Weatherlink.
	switch device {
	case "/dev/null":
		Info.Print("Test poller started")

		ec = make(chan interface{})

		// Send a mostly empty loop packet, except for a few
		// things initialized so it passes QC,  every 2s.
		l := weatherlink.Loop{}
		l.Bar.Altimeter = 6.8 // QC minimums
		l.Bar.SeaLevel = 25.0
		l.Bar.Station = 6.8

		go func() {
			for {
				ec <- l
				time.Sleep(2 * time.Second)
			}
		}()
	default:
		Info.Print("Weatherlink poller started")

		// Connect the weatherlink loggers
		weatherlink.Trace.SetOutput(Trace)
		weatherlink.Debug.SetOutput(Debug)
		weatherlink.Info.SetOutput(Info)
		weatherlink.Warn.SetOutput(Warn)
		weatherlink.Error.SetOutput(Error)

		// Open connection and start command broker
		var wl weatherlink.Weatherlink
		wl, err = weatherlink.Dial(device)
		if err != nil {
			return
		}
		defer wl.Close()
		wl.LastDmpTime = sc.ad.Last()
		ec <- weatherlink.CmdGetDmps
		ec = wl.Start()
	}

	return
}

func stationServer(sc serverCtx, device string) error {
	// Open and setup events channel for weather station
	ec, err := stationEvents(sc, device)
	if err != nil {
		Error.Fatalf("Weatherlink command broker failed to start: %s", err.Error())
		return err
	}

	// Receive events forever
	var seq int64
	for e := range ec {
		switch e.(type) {
		case weatherlink.Archive:
			a := e.(weatherlink.Archive)

			// Add record to archive database
			err := sc.ad.Add(a)
			if err != nil {
				Error.Printf("Unable to add archive record to database: %s", err.Error())
			}

			// Update events broker
			sc.eb.Publish(events.Event{Event: "archive", Data: a})
		case weatherlink.Loop:
			// Create Loop with sequence and timestamp
			l := Loop{}
			l.Timestamp = time.Now()
			l.Seq = seq
			l.Loop = e.(weatherlink.Loop)

			// Quality control validity check
			qc := validityCheck(l)
			if !qc.passed {
				// Log and ignore bad packets
				Error.Printf("QC %s", qc.errs)
				continue
			}

			// Update loop buffer
			sc.lb.Add(l)

			// Publish to events broker
			sc.eb.Publish(events.Event{Event: "loop", Data: l})

			// Increment loop sequence - this intentionally only occurs
			// if it passed QC.
			seq++
		default:
			Warn.Printf("Unhandled event type: %T", e)
		}
	}

	Error.Fatal("Weatherlink command broker unexpectedly exited")
	return nil
}
<commit_msg>Fix ordering to prevent a deadlock<commit_after>// Copyright (c) 2016-2017 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package main

import (
	"time"

	"github.com/ebarkie/davis-station/internal/events"
	"github.com/ebarkie/weatherlink"
)

// Loop is a weatherlink.Loop with a sequence and timestamp
// added in.
type Loop struct {
	Seq       int64     `json:"sequence"`
	Timestamp time.Time `json:"timestamp"`
	weatherlink.Loop
}

func stationEvents(sc serverCtx, device string) (ec chan interface{}, err error) {
	// If a device name of "/dev/null" is specified launch
	// a primitive test server instead of attaching to the
	// Weatherlink.
	switch device {
	case "/dev/null":
		Info.Print("Test poller started")

		ec = make(chan interface{})

		// Send a mostly empty loop packet, except for a few
		// things initialized so it passes QC,  every 2s.
		l := weatherlink.Loop{}
		l.Bar.Altimeter = 6.8 // QC minimums
		l.Bar.SeaLevel = 25.0
		l.Bar.Station = 6.8

		go func() {
			for {
				ec <- l
				time.Sleep(2 * time.Second)
			}
		}()
	default:
		Info.Print("Weatherlink poller started")

		// Connect the weatherlink loggers
		weatherlink.Trace.SetOutput(Trace)
		weatherlink.Debug.SetOutput(Debug)
		weatherlink.Info.SetOutput(Info)
		weatherlink.Warn.SetOutput(Warn)
		weatherlink.Error.SetOutput(Error)

		// Open connection and start command broker
		var wl weatherlink.Weatherlink
		wl, err = weatherlink.Dial(device)
		if err != nil {
			return
		}
		defer wl.Close()
		wl.LastDmpTime = sc.ad.Last()
		ec = wl.Start()
		ec <- weatherlink.CmdGetDmps
	}

	return
}

func stationServer(sc serverCtx, device string) error {
	// Open and setup events channel for weather station
	ec, err := stationEvents(sc, device)
	if err != nil {
		Error.Fatalf("Weatherlink command broker failed to start: %s", err.Error())
		return err
	}

	// Receive events forever
	var seq int64
	for e := range ec {
		switch e.(type) {
		case weatherlink.Archive:
			a := e.(weatherlink.Archive)

			// Add record to archive database
			err := sc.ad.Add(a)
			if err != nil {
				Error.Printf("Unable to add archive record to database: %s", err.Error())
			}

			// Update events broker
			sc.eb.Publish(events.Event{Event: "archive", Data: a})
		case weatherlink.Loop:
			// Create Loop with sequence and timestamp
			l := Loop{}
			l.Timestamp = time.Now()
			l.Seq = seq
			l.Loop = e.(weatherlink.Loop)

			// Quality control validity check
			qc := validityCheck(l)
			if !qc.passed {
				// Log and ignore bad packets
				Error.Printf("QC %s", qc.errs)
				continue
			}

			// Update loop buffer
			sc.lb.Add(l)

			// Publish to events broker
			sc.eb.Publish(events.Event{Event: "loop", Data: l})

			// Increment loop sequence - this intentionally only occurs
			// if it passed QC.
			seq++
		default:
			Warn.Printf("Unhandled event type: %T", e)
		}
	}

	Error.Fatal("Weatherlink command broker unexpectedly exited")
	return nil
}
<|endoftext|>