<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
	"text/tabwriter"
	"io"
	"io/ioutil"
	"net/http"
	"log"
	"encoding/xml"
	"strings"
	"strconv"
	"net/url"
	"reflect"
	"time"
	"flag"
)

type Stock struct {
	XMLName xml.Name `xml:"query"`
	Data []Data `xml:"results>row"`
}

type Data struct {
	Symbol string `xml:"symbol"`
	Open float32 `xml:"open"`
	High float32 `xml:"high"`
	Low float32 `xml:"low"`
	Date string `xml:"lastTradeDate"`
	Time string `xml:"lastTradeTime"`
	Last float32 `xml:"lastTradePrice"`
	Change float32 `xml:"change"`
	Pct string `xml:"changePct"`
}

var intervalFlag time.Duration

func init() {
	flag.DurationVar(&intervalFlag, "interval", 3*time.Second, "interval to refresh list")
	flag.DurationVar(&intervalFlag, "i", 3*time.Second, "interval to refresh list")
}

func main() {

	flag.Parse()

	duration := intervalFlag

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	data, err := ioutil.ReadFile(usr.HomeDir + "/stocks.txt")

	if err != nil {
		log.Fatal(err)
	}

	loadData(data)

	for _ = range time.Tick(duration) {
		loadData(data)
	}
}

func loadData(data []byte) {

	symbols := strings.Join(strings.Split(string(data), "\n"), ",")

	format := make([]string, 9)
	format[0] = "s" 	// Symbol
	format[1] = "d1" 	// Last Trade Date
	format[2] = "t1"	// Last Trade Time
	format[3] = "l1"	// Last Trade (Price Only)
	format[4] = "c6"	// Change (Realtime)
	format[5] = "p2"	// Change Percent (Realtime)
	format[6] = "o"		// Open
	format[7] = "h"		// Day's High
	format[8] = "g"		// Day's Low

	v := url.Values{}

	v.Set("q", "select * from csv where url='http://download.finance.yahoo.com/d/quotes.csv?f=" + strings.Join(format, "") + "&s=" + symbols + "&e=.csv' and columns='symbol,lastTradeDate,lastTradeTime,lastTradePrice,change,changePct,open,high,low'")
	v.Add("format", "xml")
	v.Add("env", "store://datatables.org/alltableswithkeys")

	resp, err := http.Get("https://query.yahooapis.com/v1/public/yql?" + v.Encode())

	if err != nil {
		log.Fatal(err)
	}

	defer resp.Body.Close() // Defer the closing of the request

	stocks := decodeXml(resp.Body)

	formatOutput(stocks)
}

func decodeXml(body io.ReadCloser) Stock {

	XMLdata := xml.NewDecoder(body)

	var s Stock

	err := XMLdata.Decode(&s)
	if err != nil {
		log.Fatal(err)
	}

	return s
}

func formatOutput (s Stock) {
	w := new(tabwriter.Writer)

	w.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Print("\033[2J\033[H")

	fmt.Fprintln(w, "\033[K" + time.Now().Round(time.Second).String())

	var d Data
	v := reflect.ValueOf(d) // reflect lets us iterate on the struct

	var value, separator, header string

	for i := 0; i < v.NumField(); i++ {
		value = v.Type().Field(i).Name
		if (i < (v.NumField() - 1)) {
			separator = "\t"
		} else {
			separator = ""
		}

		// Print the header labels underlined
		header += fmt.Sprintf("\033[4m%s\033[0m%s", value, separator)
	}

	fmt.Fprintln(w, header)

	// run the stock through String()
	for _, stock := range s.Data {
		fmt.Fprintln(w, stock)
	}

	w.Flush()
}

func (d Data) String() string {
	color := "0"

	// If the change is positive make it green, else red
	if d.Change > 0 {
		color = "32"
	} else {
		color = "31"
	}

	v := reflect.ValueOf(d)

	var fs, s, ansi, value string

	// We're starting at 1 to skip the XML field name
	for i := 0; i < v.NumField(); i++ {
		value = v.Field(i).String()
		switch v.Type().Field(i).Name {

			case "Change":
				ansi = color
				flt := v.Field(i).Float()
				value = strconv.FormatFloat(flt, 'f', 2, 32)

				if d.Change > 0 {
					value = "+" + value
				}

			case "Pct":
				ansi = color

			case "Symbol":
				ansi = "1"

			case "Open", "High", "Low", "Last":
				ansi = "0"
				value = strconv.FormatFloat(v.Field(i).Float(), 'f', 2, 32)

			default:
				ansi = "0"
		}

		fs = "\033[%sm%s\033[0m\t"
		s += fmt.Sprintf(fs, ansi, value)
	}

	return s
}
<commit_msg>do some stupid things<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"text/tabwriter"
	"io"
	"io/ioutil"
	"net/http"
	"log"
	"encoding/xml"
	"strings"
	"strconv"
	"net/url"
	"reflect"
	"time"
	"flag"
)

type Stock struct {
	XMLName xml.Name `xml:"query"`
	Data []Data `xml:"results>row"`
}

var stockContainer Stock

type Data struct {
	Symbol string `xml:"symbol"`
	Open string `xml:"open"`
	High string `xml:"high"`
	Low string `xml:"low"`
	Date string `xml:"lastTradeDate"`
	Time string `xml:"lastTradeTime"`
	Last string `xml:"lastTradePrice"`
	Change string `xml:"change"`
	Pct string `xml:"changePct"`
}

var intervalFlag time.Duration

func init() {
	flag.DurationVar(&intervalFlag, "interval", 3*time.Second, "interval to refresh list")
	flag.DurationVar(&intervalFlag, "i", 3*time.Second, "interval to refresh list")
}

func main() {

	flag.Parse()

	duration := intervalFlag

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	data, err := ioutil.ReadFile(usr.HomeDir + "/stocks.txt")

	if err != nil {
		log.Fatal(err)
	}

	loadData(data)

	for _ = range time.Tick(duration) {
		loadData(data)
	}
}

func loadData(data []byte) {

	symbols := strings.Join(strings.Split(string(data), "\n"), ",")

	format := make([]string, 9)
	format[0] = "s" 	// Symbol
	format[1] = "d1" 	// Last Trade Date
	format[2] = "t1"	// Last Trade Time
	format[3] = "l1"	// Last Trade (Price Only)
	format[4] = "c6"	// Change (Realtime)
	format[5] = "p2"	// Change Percent (Realtime)
	format[6] = "o"		// Open
	format[7] = "h"		// Day's High
	format[8] = "g"		// Day's Low

	v := url.Values{}

	v.Set("q", "select * from csv where url='http://download.finance.yahoo.com/d/quotes.csv?f=" + strings.Join(format, "") + "&s=" + symbols + "&e=.csv' and columns='symbol,lastTradeDate,lastTradeTime,lastTradePrice,change,changePct,open,high,low'")
	v.Add("format", "xml")
	v.Add("env", "store://datatables.org/alltableswithkeys")

	resp, err := http.Get("https://query.yahooapis.com/v1/public/yql?" + v.Encode())

	if err != nil {
		log.Fatal(err)
	}

	defer resp.Body.Close() // Defer the closing of the request

	stocks := decodeXml(resp.Body)

	formatOutput(stocks)
}

func decodeXml(body io.ReadCloser) Stock {

	XMLdata := xml.NewDecoder(body)

	// reset value of stockContainer
	stockContainer = Stock{}

	err := XMLdata.Decode(&stockContainer)
	if err != nil {
		log.Fatal(err)
	}

	return stockContainer
}

func formatOutput (s Stock) {
	w := new(tabwriter.Writer)

	w.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Print("\033[2J\033[H")

	fmt.Fprintln(w, time.Now().Round(time.Second).String())

	var d Data
	v := reflect.ValueOf(d) // reflect lets us iterate on the struct

	var value, separator, header string

	for i := 0; i < v.NumField(); i++ {
		value = v.Type().Field(i).Name
		if (i < (v.NumField() - 1)) {
			separator = "\t"
		} else {
			separator = ""
		}

		// Print the header labels underlined
		header += fmt.Sprintf("\033[4m%s\033[0m%s", value, separator)
	}

	fmt.Fprintln(w, header)

	// run the stock through String()
	for _, stock := range s.Data {
		fmt.Fprintln(w, stock)
	}

	w.Flush()
}

func (d Data) String() string {
	color := "0"

	// If the change is positive make it green, else red
	change, err := strconv.ParseFloat(d.Change, 32)
	if err != nil || change > 0 {
		color = "32"
	} else {
		color = "31"
	}

	v := reflect.ValueOf(d)

	var fs, s, ansi, value string

	// We're starting at 1 to skip the XML field name
	for i := 0; i < v.NumField(); i++ {
		value = v.Field(i).String()
		switch v.Type().Field(i).Name {

			case "Change":
				ansi = color
				val, err := strconv.ParseFloat(value, 32)
				if err != nil {
					value = "N/A"
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 32)
				}

				if change > 0 {
					value = "+" + value
				}

			case "Pct":
				ansi = color

			case "Symbol":
				ansi = "1"
				maxLen := maxSymbolLength(stockContainer)
				if len(value) < maxLen {
					diff := maxLen - len(value)
					value = value + strings.Repeat(" ", diff)

				}

			case "Open", "High", "Low", "Last":
				ansi = "0"
				val, err := strconv.ParseFloat(value, 32)
				if err != nil {
					value = "N/A"
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 32)
				}

			default:
				ansi = "0"
		}

		fs = "\033[%sm%s\033[0m\t"
		s += fmt.Sprintf(fs, ansi, value)
	}

	return s
}

func maxSymbolLength(s Stock) int {
	size := 0
	for _, stock := range s.Data {
		if len(stock.Symbol) > size {
			size = len(stock.Symbol)
		}
	}
	return size
}
<|endoftext|>


<commit_before>package main

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

	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/gateway"
	"github.com/42wim/matterbridge/gateway/bridgemap"
	"github.com/google/gops/agent"
	prefixed "github.com/matterbridge/logrus-prefixed-formatter"
	"github.com/sirupsen/logrus"
)

var (
	version = "1.17.5"
	githash string

	flagConfig  = flag.String("conf", "matterbridge.toml", "config file")
	flagDebug   = flag.Bool("debug", false, "enable debug")
	flagVersion = flag.Bool("version", false, "show version")
	flagGops    = flag.Bool("gops", false, "enable gops agent")
)

func main() {
	flag.Parse()
	if *flagVersion {
		fmt.Printf("version: %s %s\n", version, githash)
		return
	}

	rootLogger := setupLogger()
	logger := rootLogger.WithFields(logrus.Fields{"prefix": "main"})

	if *flagGops {
		if err := agent.Listen(agent.Options{}); err != nil {
			logger.Errorf("Failed to start gops agent: %#v", err)
		} else {
			defer agent.Close()
		}
	}

	logger.Printf("Running version %s %s", version, githash)
	if strings.Contains(version, "-dev") {
		logger.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
	}

	cfg := config.NewConfig(rootLogger, *flagConfig)
	cfg.BridgeValues().General.Debug = *flagDebug

	r, err := gateway.NewRouter(rootLogger, cfg, bridgemap.FullMap)
	if err != nil {
		logger.Fatalf("Starting gateway failed: %s", err)
	}
	if err = r.Start(); err != nil {
		logger.Fatalf("Starting gateway failed: %s", err)
	}
	logger.Printf("Gateway(s) started succesfully. Now relaying messages")
	select {}
}

func setupLogger() *logrus.Logger {
	logger := &logrus.Logger{
		Out: os.Stdout,
		Formatter: &prefixed.TextFormatter{
			PrefixPadding: 13,
			DisableColors: true,
		},
		Level: logrus.InfoLevel,
	}
	if *flagDebug || os.Getenv("DEBUG") == "1" {
		logger.SetReportCaller(true)
		logger.Formatter = &prefixed.TextFormatter{
			PrefixPadding: 13,
			DisableColors: true,
			FullTimestamp: false,

			CallerFormatter: func(function, file string) string {
				return fmt.Sprintf(" [%s:%s]", function, file)
			},
			CallerPrettyfier: func(f *runtime.Frame) (string, string) {
				sp := strings.SplitAfter(f.File, "/matterbridge/")
				filename := f.File
				if len(sp) > 1 {
					filename = sp[1]
				}
				s := strings.Split(f.Function, ".")
				funcName := s[len(s)-1]
				return funcName, fmt.Sprintf("%s:%d", filename, f.Line)
			},
		}

		logger.Level = logrus.DebugLevel
		logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.")
	}
	return logger
}
<commit_msg>Bump version<commit_after>package main

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

	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/gateway"
	"github.com/42wim/matterbridge/gateway/bridgemap"
	"github.com/google/gops/agent"
	prefixed "github.com/matterbridge/logrus-prefixed-formatter"
	"github.com/sirupsen/logrus"
)

var (
	version = "1.17.6-dev"
	githash string

	flagConfig  = flag.String("conf", "matterbridge.toml", "config file")
	flagDebug   = flag.Bool("debug", false, "enable debug")
	flagVersion = flag.Bool("version", false, "show version")
	flagGops    = flag.Bool("gops", false, "enable gops agent")
)

func main() {
	flag.Parse()
	if *flagVersion {
		fmt.Printf("version: %s %s\n", version, githash)
		return
	}

	rootLogger := setupLogger()
	logger := rootLogger.WithFields(logrus.Fields{"prefix": "main"})

	if *flagGops {
		if err := agent.Listen(agent.Options{}); err != nil {
			logger.Errorf("Failed to start gops agent: %#v", err)
		} else {
			defer agent.Close()
		}
	}

	logger.Printf("Running version %s %s", version, githash)
	if strings.Contains(version, "-dev") {
		logger.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
	}

	cfg := config.NewConfig(rootLogger, *flagConfig)
	cfg.BridgeValues().General.Debug = *flagDebug

	r, err := gateway.NewRouter(rootLogger, cfg, bridgemap.FullMap)
	if err != nil {
		logger.Fatalf("Starting gateway failed: %s", err)
	}
	if err = r.Start(); err != nil {
		logger.Fatalf("Starting gateway failed: %s", err)
	}
	logger.Printf("Gateway(s) started succesfully. Now relaying messages")
	select {}
}

func setupLogger() *logrus.Logger {
	logger := &logrus.Logger{
		Out: os.Stdout,
		Formatter: &prefixed.TextFormatter{
			PrefixPadding: 13,
			DisableColors: true,
		},
		Level: logrus.InfoLevel,
	}
	if *flagDebug || os.Getenv("DEBUG") == "1" {
		logger.SetReportCaller(true)
		logger.Formatter = &prefixed.TextFormatter{
			PrefixPadding: 13,
			DisableColors: true,
			FullTimestamp: false,

			CallerFormatter: func(function, file string) string {
				return fmt.Sprintf(" [%s:%s]", function, file)
			},
			CallerPrettyfier: func(f *runtime.Frame) (string, string) {
				sp := strings.SplitAfter(f.File, "/matterbridge/")
				filename := f.File
				if len(sp) > 1 {
					filename = sp[1]
				}
				s := strings.Split(f.Function, ".")
				funcName := s[len(s)-1]
				return funcName, fmt.Sprintf("%s:%d", filename, f.Line)
			},
		}

		logger.Level = logrus.DebugLevel
		logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.")
	}
	return logger
}
<|endoftext|>


<commit_before>// Copyright © 2016 Nagy Károly Gábriel <karasz@jpi.io>
// This file, part of genet, is free and unencumbered software
// released into the public domain.
// For more information, please refer to <http://unlicense.org/>
//

package genetlib

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type cpustats struct {
	User       int64
	Nice       int64
	System     int64
	Idle       int64
	Iowait     int64
	Irq        int64
	Softirq    int64
	Steal      int64
	Guest      int64
	Guest_nice int64
}

type procstats struct {
	Cpu           cpustats
	Intr          string
	Ctxt          int64
	Btime         time.Time
	Processes     int64
	Procs_running int64
	Procs_blocked int64
	Softirq       string
}

type diskstats struct {
	Major             int64
	Minor             int64
	Name              string
	Reads_ok          int64
	Reads_m           int64
	Sectors_read      int64
	Time_reading      time.Duration
	Writes_ok         int64
	Writes_m          int64
	Sectors_written   int64
	Time_writting     time.Duration
	IOs_current       int64
	Time_IOs          time.Duration
	Weighted_time_IOs time.Duration
}

type mntent struct {
	fsname  string
	dir     string
	mnttype string
	opts    string
	freq    int64
	passno  int64
}

type strucdf struct {
	Name  string
	Total int64
	Free  int64
}

func (p procstats) String() string {
	return fmt.Sprintf("Cpu: %v Ctxt: %v Btime: %v Processes: %v Procs_running: %v Procs_blocked: %v SoftIRQs: %v", p.Cpu, p.Ctxt, p.Btime, p.Processes, p.Procs_running, p.Procs_blocked, p.Softirq)

}

func (d diskstats) String() string {
	return fmt.Sprintf("Name: %v Major: %v Minor: %v reads completed successfully: %v reads merged: %v sectors read: %v time spent reading: %v writes completed: %v writes merged: %v sectors written: %v time spent writing: %v I/Os currently in progress: %v time spent doing I/Os: %v weighted time spent doing I/Os: %v", d.Name, d.Major, d.Minor, d.Reads_ok, d.Reads_m, d.Sectors_read, d.Time_reading, d.Writes_ok, d.Writes_m, d.Sectors_written, d.Time_writting, d.IOs_current, d.Time_IOs, d.Weighted_time_IOs)
}
func stringtoint64(s string) int64 {
	result, _ := strconv.ParseInt(s, 10, 64)
	return result

}
func GetUptime() (time.Duration, error) {
	lines, err := ReadLines("/proc/uptime")
	if err != nil {
		return -1 * time.Second, err
	}
	uptime := strings.Fields(lines[0])[0]
	return time.ParseDuration(uptime + "s")
}

func GetStatsSys() (procstats, error) {
	var result procstats

	lines, err := ReadLines("/proc/stat")
	if err != nil {
		return result, err
	}
	for _, line := range lines {
		switch strings.Fields(line)[0] {
		case "cpu":
			result.Cpu = fillcpustats(line)
		case "intr":
			result.Intr = line[5:]
		case "ctxt":
			result.Ctxt = stringtoint64(line[5:])
		case "btime":
			result.Btime = time.Unix(stringtoint64(line[6:]), 0)
		case "processes":
			result.Processes = stringtoint64(line[10:])
		case "procs_running":
			result.Procs_running = stringtoint64(line[14:])
		case "procs_blocked":
			result.Procs_blocked = stringtoint64(line[14:])
		case "softirq":
			result.Softirq = line[8:]
		default:
			continue
		}
	}
	return result, err

}

func fillcpustats(str string) cpustats {
	var result cpustats
	result.User = stringtoint64(strings.Fields(str)[1])
	result.Nice = stringtoint64(strings.Fields(str)[2])
	result.System = stringtoint64(strings.Fields(str)[3])
	result.Idle = stringtoint64(strings.Fields(str)[4])
	result.Iowait = stringtoint64(strings.Fields(str)[5])
	result.Irq = stringtoint64(strings.Fields(str)[6])
	result.Softirq = stringtoint64(strings.Fields(str)[7])
	result.Steal = stringtoint64(strings.Fields(str)[8])
	result.Guest = stringtoint64(strings.Fields(str)[9])
	result.Guest_nice = stringtoint64(strings.Fields(str)[10])

	return result
}

func GetStatsNet() (string, error) {
	return "Not implemented", nil
}
func GetStatsAll() (string, error) {
	return "Not implemented", nil
}
func GetStatsDisk() ([]diskstats, error) {
	var result []diskstats

	lines, err := ReadLines("/proc/diskstats")
	if err != nil {
		return result, err
	}

	for _, line := range lines {
		// filter out loop devices and ram
		if !strings.HasPrefix(strings.Fields(line)[2], "ram") && !strings.HasPrefix(strings.Fields(line)[2], "loop") {
			var res diskstats
			res.Major = stringtoint64(strings.Fields(line)[0])
			res.Minor = stringtoint64(strings.Fields(line)[1])
			res.Name = strings.Fields(line)[2]
			res.Reads_ok = stringtoint64(strings.Fields(line)[3])
			res.Reads_m = stringtoint64(strings.Fields(line)[4])
			res.Sectors_read = stringtoint64(strings.Fields(line)[5])
			res.Time_reading = time.Duration(stringtoint64(strings.Fields(line)[6]) * 1000000)
			res.Writes_ok = stringtoint64(strings.Fields(line)[7])
			res.Writes_m = stringtoint64(strings.Fields(line)[8])
			res.Sectors_written = stringtoint64(strings.Fields(line)[9])
			res.Time_writting = time.Duration(stringtoint64(strings.Fields(line)[10]) * 1000000)
			res.IOs_current = stringtoint64(strings.Fields(line)[11])
			res.Time_IOs = time.Duration(stringtoint64(strings.Fields(line)[12]) * 1000000)
			res.Weighted_time_IOs = time.Duration(stringtoint64(strings.Fields(line)[13]) * 100000)
			result = append(result, res)

		}
	}

	return result, nil
}

func GetMountSpace(path string) (int64, int64, error) {

	struc := syscall.Statfs_t{}

	err := syscall.Statfs(path, &struc)
	if err != nil {
		return 0, 0, err
	}

	return int64(struc.Bsize) * int64(struc.Blocks), int64(struc.Bsize) * int64(struc.Bfree), nil
}

func parseMounts() ([]mntent, error) {
	result := []mntent{}

	lines, err := ReadLines("/proc/mounts")
	if err != nil {
		return result, err
	}
	for _, line := range lines {
		var lmnt mntent
		lmnt.fsname = strings.Fields(line)[0]
		lmnt.dir = strings.Fields(line)[1]
		lmnt.mnttype = strings.Fields(line)[2]
		lmnt.opts = strings.Fields(line)[3]
		lmnt.freq = stringtoint64(strings.Fields(line)[4])
		lmnt.passno = stringtoint64(strings.Fields(line)[5])
		result = append(result, lmnt)
	}
	return result, nil
}

func GetDF() []strucdf {
	var result []strucdf
	mounts, _ := parseMounts()
	for _, mount := range mounts {
		var st strucdf
		st.Name = mount.dir
		st.Total, st.Free, _ = GetMountSpace(mount.dir)
		//filter out devices with 0 blocks
		if st.Total != 0 {
			result = append(result, st)
		}
	}
	return result
}
<commit_msg>getetlib: stats cosmetic whitespace changes<commit_after>// Copyright © 2016 Nagy Károly Gábriel <karasz@jpi.io>
// This file, part of genet, is free and unencumbered software
// released into the public domain.
// For more information, please refer to <http://unlicense.org/>
//

package genetlib

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type cpustats struct {
	User       int64
	Nice       int64
	System     int64
	Idle       int64
	Iowait     int64
	Irq        int64
	Softirq    int64
	Steal      int64
	Guest      int64
	Guest_nice int64
}

type procstats struct {
	Cpu           cpustats
	Intr          string
	Ctxt          int64
	Btime         time.Time
	Processes     int64
	Procs_running int64
	Procs_blocked int64
	Softirq       string
}

type diskstats struct {
	Major             int64
	Minor             int64
	Name              string
	Reads_ok          int64
	Reads_m           int64
	Sectors_read      int64
	Time_reading      time.Duration
	Writes_ok         int64
	Writes_m          int64
	Sectors_written   int64
	Time_writting     time.Duration
	IOs_current       int64
	Time_IOs          time.Duration
	Weighted_time_IOs time.Duration
}

type mntent struct {
	fsname  string
	dir     string
	mnttype string
	opts    string
	freq    int64
	passno  int64
}

type strucdf struct {
	Name  string
	Total int64
	Free  int64
}

func (p procstats) String() string {
	return fmt.Sprintf("Cpu: %v Ctxt: %v Btime: %v Processes: %v Procs_running: %v Procs_blocked: %v SoftIRQs: %v", p.Cpu, p.Ctxt, p.Btime, p.Processes, p.Procs_running, p.Procs_blocked, p.Softirq)

}

func (d diskstats) String() string {
	return fmt.Sprintf("Name: %v Major: %v Minor: %v reads completed successfully: %v reads merged: %v sectors read: %v time spent reading: %v writes completed: %v writes merged: %v sectors written: %v time spent writing: %v I/Os currently in progress: %v time spent doing I/Os: %v weighted time spent doing I/Os: %v", d.Name, d.Major, d.Minor, d.Reads_ok, d.Reads_m, d.Sectors_read, d.Time_reading, d.Writes_ok, d.Writes_m, d.Sectors_written, d.Time_writting, d.IOs_current, d.Time_IOs, d.Weighted_time_IOs)
}

func stringtoint64(s string) int64 {
	result, _ := strconv.ParseInt(s, 10, 64)
	return result

}
func GetUptime() (time.Duration, error) {
	lines, err := ReadLines("/proc/uptime")
	if err != nil {
		return -1 * time.Second, err
	}
	uptime := strings.Fields(lines[0])[0]
	return time.ParseDuration(uptime + "s")
}

func GetStatsSys() (procstats, error) {
	var result procstats

	lines, err := ReadLines("/proc/stat")
	if err != nil {
		return result, err
	}
	for _, line := range lines {
		switch strings.Fields(line)[0] {
		case "cpu":
			result.Cpu = fillcpustats(line)
		case "intr":
			result.Intr = line[5:]
		case "ctxt":
			result.Ctxt = stringtoint64(line[5:])
		case "btime":
			result.Btime = time.Unix(stringtoint64(line[6:]), 0)
		case "processes":
			result.Processes = stringtoint64(line[10:])
		case "procs_running":
			result.Procs_running = stringtoint64(line[14:])
		case "procs_blocked":
			result.Procs_blocked = stringtoint64(line[14:])
		case "softirq":
			result.Softirq = line[8:]
		default:
			continue
		}
	}
	return result, err

}

func fillcpustats(str string) cpustats {
	var result cpustats
	result.User = stringtoint64(strings.Fields(str)[1])
	result.Nice = stringtoint64(strings.Fields(str)[2])
	result.System = stringtoint64(strings.Fields(str)[3])
	result.Idle = stringtoint64(strings.Fields(str)[4])
	result.Iowait = stringtoint64(strings.Fields(str)[5])
	result.Irq = stringtoint64(strings.Fields(str)[6])
	result.Softirq = stringtoint64(strings.Fields(str)[7])
	result.Steal = stringtoint64(strings.Fields(str)[8])
	result.Guest = stringtoint64(strings.Fields(str)[9])
	result.Guest_nice = stringtoint64(strings.Fields(str)[10])

	return result
}

func GetStatsNet() (string, error) {
	return "Not implemented", nil
}
func GetStatsAll() (string, error) {
	return "Not implemented", nil
}
func GetStatsDisk() ([]diskstats, error) {
	var result []diskstats

	lines, err := ReadLines("/proc/diskstats")
	if err != nil {
		return result, err
	}

	for _, line := range lines {
		// filter out loop devices and ram
		if !strings.HasPrefix(strings.Fields(line)[2], "ram") && !strings.HasPrefix(strings.Fields(line)[2], "loop") {
			var res diskstats
			res.Major = stringtoint64(strings.Fields(line)[0])
			res.Minor = stringtoint64(strings.Fields(line)[1])
			res.Name = strings.Fields(line)[2]
			res.Reads_ok = stringtoint64(strings.Fields(line)[3])
			res.Reads_m = stringtoint64(strings.Fields(line)[4])
			res.Sectors_read = stringtoint64(strings.Fields(line)[5])
			res.Time_reading = time.Duration(stringtoint64(strings.Fields(line)[6]) * 1000000)
			res.Writes_ok = stringtoint64(strings.Fields(line)[7])
			res.Writes_m = stringtoint64(strings.Fields(line)[8])
			res.Sectors_written = stringtoint64(strings.Fields(line)[9])
			res.Time_writting = time.Duration(stringtoint64(strings.Fields(line)[10]) * 1000000)
			res.IOs_current = stringtoint64(strings.Fields(line)[11])
			res.Time_IOs = time.Duration(stringtoint64(strings.Fields(line)[12]) * 1000000)
			res.Weighted_time_IOs = time.Duration(stringtoint64(strings.Fields(line)[13]) * 100000)
			result = append(result, res)

		}
	}

	return result, nil
}

func GetMountSpace(path string) (int64, int64, error) {

	struc := syscall.Statfs_t{}

	err := syscall.Statfs(path, &struc)
	if err != nil {
		return 0, 0, err
	}

	return int64(struc.Bsize) * int64(struc.Blocks), int64(struc.Bsize) * int64(struc.Bfree), nil
}

func parseMounts() ([]mntent, error) {
	result := []mntent{}

	lines, err := ReadLines("/proc/mounts")
	if err != nil {
		return result, err
	}
	for _, line := range lines {
		var lmnt mntent
		lmnt.fsname = strings.Fields(line)[0]
		lmnt.dir = strings.Fields(line)[1]
		lmnt.mnttype = strings.Fields(line)[2]
		lmnt.opts = strings.Fields(line)[3]
		lmnt.freq = stringtoint64(strings.Fields(line)[4])
		lmnt.passno = stringtoint64(strings.Fields(line)[5])
		result = append(result, lmnt)
	}
	return result, nil
}

func GetDF() []strucdf {
	var result []strucdf
	mounts, _ := parseMounts()
	for _, mount := range mounts {
		var st strucdf
		st.Name = mount.dir
		st.Total, st.Free, _ = GetMountSpace(mount.dir)
		//filter out devices with 0 blocks
		if st.Total != 0 {
			result = append(result, st)
		}
	}
	return result
}
<|endoftext|>


<commit_before>package h5

import (
	"fmt"
)

// The type of a html5 nodes attributes
type Attribute struct {
	Name  string
	Value string
	// TODO for gob this should be public field
	quote rune
}

// Serialize an html5 attribute to a string
func (a *Attribute) String() string {
	// TODO handle different quoting styles.
	c := '"'
	if a.quote != 0 {
		c = a.quote
	}
	return fmt.Sprintf(
		"%s=%c%s%c", a.Name, c, a.Value, c)
}

// Clone an html5 attribute
func (a *Attribute) Clone() *Attribute {
	return &Attribute{Name: a.Name, Value: a.Value}
}

// Represents the type of an html5 node
type NodeType int

const (
	TextNode    NodeType = iota // zero value so the default
	ElementNode NodeType = iota
	DoctypeNode NodeType = iota
	CommentNode NodeType = iota
)

// The type of an html5 node
type Node struct {
	Type NodeType // The type of node this is.
	// TODO for gob this should be a public field
	data       []rune
	Attr       []*Attribute // The attributes of the html5 node
	Parent     *Node        // The parent of the html5 node if it has one, nil otherwise
	Children   []*Node      // The children of the html5 node if it has any.
	Public     bool         // True if this is a PUBLIC doctype node
	System     bool         // True if this is a SYSTEM doctype node
	Identifier []rune       // The identifier if this is a doctype node
}

// Sets a Nodes data. (eg: The Tagname for ElementNodes or text for TextNodes)
func (n *Node) SetData(rs []rune) {
	n.data = rs
}

func attrString(attrs []*Attribute) string {
	if attrs == nil {
		return ""
	}
	s := ""
	for _, a := range attrs {
		s += fmt.Sprintf(" %s", a)
	}
	return s
}

func doctypeString(n *Node) string {
	keyword := ""
	identifier := string(n.Identifier)
	switch {
	case n.Public:
		keyword = "PUBLIC"
	case n.System:
		keyword = "SYSTEM"
	default:
		return "<!DOCTYPE html>"
	}
	return fmt.Sprintf("<!DOCTYPE %s=\"%s\">", keyword, identifier)
}

func commentString(n *Node) string {
	return fmt.Sprintf("<!--%s-->", n.Data())
}

// Serialize an html5 node to a string.
func (n *Node) String() string {
	switch n.Type {
	case TextNode:
		return n.Data()
	case ElementNode:
		// TODO handle the strange self close tags
		if n.Children == nil || len(n.Children) == 0 {
			name := n.Data()
			switch name {
			case "base", "bgsound", "command", "link", "meta",
				"area", "br", "embed", "img", "keygen", "wbr",
				"param", "source", "track", "hr", "input", "image":
				return fmt.Sprintf("<%s%s>", name, attrString(n.Attr))
			default:
				return fmt.Sprintf("<%s%s></%s>", name, attrString(n.Attr), name)
			}
			return "<" + n.Data() + attrString(n.Attr) + "/>"
		} else {
			s := "<" + n.Data() + attrString(n.Attr) + ">"
			for _, n := range n.Children {
				s += n.String()
			}
			s += "</" + n.Data() + ">"
			return s
		}
	case DoctypeNode:
		// TODO Doctype stringification
		s := doctypeString(n)
		for _, n := range n.Children {
			s += n.String()
		}
		return s
	case CommentNode:
		return commentString(n)
	}
	return ""
}

// Walk a Node tree with a given function.
func (n *Node) Walk(f func(*Node)) {
	f(n)
	if len(n.Children) > 0 {
		for _, c := range n.Children {
			c.Walk(f)
		}
	}
}

func cloneNode(n, p *Node) *Node {
	clone := new(Node)
	clone.data = make([]rune, len(n.data))
	clone.Attr = make([]*Attribute, len(n.Attr))
	clone.Children = make([]*Node, len(n.Children))
	clone.Parent = p
	clone.Type = n.Type
	clone.Public = n.Public
	clone.System = n.System
	clone.Identifier = n.Identifier
	copy(clone.data, n.data)
	for i, a := range n.Attr {
		clone.Attr[i] = a.Clone()
	}
	if len(n.Children) > 0 {
		for i, c := range n.Children {
			clone.Children[i] = cloneNode(c, clone)
		}
	}
	return clone
}

// Clone an html5 nodetree to get a copy.
func (n *Node) Clone() *Node {
	return cloneNode(n, n.Parent)
}

// String form of an html nodes data.
// (eg: The Tagname for ElementNodes or text for TextNodes)
func (n *Node) Data() string {
	if n.data != nil {
		return string(n.data)
	}
	return ""
}

// Construct a TextNode
func Text(str string) *Node {
	return &Node{data: []rune(str)}
}

// TODO Constructors for other html node types.

// Copyright 2011 Jeremy Wall (jeremy@marzhillstudios.com)
// Use of this source code is governed by the Artistic License 2.0.
// That License is included in the LICENSE file.
<commit_msg>Html escape the TextNodes.<commit_after>package h5

import (
	"fmt"
	"html"
)

// The type of a html5 nodes attributes
type Attribute struct {
	Name  string
	Value string
	// TODO for gob this should be public field
	quote rune
}

// Serialize an html5 attribute to a string
func (a *Attribute) String() string {
	// TODO handle different quoting styles.
	c := '"'
	if a.quote != 0 {
		c = a.quote
	}
	return fmt.Sprintf(
		"%s=%c%s%c", a.Name, c, a.Value, c)
}

// Clone an html5 attribute
func (a *Attribute) Clone() *Attribute {
	return &Attribute{Name: a.Name, Value: a.Value}
}

// Represents the type of an html5 node
type NodeType int

const (
	TextNode    NodeType = iota // zero value so the default
	ElementNode NodeType = iota
	DoctypeNode NodeType = iota
	CommentNode NodeType = iota
)

// The type of an html5 node
type Node struct {
	Type NodeType // The type of node this is.
	// TODO for gob this should be a public field
	data       []rune
	Attr       []*Attribute // The attributes of the html5 node
	Parent     *Node        // The parent of the html5 node if it has one, nil otherwise
	Children   []*Node      // The children of the html5 node if it has any.
	Public     bool         // True if this is a PUBLIC doctype node
	System     bool         // True if this is a SYSTEM doctype node
	Identifier []rune       // The identifier if this is a doctype node
}

// Sets a Nodes data. (eg: The Tagname for ElementNodes or text for TextNodes)
func (n *Node) SetData(rs []rune) {
	n.data = rs
}

func attrString(attrs []*Attribute) string {
	if attrs == nil {
		return ""
	}
	s := ""
	for _, a := range attrs {
		s += fmt.Sprintf(" %s", a)
	}
	return s
}

func doctypeString(n *Node) string {
	keyword := ""
	identifier := string(n.Identifier)
	switch {
	case n.Public:
		keyword = "PUBLIC"
	case n.System:
		keyword = "SYSTEM"
	default:
		return "<!DOCTYPE html>"
	}
	return fmt.Sprintf("<!DOCTYPE %s=\"%s\">", keyword, identifier)
}

func commentString(n *Node) string {
	return fmt.Sprintf("<!--%s-->", n.Data())
}

// Serialize an html5 node to a string.
func (n *Node) String() string {
	switch n.Type {
	case TextNode:
		return n.Data()
	case ElementNode:
		// TODO handle the strange self close tags
		if n.Children == nil || len(n.Children) == 0 {
			name := n.Data()
			switch name {
			case "base", "bgsound", "command", "link", "meta",
				"area", "br", "embed", "img", "keygen", "wbr",
				"param", "source", "track", "hr", "input", "image":
				return fmt.Sprintf("<%s%s>", name, attrString(n.Attr))
			default:
				return fmt.Sprintf("<%s%s></%s>", name, attrString(n.Attr), name)
			}
			return "<" + n.Data() + attrString(n.Attr) + "/>"
		} else {
			s := "<" + n.Data() + attrString(n.Attr) + ">"
			for _, n := range n.Children {
				s += n.String()
			}
			s += "</" + n.Data() + ">"
			return s
		}
	case DoctypeNode:
		// TODO Doctype stringification
		s := doctypeString(n)
		for _, n := range n.Children {
			s += n.String()
		}
		return s
	case CommentNode:
		return commentString(n)
	}
	return ""
}

// Walk a Node tree with a given function.
func (n *Node) Walk(f func(*Node)) {
	f(n)
	if len(n.Children) > 0 {
		for _, c := range n.Children {
			c.Walk(f)
		}
	}
}

func cloneNode(n, p *Node) *Node {
	clone := new(Node)
	clone.data = make([]rune, len(n.data))
	clone.Attr = make([]*Attribute, len(n.Attr))
	clone.Children = make([]*Node, len(n.Children))
	clone.Parent = p
	clone.Type = n.Type
	clone.Public = n.Public
	clone.System = n.System
	clone.Identifier = n.Identifier
	copy(clone.data, n.data)
	for i, a := range n.Attr {
		clone.Attr[i] = a.Clone()
	}
	if len(n.Children) > 0 {
		for i, c := range n.Children {
			clone.Children[i] = cloneNode(c, clone)
		}
	}
	return clone
}

// Clone an html5 nodetree to get a copy.
func (n *Node) Clone() *Node {
	return cloneNode(n, n.Parent)
}

// String form of an html nodes data.
// (eg: The Tagname for ElementNodes or text for TextNodes)
func (n *Node) Data() string {
	if n.data != nil {
		return string(n.data)
	}
	return ""
}

// Construct a TextNode
func Text(str string) *Node {
	return &Node{data: []rune(html.EscapeString(str))}
}

// TODO Constructors for other html node types.

// Copyright 2011 Jeremy Wall (jeremy@marzhillstudios.com)
// Use of this source code is governed by the Artistic License 2.0.
// That License is included in the LICENSE file.
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/cajun/shoehorn/config"
	"github.com/cajun/shoehorn/logger"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

var (
	available Available
	root      = "."
)

type Ports struct {
	tcp string
	udp string
}

type Network struct {
	Ip      string
	Public  Ports
	Private Ports
}

type Available struct {
	daemonized  map[string]Executor
	info        map[string]Executor
	interactive map[string]Executor
}

func Root() string {
	return root
}

func (a *Available) addDaemon(name string, exe Executor) {
	if a.daemonized == nil {
		a.daemonized = make(map[string]Executor)
	}
	a.daemonized[name] = exe
}

func (a *Available) addInfo(name string, exe Executor) {
	if a.info == nil {
		a.info = make(map[string]Executor)
	}
	a.info[name] = exe
}

func (a *Available) addInteractive(name string, exe Executor) {
	if a.interactive == nil {
		a.interactive = make(map[string]Executor)
	}
	a.interactive[name] = exe
}

func init() {
	flag.StringVar(&root, "root", ".", "which dir at the apps located")

	available.addDaemon("start", Executor{
		description: "start the given process",
		run:         Start})
	available.addDaemon("stop", Executor{
		description: "stop the given process",
		run:         Stop})
	available.addDaemon("kill", Executor{
		description: "kill the given process",
		run:         Kill})
	available.addDaemon("restart", Executor{
		description: "restrat the givne process",
		run:         Restart})

	available.addInfo("status", Executor{
		description: "view the status of the process",
		run:         Status})
	available.addInfo("logs", Executor{
		description: "see logs for the process",
		run:         Logs})
	available.addInfo("params", Executor{
		description: "view the params that will be used in the docker command",
		run:         PrintParams})
	available.addInfo("build", Executor{
		description: "build a container from a file or url",
		run:         Build})

	available.addInteractive("console", Executor{
		description: "execute the console command from the config",
		run:         Console})
	available.addInteractive("bash", Executor{
		description: "execute a bash shell for the process",
		run:         Bash})
	available.addInteractive("run", Executor{
		description: "execute raw command against the container",
		run:         Run})
	available.addInteractive("get", Executor{
		description: "clone a git repo and then build the images",
		run:         Install})
	available.addInteractive("update", Executor{
		description: "pull and then build the images",
		run:         Update})
	available.addInteractive("attach", Executor{
		description: "attaches to the first instance running",
		run:         Attach})
	//available.addInteractive("ssh", Executor{
	//description: "ssh into the container",
	//run:         Ssh})
}

// DaemonizedCommands are commands that will be daemonized or manage daemonized
// commands
func DaemonizedCommands() map[string]Executor {
	return available.daemonized
}

// InfoCommands are commands the will pull out information about the
// given process
func InfoCommands() map[string]Executor {
	return available.info
}

// InteractiveCommands will turn over some kind of command back to the user
func InteractiveCommands() map[string]Executor {
	return available.interactive
}

// Build will create the container if nessary
func Build(args ...string) {
	wd, _ := os.Getwd()
	logger.Log(fmt.Sprintf("In %s to build.", wd))
	if cfg != nil {
		logger.Log(fmt.Sprintf("Building...%s\n", cfg.App))
		cmd := exec.Command("docker", "build", "-t", cfg.Container, cfg.BuildFile)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		cmd.Run()
	} else {
		config.LoadConfigs()
		for _, process := range config.List() {
			SetProcess(process)
			SetConfig(config.Process(process))
			Build(args...)
		}
	}
}

// Start will run the standard start command
func Start(args ...string) {
	runInstances("Start", func(i int, id string) error {
		return runDaemon("run", settingsToParams(i, true)...)
	})

	if cfg.UseNginx {
		UpdateNginxConf()
	}
}

// Stop will stop all the process if this type.  If the 'Kill' setting is turned
// on then the stop will kill the process instead
func Stop(args ...string) {
	switch {
	case cfg.Kill:
		Kill(args...)
	default:
		runInstances("Stopping", func(i int, id string) error {
			defer os.Remove(pidFileName(i))
			return run("stop", id)
		})
	}
}

// Restart will call stop then start for this process
func Restart(args ...string) {
	logger.Log(fmt.Sprintf("Restarting %v\n", process))
	Stop(args...)
	Start(args...)
}

// Kill will kill the given process
func Kill(args ...string) {
	runInstances("Killing", func(i int, id string) error {
		defer os.Remove(pidFileName(i))
		return run("kill", id)
	})
}

// Console will run an interactive command for the given console command
func Console(args ...string) {
	cfg.StartCmd = "/bin/bash -c"
	cfg.QuotedOpts = "'" + cfg.Console + "'"
	runInteractive("run", settingsToParams(0, false)...)
}

func Run(args ...string) {
	cfg.StartCmd = "/bin/bash -c"
	cfg.QuotedOpts = "'" + strings.Join(args, " ") + "'"
	runInteractive("run", settingsToParams(0, false)...)
}

// Bash will execute a bash command against the given container
func Bash(args ...string) {
	cfg.StartCmd = "/bin/bash"
	runInteractive("run", settingsToParams(0, false)...)
}

func Install(args ...string) {
	go func() {
		os.Chdir(root)
		opts := []string{"clone", args[0]}

		logger.Log("Cloning " + args[0] + "\n")
		cmd := exec.Command("git", opts...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err := cmd.Run()

		if err == nil {
			parts := strings.Split(args[0], "/")
			path := parts[len(parts)-1:][0]
			path = strings.Replace(path, ".git", "", -1)
			wd, _ := os.Getwd()
			logger.Log("Building Images here: " + wd + "/" + path + "\n")
			os.Chdir(wd + "/" + path)
			Build(path)
			cfg = nil
			BundleInstall("now")
			postInstall()
		} else {
			logger.Log(err.Error())
		}
	}()
}

func Update(args ...string) {
	go func() {
		logger.Log("Updaing...")
		cmd := exec.Command("git", "pull")
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err := cmd.Run()

		if err == nil {
			Build(root)
			cfg = nil
			BundleInstall("now")
			postUpdate()
		} else {
			logger.Log(err.Error())
		}
	}()

}

func postInstall() {
	if cfg == nil {
		config.LoadConfigs()
		for _, process := range config.List() {
			SetProcess(process)
			SetConfig(config.Process(process))
			postInstall()
		}
	} else if cfg.PostInstall != "" {
		cmd := exec.Command(cfg.PostInstall)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
	}
}

func postUpdate() {
	if cfg == nil {
		config.LoadConfigs()
		for _, process := range config.List() {
			SetProcess(process)
			SetConfig(config.Process(process))
			postInstall()
		}
	} else if cfg.PostUpdate != "" {
		cmd := exec.Command(cfg.PostUpdate)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
	}
}

func ip(instance int) string {
	return networkSettings(instance).Ip
}

func ports(instance int, settings map[string]interface{}) (public, private Ports) {

	if settings["Ports"] != nil {
		s := settings["Ports"].(map[string]interface{})

		for key, value := range s {
			if strings.Contains(key, "tcp") {
				private.tcp = strings.Split(key, "/")[0]

				for _, data := range value.([]interface{}) {
					for host, host_val := range data.(map[string]interface{}) {
						if strings.Contains(host, "Port") {
							public.tcp = host_val.(string)
						}
					}
				}
			} else if strings.Contains(key, "udp") {
				private.udp = strings.Split(key, "/")[0]

				for _, data := range value.([]interface{}) {
					for host, host_val := range data.(map[string]interface{}) {
						if strings.Contains(host, "Port") {
							public.udp = host_val.(string)
						}
					}
				}

			}
		}
	}
	return
}

//func Ssh(args ...string) {
//}

func networkSettings(instance int) (net Network) {
	settings, _ := inspect(instance)
	settings = settings["NetworkSettings"].(map[string]interface{})

	net.Ip = settings["IPAddress"].(string)

	net.Public, net.Private = ports(instance, settings)
	return
}

func inspect(instance int) (u map[string]interface{}, err error) {
	id, err := pid(instance)
	out, err := exec.Command("docker", "inspect", id).Output()
	if err != nil {
		return
	}

	all := []map[string]interface{}{}
	err = json.Unmarshal(out, &all)
	if len(all) > 0 {
		u = all[0]
	}
	return
}

// Running determines if the given process is running.
func running(args ...string) (found bool) {
	found = false
	cmd := exec.Command("docker", "ps")

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		logger.Log(fmt.Sprintln(err))
	}

	_, err = cmd.StderrPipe()
	if err != nil {
		logger.Log(fmt.Sprintln(err))
	}

	err = cmd.Start()
	if err != nil {
		logger.Log(fmt.Sprintln(err))
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(stdout)
	s := buf.String()

	cmd.Wait()

	for _, id := range pids() {
		if !found {
			found = strings.Contains(s, id)
		}
	}

	return
}

// Logs will print out all of the logs for each of the instances
func Logs(args ...string) {
	runInstances("Logs", func(i int, id string) error {
		return run("logs", id)
	})
}

func Attach(args ...string) {
	runInstances("Attach", func(i int, id string) error {
		return run("attach", id)
	})
}

// Status will list out the statuses for the given process
func Status(args ...string) {
	runInstances("Status", func(i int, id string) error {
		on := running()
		logger.Log(fmt.Sprintf("Container ID: %s\n", id))
		logger.Log(fmt.Sprintf("     Running: %s\n", strconv.FormatBool(on)))

		if on {
			net := networkSettings(i)
			logger.Log(fmt.Sprintf("          IP: %s\n", net.Ip))
			logger.Log(fmt.Sprintf(" Public Port: %s\n", net.Public.tcp))
			logger.Log(fmt.Sprintf("Private Port: %s\n", net.Private.tcp))
		}

		return nil
	})
}

// run will execute a command against docker with the given
// options as a daemon. run also sets the pid. run will not
// execute if there is an existing pid
func runDaemon(command string, inOpts ...string) error {
	base := []string{"-d"}
	opts := append(base, inOpts...)

	return run(command, opts...)
}

// run will execute a command against docker with the given
// options as a daemon. run also sets the pid. run will not
// execute if there is an existing pid
func run(command string, inOpts ...string) error {
	base := []string{command}
	opts := append(base, inOpts...)
	outOpts(opts)

	cmd := exec.Command("docker", opts...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// runInteractive will give the user the option for input into the
// docker command. examples would be running bash or ssh
func runInteractive(command string, inOpts ...string) error {
	base := []string{command, "-i", "-t"}
	opts := append(base, inOpts...)
	outOpts(opts)

	cmd := exec.Command("docker", opts...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

// runner is an interface to a function that will execute
// the run command.
type runner func(instance int, pid string) error

// runInstances wraps the given function and execute
// the number of instances requested by the config file
// for the command given.
func runInstances(message string, fn runner) {
	logger.Log(fmt.Sprintf("%s %v\n", message, process))
	for i := 0; i < cfg.Instances; i++ {
		logger.Log(fmt.Sprintf("...Instance %d of %d %s\n", i, cfg.Instances, process))
		id, _ := pid(i)
		fn(i, id)
	}

}

func runExec(cmd string, args ...string) {
	joined := strings.Join(args, " ")
	cfg.StartCmd = "/bin/bash -c"
	cfg.QuotedOpts = fmt.Sprintf("'%s %s'", cmd, joined)

	runInteractive("run", settingsToParams(0, false)...)
}
<commit_msg>Updaging file<commit_after>package command

import (
  "bytes"
  "encoding/json"
  "flag"
  "fmt"
  "github.com/cajun/shoehorn/config"
  "github.com/cajun/shoehorn/logger"
  "os"
  "os/exec"
  "strconv"
  "strings"
)

var (
  available Available
  root      = "."
)

type Ports struct {
  tcp string
  udp string
}

type Network struct {
  Ip      string
  Public  Ports
  Private Ports
}

type Available struct {
  daemonized  map[string]Executor
  info        map[string]Executor
  interactive map[string]Executor
}

func Root() string {
  return root
}

func (a *Available) addDaemon(name string, exe Executor) {
  if a.daemonized == nil {
    a.daemonized = make(map[string]Executor)
  }
  a.daemonized[name] = exe
}

func (a *Available) addInfo(name string, exe Executor) {
  if a.info == nil {
    a.info = make(map[string]Executor)
  }
  a.info[name] = exe
}

func (a *Available) addInteractive(name string, exe Executor) {
  if a.interactive == nil {
    a.interactive = make(map[string]Executor)
  }
  a.interactive[name] = exe
}

func init() {
  flag.StringVar(&root, "root", ".", "which dir at the apps located")

  available.addDaemon("start", Executor{
    description: "start the given process",
    run:         Start})
  available.addDaemon("stop", Executor{
    description: "stop the given process",
    run:         Stop})
  available.addDaemon("kill", Executor{
    description: "kill the given process",
    run:         Kill})
  available.addDaemon("restart", Executor{
    description: "restrat the givne process",
    run:         Restart})

  available.addInfo("status", Executor{
    description: "view the status of the process",
    run:         Status})
  available.addInfo("logs", Executor{
    description: "see logs for the process",
    run:         Logs})
  available.addInfo("params", Executor{
    description: "view the params that will be used in the docker command",
    run:         PrintParams})
  available.addInfo("build", Executor{
    description: "build a container from a file or url",
    run:         Build})

  available.addInteractive("console", Executor{
    description: "execute the console command from the config",
    run:         Console})
  available.addInteractive("bash", Executor{
    description: "execute a bash shell for the process",
    run:         Bash})
  available.addInteractive("run", Executor{
    description: "execute raw command against the container",
    run:         Run})
  available.addInteractive("get", Executor{
    description: "clone a git repo and then build the images",
    run:         Install})
  available.addInteractive("update", Executor{
    description: "pull and then build the images",
    run:         Update})
  available.addInteractive("attach", Executor{
    description: "attaches to the first instance running",
    run:         Attach})
  //available.addInteractive("ssh", Executor{
  //description: "ssh into the container",
  //run:         Ssh})
}

// DaemonizedCommands are commands that will be daemonized or manage daemonized
// commands
func DaemonizedCommands() map[string]Executor {
  return available.daemonized
}

// InfoCommands are commands the will pull out information about the
// given process
func InfoCommands() map[string]Executor {
  return available.info
}

// InteractiveCommands will turn over some kind of command back to the user
func InteractiveCommands() map[string]Executor {
  return available.interactive
}

// Build will create the container if nessary
func Build(args ...string) {
  wd, _ := os.Getwd()
  logger.Log(fmt.Sprintf("In %s to build.", wd))
  if cfg != nil {
    logger.Log(fmt.Sprintf("Building...%s\n", cfg.App))
    cmd := exec.Command("docker", "build", "-t", cfg.Container, cfg.BuildFile)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    cmd.Run()
  } else {
    config.LoadConfigs()
    for _, process := range config.List() {
      SetProcess(process)
      SetConfig(config.Process(process))
      Build(args...)
    }
  }
}

// Start will run the standard start command
func Start(args ...string) {
  runInstances("Start", func(i int, id string) error {
    return runDaemon("run", settingsToParams(i, true)...)
  })

  if cfg.UseNginx {
    UpdateNginxConf()
  }
}

// Stop will stop all the process if this type.  If the 'Kill' setting is turned
// on then the stop will kill the process instead
func Stop(args ...string) {
  switch {
  case cfg.Kill:
    Kill(args...)
  default:
    runInstances("Stopping", func(i int, id string) error {
      defer os.Remove(pidFileName(i))
      return run("stop", id)
    })
  }
}

// Restart will call stop then start for this process
func Restart(args ...string) {
  logger.Log(fmt.Sprintf("Restarting %v\n", process))
  Stop(args...)
  Start(args...)
}

// Kill will kill the given process
func Kill(args ...string) {
  runInstances("Killing", func(i int, id string) error {
    defer os.Remove(pidFileName(i))
    return run("kill", id)
  })
}

// Console will run an interactive command for the given console command
func Console(args ...string) {
  cfg.StartCmd = "/bin/bash -c"
  cfg.QuotedOpts = "'" + cfg.Console + "'"
  runInteractive("run", settingsToParams(0, false)...)
}

func Run(args ...string) {
  cfg.StartCmd = "/bin/bash -c"
  cfg.QuotedOpts = "'" + strings.Join(args, " ") + "'"
  runInteractive("run", settingsToParams(0, false)...)
}

// Bash will execute a bash command against the given container
func Bash(args ...string) {
  cfg.StartCmd = "/bin/bash"
  runInteractive("run", settingsToParams(0, false)...)
}

func Install(args ...string) {
  go func() {
    os.Chdir(root)
    opts := []string{"clone", args[0]}

    logger.Log("Cloning " + args[0] + "\n")
    cmd := exec.Command("git", opts...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()

    if err == nil {
      parts := strings.Split(args[0], "/")
      path := parts[len(parts)-1:][0]
      path = strings.Replace(path, ".git", "", -1)
      wd, _ := os.Getwd()
      logger.Log("Building Images here: " + wd + "/" + path + "\n")
      os.Chdir(wd + "/" + path)
      Build(path)
      cfg = nil
      BundleInstall("now")
      postInstall()
    } else {
      logger.Log(err.Error())
    }
  }()
}

func Update(args ...string) {
  go func() {
    logger.Log("Updaing...")
    cmd := exec.Command("git", "pull")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()

    if err == nil {
      Build(root)
      cfg = nil
      BundleInstall("now")
      postUpdate()
    } else {
      logger.Log(err.Error())
    }
  }()

}

func postInstall() {
  if cfg == nil {
    config.LoadConfigs()
    for _, process := range config.List() {
      SetProcess(process)
      SetConfig(config.Process(process))
      postInstall()
    }
  } else if cfg.PostInstall != "" {
    cmd := exec.Command(cfg.PostInstall)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()
  }
}

func postUpdate() {
  if cfg == nil {
    config.LoadConfigs()
    for _, process := range config.List() {
      SetProcess(process)
      SetConfig(config.Process(process))
      postInstall()
    }
  } else if cfg.PostUpdate != "" {
    cmd := exec.Command(cfg.PostUpdate)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()
  }
}

func ip(instance int) string {
  return networkSettings(instance).Ip
}

func ports(instance int, settings map[string]interface{}) (public, private Ports) {

  if settings["Ports"] != nil {
    s := settings["Ports"].(map[string]interface{})

    for key, value := range s {
      if strings.Contains(key, "tcp") {
        private.tcp = strings.Split(key, "/")[0]

        for _, data := range value.([]interface{}) {
          for host, host_val := range data.(map[string]interface{}) {
            if strings.Contains(host, "Port") {
              public.tcp = host_val.(string)
            }
          }
        }
      } else if strings.Contains(key, "udp") {
        private.udp = strings.Split(key, "/")[0]

        for _, data := range value.([]interface{}) {
          for host, host_val := range data.(map[string]interface{}) {
            if strings.Contains(host, "Port") {
              public.udp = host_val.(string)
            }
          }
        }

      }
    }
  }
  return
}

//func Ssh(args ...string) {
//}

func networkSettings(instance int) (net Network) {
  settings, _ := inspect(instance)

  settings = settings["NetworkSettings"].(map[string]interface{})

  net.Ip = settings["IPAddress"].(string)

  net.Public, net.Private = ports(instance, settings)
  return
}

func inspect(instance int) (u map[string]interface{}, err error) {
  id, err := pid(instance)
  out, err := exec.Command("docker", "inspect", id).Output()
  if err != nil {
    return
  }

  all := []map[string]interface{}{}
  err = json.Unmarshal(out, &all)
  if len(all) > 0 {
    u = all[0]
  }
  return
}

// Running determines if the given process is running.
func running(args ...string) (found bool) {
  found = false
  cmd := exec.Command("docker", "ps", "-notrunc")

  stdout, err := cmd.StdoutPipe()
  if err != nil {
    logger.Log(fmt.Sprintln(err))
  }

  _, err = cmd.StderrPipe()
  if err != nil {
    logger.Log(fmt.Sprintln(err))
  }

  err = cmd.Start()
  if err != nil {
    logger.Log(fmt.Sprintln(err))
  }

  buf := new(bytes.Buffer)
  buf.ReadFrom(stdout)
  s := buf.String()

  cmd.Wait()

  for _, id := range pids() {
    if len(id) > 0 && !found {
      found = strings.Contains(s, id)
    }
  }

  return
}

// Logs will print out all of the logs for each of the instances
func Logs(args ...string) {
  runInstances("Logs", func(i int, id string) error {
    return run("logs", id)
  })
}

func Attach(args ...string) {
  runInstances("Attach", func(i int, id string) error {
    return run("attach", id)
  })
}

// Status will list out the statuses for the given process
func Status(args ...string) {
  runInstances("Status", func(i int, id string) error {
    on := running()
    logger.Log(fmt.Sprintf("Container ID: %s\n", id))
    logger.Log(fmt.Sprintf("     Running: %s\n", strconv.FormatBool(on)))

    if on {
      net := networkSettings(i)
      logger.Log(fmt.Sprintf("          IP: %s\n", net.Ip))
      logger.Log(fmt.Sprintf(" Public Port: %s\n", net.Public.tcp))
      logger.Log(fmt.Sprintf("Private Port: %s\n", net.Private.tcp))
    }

    return nil
  })
}

// run will execute a command against docker with the given
// options as a daemon. run also sets the pid. run will not
// execute if there is an existing pid
func runDaemon(command string, inOpts ...string) error {
  base := []string{"-d"}
  opts := append(base, inOpts...)

  return run(command, opts...)
}

// run will execute a command against docker with the given
// options as a daemon. run also sets the pid. run will not
// execute if there is an existing pid
func run(command string, inOpts ...string) error {
  base := []string{command}
  opts := append(base, inOpts...)
  outOpts(opts)

  cmd := exec.Command("docker", opts...)
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  return cmd.Run()
}

// runInteractive will give the user the option for input into the
// docker command. examples would be running bash or ssh
func runInteractive(command string, inOpts ...string) error {
  base := []string{command, "-i", "-t"}
  opts := append(base, inOpts...)
  outOpts(opts)

  cmd := exec.Command("docker", opts...)
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Stdin = os.Stdin
  return cmd.Run()
}

// runner is an interface to a function that will execute
// the run command.
type runner func(instance int, pid string) error

// runInstances wraps the given function and execute
// the number of instances requested by the config file
// for the command given.
func runInstances(message string, fn runner) {
  logger.Log(fmt.Sprintf("%s %v\n", message, process))
  for i := 0; i < cfg.Instances; i++ {
    logger.Log(fmt.Sprintf("...Instance %d of %d %s\n", i, cfg.Instances, process))
    id, _ := pid(i)
    fn(i, id)
  }

}

func runExec(cmd string, args ...string) {
  joined := strings.Join(args, " ")
  cfg.StartCmd = "/bin/bash -c"
  cfg.QuotedOpts = fmt.Sprintf("'%s %s'", cmd, joined)

  runInteractive("run", settingsToParams(0, false)...)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 - Max Ekman <max@looplab.se>
//
// 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 eventhorizon

import (
	"context"
)

// CommandHandler is an interface that all handlers of commands should implement.
type CommandHandler interface {
	HandleCommand(context.Context, Command) error
}
<commit_msg>Add CommandHandlerFunc<commit_after>// Copyright (c) 2014 - Max Ekman <max@looplab.se>
//
// 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 eventhorizon

import (
	"context"
)

// CommandHandler is an interface that all handlers of commands should implement.
type CommandHandler interface {
	HandleCommand(context.Context, Command) error
}

// CommandHandlerFunc is a function that can be used as a command handler.
type CommandHandlerFunc func(context.Context, Command) error

// HandleCommand implements the HandleCommand method of the CommandHandler.
func (h CommandHandlerFunc) HandleCommand(ctx context.Context, cmd Command) error {
	return h(ctx, cmd)
}
<|endoftext|>


<commit_before>package geocoder

import "testing"

const (
	city       = "Seattle"
	state      = "WA"
	postalCode = "98164"
	seattleLat = 47.603832
	seattleLng = -122.330062
	antwerpLat = 51.221110
	antwerpLng = 4.399708
	beijingLat = 39.905963
	beijingLng = 116.391248
)

func TestGeocode(t *testing.T) {
	query := "Seattle WA"
	lat, lng, err := Geocode(query)
	if err != nil {
		t.Errorf("Seattle: Expected error to be nil ~ Recieved %v", err)
	}

	if lat != seattleLat || lng != seattleLng {
		t.Errorf("Seattle: Expected (%f, %f) ~ Received (%f, %f)", seattleLat, seattleLng, lat, lng)
	}
}

func TestReverseGeoCode(t *testing.T) {
	address, err := ReverseGeocode(seattleLat, seattleLng)
	if err != nil {
		t.Errorf("Seattle (reverse): Expected error to be nil ~ Recieved %v", err)
	}

	if address != nil && address.City != city || address.State != state || address.PostalCode != postalCode {
		t.Errorf("Seattle (reverse): Expected %s %s %s ~ Received %s %s %s",
			city, state, postalCode, address.City, address.State, address.PostalCode)
	}
}

func TestBatchGeocode(t *testing.T) {
	latLngs, err := BatchGeocode([]string{"Antwerp,Belgium", "Beijing,China"})
	if err != nil {
		t.Errorf("Seattle (reverse): Expected error to be nil ~ Recieved %v", err)
	}

	if len(latLngs) != 2 {
		t.Fatalf("Batch: Expected len(batch) to be 2, got %v", latLngs)
	}
	antwerp := latLngs[0]
	if antwerp.Lat != antwerpLat || antwerp.Lng != antwerpLng {
		t.Errorf("Antwerp: Expected %f, %f ~ Received %f, %f", antwerpLat, antwerpLng, antwerp.Lat, antwerp.Lng)
	}
	beijing := latLngs[1]
	if beijing.Lat != beijingLat || beijing.Lng != beijingLng {
		t.Errorf("Beijng: Expected %f, %f ~ Received %f, %f", beijingLat, beijingLng, beijing.Lat, beijing.Lng)
	}
}

func TestGeocodeShouldFail(t *testing.T) {
	query := "Seattle WA"
	// set a bad api key
	SetAPIKey("bad api key that doesn't exist, hopefully!")
	lat, lng, err := Geocode(query)
	SetAPIKey(apiKey)

	if err == nil {
		t.Errorf("Seattle: Expected error to not be nil ~ Recieved %v", err)
	}

	if lat != 0 || lng != 0 {
		t.Errorf("Seattle: Expected (0, 0) ~ Received (%f, %f)", lat, lng)
	}
}

func TestReverseGeoCodeShouldFail(t *testing.T) {
	// set a bad api key
	SetAPIKey("bad api key that doesn't exist, hopefully!")
	address, err := ReverseGeocode(seattleLat, seattleLng)
	SetAPIKey(apiKey)

	if err == nil {
		t.Errorf("Seattle (reverse): Expected error to not be nil ~ Recieved %v", err)
	}

	if address != nil {
		t.Errorf("Seattle (reverse): Expected address to be nil ~ Received %v",
			address)
	}
}

func TestBatchGeocodeShouldFail(t *testing.T) {
	// set a bad api key
	SetAPIKey("bad api key that doesn't exist, hopefully!")
	latLngs, err := BatchGeocode([]string{"Antwerp,Belgium", "Beijing,China"})
	SetAPIKey(apiKey)

	if err == nil {
		t.Errorf("Batch: Expected error to be nil ~ Recieved %v", err)
	}

	if len(latLngs) != 0 {
		t.Errorf("Batch: Expected len(batch) to be 0, got %v", latLngs)
	}
}
<commit_msg>Spelling fix<commit_after>package geocoder

import "testing"

const (
	city       = "Seattle"
	state      = "WA"
	postalCode = "98164"
	seattleLat = 47.603832
	seattleLng = -122.330062
	antwerpLat = 51.221110
	antwerpLng = 4.399708
	beijingLat = 39.905963
	beijingLng = 116.391248
)

func TestGeocode(t *testing.T) {
	query := "Seattle WA"
	lat, lng, err := Geocode(query)
	if err != nil {
		t.Errorf("Seattle: Expected error to be nil ~ Received %v", err)
	}

	if lat != seattleLat || lng != seattleLng {
		t.Errorf("Seattle: Expected (%f, %f) ~ Received (%f, %f)", seattleLat, seattleLng, lat, lng)
	}
}

func TestReverseGeoCode(t *testing.T) {
	address, err := ReverseGeocode(seattleLat, seattleLng)
	if err != nil {
		t.Errorf("Seattle (reverse): Expected error to be nil ~ Received %v", err)
	}

	if address != nil && address.City != city || address.State != state || address.PostalCode != postalCode {
		t.Errorf("Seattle (reverse): Expected %s %s %s ~ Received %s %s %s",
			city, state, postalCode, address.City, address.State, address.PostalCode)
	}
}

func TestBatchGeocode(t *testing.T) {
	latLngs, err := BatchGeocode([]string{"Antwerp,Belgium", "Beijing,China"})
	if err != nil {
		t.Errorf("Seattle (reverse): Expected error to be nil ~ Received %v", err)
	}

	if len(latLngs) != 2 {
		t.Fatalf("Batch: Expected len(batch) to be 2, got %v", latLngs)
	}
	antwerp := latLngs[0]
	if antwerp.Lat != antwerpLat || antwerp.Lng != antwerpLng {
		t.Errorf("Antwerp: Expected %f, %f ~ Received %f, %f", antwerpLat, antwerpLng, antwerp.Lat, antwerp.Lng)
	}
	beijing := latLngs[1]
	if beijing.Lat != beijingLat || beijing.Lng != beijingLng {
		t.Errorf("Beijng: Expected %f, %f ~ Received %f, %f", beijingLat, beijingLng, beijing.Lat, beijing.Lng)
	}
}

func TestGeocodeShouldFail(t *testing.T) {
	query := "Seattle WA"
	// set a bad api key
	SetAPIKey("bad api key that doesn't exist, hopefully!")
	lat, lng, err := Geocode(query)
	SetAPIKey(apiKey)

	if err == nil {
		t.Errorf("Seattle: Expected error to not be nil ~ Received %v", err)
	}

	if lat != 0 || lng != 0 {
		t.Errorf("Seattle: Expected (0, 0) ~ Received (%f, %f)", lat, lng)
	}
}

func TestReverseGeoCodeShouldFail(t *testing.T) {
	// set a bad api key
	SetAPIKey("bad api key that doesn't exist, hopefully!")
	address, err := ReverseGeocode(seattleLat, seattleLng)
	SetAPIKey(apiKey)

	if err == nil {
		t.Errorf("Seattle (reverse): Expected error to not be nil ~ Received %v", err)
	}

	if address != nil {
		t.Errorf("Seattle (reverse): Expected address to be nil ~ Received %v",
			address)
	}
}

func TestBatchGeocodeShouldFail(t *testing.T) {
	// set a bad api key
	SetAPIKey("bad api key that doesn't exist, hopefully!")
	latLngs, err := BatchGeocode([]string{"Antwerp,Belgium", "Beijing,China"})
	SetAPIKey(apiKey)

	if err == nil {
		t.Errorf("Batch: Expected error to be nil ~ Received %v", err)
	}

	if len(latLngs) != 0 {
		t.Errorf("Batch: Expected len(batch) to be 0, got %v", latLngs)
	}
}
<|endoftext|>


<commit_before>// ui mode specific handlers

package main

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

	"fmt"
	"github.com/nsf/termbox-go"
)

type ModeHandler interface {
	HandleKey(ev termbox.Event)
	Repaint()
}

type HandlerDefault struct {
	ui *UI
}

func (h *HandlerDefault) Repaint() {
	ui := h.ui
	_, height := termbox.Size()

	first := ui.offsetY
	last := clamp(ui.offsetY+height, 0, len(ui.filterMatches))
	total := len(ui.filterMatches) - 1
	filter := ""

	if ui.filterString != "" {
		filter = fmt.Sprintf("[filter: \"%s\"] ", ui.filterString)
	}

	line := fmt.Sprintf("%s[rows %d-%d of %d] :%d, %d", filter, first, last, total, ui.offsetX, ui.offsetY)
	writeLine(0, height-1, termbox.ColorDefault, termbox.ColorDefault, line)
}

func (h *HandlerDefault) HandleKey(ev termbox.Event) {
	ui := h.ui
	vw, vh := ui.viewSize()

	maxYOffset := clamp(len(ui.filterMatches)-(vh-2), 0, len(ui.filterMatches)-1)
	endOfLine := ui.endOfLine()

	switch {
	case ev.Key == termbox.KeyCtrlL:
		termbox.Sync()
	case ev.Key == termbox.KeyCtrlA:
		ui.offsetX = 0
	case ev.Key == termbox.KeyCtrlE:
		ui.offsetX = -endOfLine + vw
	case ev.Key == termbox.KeyArrowRight:
		ui.offsetX = clamp(ui.offsetX-5, -endOfLine+vw, 0)
	case ev.Key == termbox.KeyArrowLeft:
		ui.offsetX = clamp(ui.offsetX+5, -endOfLine+vw, 0)
	case ev.Key == termbox.KeyArrowUp:
		ui.offsetY = clamp(ui.offsetY-1, 0, maxYOffset)
	case ev.Key == termbox.KeyArrowDown:
		ui.offsetY = clamp(ui.offsetY+1, 0, maxYOffset)
	case ev.Ch == '/', ev.Key == termbox.KeyCtrlR:
		ui.handler = &HandlerFilter{*h}
		ui.offsetY = 0
	case ev.Key == termbox.KeyEnter:
		jsonObj := make(map[string]interface{})

		row := ui.rows[ui.offsetY]
		for i, col := range ui.columns {
			str := row[i]

			if v, err := strconv.ParseInt(str, 10, 64); err == nil {
				jsonObj[col] = v
			} else if v, err := strconv.ParseFloat(str, 64); err == nil {
				jsonObj[col] = v
			} else if v, err := strconv.ParseBool(str); err == nil {
				jsonObj[col] = v
			} else {
				jsonObj[col] = str
			}

		}

		jsonStr, err := json.MarshalIndent(jsonObj, "", "  ")
		// TODO: This should be handled
		if err != nil {
			panic(err)
		}

		ui.handler = NewPopup(h.ui, string(jsonStr))

	case ev.Key == termbox.KeySpace:
		ui.offsetY = clamp(ui.offsetY+vh, 0, maxYOffset)
	case ev.Ch == 'C':
		ui.handler = NewColumnSelect(h.ui)
		ui.offsetX = 0
	case ev.Ch == 'G':
		ui.offsetY = maxYOffset
	case ev.Ch == 'g':
		ui.offsetY = 0
	case ev.Ch == 'Z':
		ui.zebraStripe = !ui.zebraStripe
	case ev.Ch == 'X':
		for i, _ := range ui.columnOpts {
			ui.columnOpts[i].expanded = !globalExpanded
			// FIXME: Possibly not the best behavior
			ui.columnOpts[i].collapsed = false
		}
		globalExpanded = !globalExpanded
	case ev.Ch == '?':
		ui.handler = NewPopup(h.ui, HELP_TEXT)
	}
}

type HandlerFilter struct {
	HandlerDefault
}

func (h *HandlerFilter) Repaint() {
	ui := h.ui
	_, height := termbox.Size()

	line := fmt.Sprintf("FILTER [%d matches]: %s", len(ui.filterMatches), ui.filterString)
	writeLine(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)
	termbox.SetCursor(len(line), height-1)

}

func (h *HandlerFilter) HandleKey(ev termbox.Event) {
	ui := h.ui

	// Ch == 0 implies this was a special key
	if ev.Ch == 0 && ev.Key != termbox.KeySpace {
		if ev.Key == termbox.KeyEsc || ev.Key == termbox.KeyCtrlG {
			ui.switchToDefault()

			ui.filterString = ""
			ui.filterRows(false)
		} else if ev.Key == termbox.KeyEnter {
			ui.switchToDefault()
		} else if ev.Key == termbox.KeyDelete || ev.Key == termbox.KeyBackspace ||
			ev.Key == termbox.KeyBackspace2 {
			if sz := len(ui.filterString); sz > 0 {
				ui.filterString = ui.filterString[:sz-1]
				ui.filterRows(false)
			}
		} else if ev.Key == termbox.KeyCtrlW || ev.Key == termbox.KeyCtrlU {
			ui.filterString = ""
			ui.filterRows(false)
		} else {
			// Fallback to default handling for arrows etc
			// FIXME: is this really the best way to do this in go?
			def := &HandlerDefault{h.ui}
			def.HandleKey(ev)
		}
		return
	}

	if ev.Key == termbox.KeySpace {
		ui.filterString += " "
	} else {
		ui.filterString += string(ev.Ch)
	}

	ui.offsetY = 0
	ui.filterRows(true)
}

type HandlerColumnSelect struct {
	HandlerDefault
	column int
}

func NewColumnSelect(ui *UI) *HandlerColumnSelect {
	h := HandlerColumnSelect{
		HandlerDefault: HandlerDefault{ui},
		column:         0,
	}

	h.selectColumn(0)
	return &h
}

// TODO: also adjust x offset
func (h *HandlerColumnSelect) selectColumn(idx int) {
	h.ui.columnOpts[h.column].highlight = false
	h.column = idx

	if h.column >= 0 {
		h.ui.columnOpts[h.column].highlight = true
	}
}

func (h *HandlerColumnSelect) rowSorter(i, j int) bool {
	ui := h.ui

	row1 := ui.rows[ui.filterMatches[i]]
	row2 := ui.rows[ui.filterMatches[j]]

	v1, err1 := strconv.ParseFloat(row1[h.column], 32)
	v2, err2 := strconv.ParseFloat(row2[h.column], 32)

	if err1 == nil && err2 == nil {
		return v1 < v2
	}

	return row1[h.column] < row2[h.column]
}

func (h *HandlerColumnSelect) Repaint() {
	ui := h.ui
	_, height := termbox.Size()

	line := fmt.Sprintf("COLUMN SELECT (^g quit) [%s] %d", ui.columns[h.column], h.column)
	writeLine(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)
}

func (h *HandlerColumnSelect) HandleKey(ev termbox.Event) {
	ui := h.ui
	savedColumn := -1

	switch {
	case ev.Key == termbox.KeyCtrlA:
		h.selectColumn(ui.findFirstColumn())
	case ev.Key == termbox.KeyCtrlE:
		h.selectColumn(len(ui.columns) - 1)
	case ev.Key == termbox.KeyArrowRight:
		next := ui.findNextColumn(h.column, 1)
		h.selectColumn(clamp(next, 0, len(ui.columns)-1))
	case ev.Key == termbox.KeyArrowLeft:
		next := ui.findNextColumn(h.column, -1)
		h.selectColumn(clamp(next, 0, len(ui.columns)-1))
	case ev.Ch == '<':
		sort.SliceStable(ui.filterMatches, func(i, j int) bool {
			return h.rowSorter(i, j)
		})
	case ev.Ch == '>':
		sort.SliceStable(ui.filterMatches, func(i, j int) bool {
			return h.rowSorter(j, i)
		})
	case ev.Ch == 'w':
		ui.columnOpts[h.column].collapsed = !ui.columnOpts[h.column].collapsed
	case ev.Ch == 'x':
		ui.columnOpts[h.column].expanded = !ui.columnOpts[h.column].expanded
		if ui.columnOpts[h.column].expanded {
			ui.columnOpts[h.column].collapsed = false
		}
	case ev.Ch == '.':
		ui.columnOpts[h.column].pinned = !ui.columnOpts[h.column].pinned

		if ui.columnOpts[h.column].pinned {
			ui.columnOpts[h.column].collapsed = false
		}

	case ev.Key == termbox.KeyCtrlG, ev.Key == termbox.KeyEsc:
		savedColumn = h.column
		h.selectColumn(-1)
		ui.switchToDefault()
	default:
		// FIXME: ditto, is this the best way to do this?
		def := &HandlerDefault{h.ui}
		def.HandleKey(ev)
	}

	// find if we've gone off screen and readjust
	// TODO: this bit is buggy
	cursorPosition := 0
	for i, _ := range ui.columns {
		colOpts := ui.columnOpts[i]

		if i == h.column || i == savedColumn {
			break
		}

		if colOpts.collapsed {
			cursorPosition += 1
		} else if !colOpts.expanded {
			cursorPosition += clamp(colOpts.width, 0, MAX_CELL_WIDTH)
		} else {
			cursorPosition += colOpts.width
		}

		cursorPosition += len(CELL_SEPARATOR)
	}

	width, _ := ui.viewSize()
	if cursorPosition > width-ui.offsetX || cursorPosition < -ui.offsetX {
		ui.offsetX = -cursorPosition
	}
}

type HandlerPopup struct {
	HandlerDefault

	content          []string
	offsetX, offsetY int
}

func NewPopup(ui *UI, content string) *HandlerPopup {
	return &HandlerPopup{
		HandlerDefault: HandlerDefault{ui},
		content:        strings.Split(content, "\n"),
	}
}

func (h *HandlerPopup) Repaint() {
	width, height := termbox.Size()

	popupW := clamp(120, 50, width-15)
	popupH := clamp(len(h.content)+2, 10, height-5)

	x := width/2 - popupW/2
	y := height/2 - popupH/2

	fmtString := "%s%-" + strconv.Itoa(popupW) + "s%s"

	borders := [][]string{
		[]string{"┌─", "─┐"},
		[]string{"│ ", " │"},
		[]string{"└─", "─┘"},
	}

	for i := -1; i <= popupH; i += 1 {
		var border []string
		var content string

		if i == -1 {
			border = borders[0]
			content = strings.Repeat("─", popupW)
		} else if i < popupH {
			border = borders[1]
			if i+h.offsetY < len(h.content) {
				content = h.content[i+h.offsetY]
				if len(content) >= popupW {
					content = content[0:popupW]
				}
			} else {
				content = " "
			}
		} else {
			border = borders[2]
			content = strings.Repeat("─", popupW)
		}

		line := fmt.Sprintf(fmtString, border[0], content, border[1])
		writeString(x, y+i, termbox.ColorWhite, termbox.ColorDefault, line)
	}

	line := "POPUP (^g quit)"
	writeLine(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)

}

func (h *HandlerPopup) HandleKey(ev termbox.Event) {
	if ev.Key == termbox.KeyEsc || ev.Key == termbox.KeyCtrlG || ev.Ch == 'q' {
		h.ui.handler = &HandlerDefault{h.ui}
	}

	switch ev.Key {
	case termbox.KeyArrowLeft:
		h.offsetX = clamp(h.offsetX-5, 0, 9999)
	case termbox.KeyArrowRight:
		h.offsetX = clamp(h.offsetX+5, 0, 9999)
	case termbox.KeyArrowUp:
		h.offsetY = clamp(h.offsetY-1, 0, len(h.content)-5)
	case termbox.KeyArrowDown:
		h.offsetY = clamp(h.offsetY+1, 0, len(h.content)-5)
	}
}
<commit_msg>Column stats popup<commit_after>// ui mode specific handlers

package main

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

	"fmt"
	"github.com/montanaflynn/stats"
	"github.com/nsf/termbox-go"
)

type ModeHandler interface {
	HandleKey(ev termbox.Event)
	Repaint()
}

type HandlerDefault struct {
	ui *UI
}

func (h *HandlerDefault) Repaint() {
	ui := h.ui
	_, height := termbox.Size()

	first := ui.offsetY
	last := clamp(ui.offsetY+height, 0, len(ui.filterMatches))
	total := len(ui.filterMatches) - 1
	filter := ""

	if ui.filterString != "" {
		filter = fmt.Sprintf("[filter: \"%s\"] ", ui.filterString)
	}

	line := fmt.Sprintf("%s[rows %d-%d of %d] :%d, %d", filter, first, last, total, ui.offsetX, ui.offsetY)
	writeLine(0, height-1, termbox.ColorDefault, termbox.ColorDefault, line)
}

func (h *HandlerDefault) HandleKey(ev termbox.Event) {
	ui := h.ui
	vw, vh := ui.viewSize()

	maxYOffset := clamp(len(ui.filterMatches)-(vh-2), 0, len(ui.filterMatches)-1)
	endOfLine := ui.endOfLine()

	switch {
	case ev.Key == termbox.KeyCtrlL:
		termbox.Sync()
	case ev.Key == termbox.KeyCtrlA:
		ui.offsetX = 0
	case ev.Key == termbox.KeyCtrlE:
		ui.offsetX = -endOfLine + vw
	case ev.Key == termbox.KeyArrowRight:
		ui.offsetX = clamp(ui.offsetX-5, -endOfLine+vw, 0)
	case ev.Key == termbox.KeyArrowLeft:
		ui.offsetX = clamp(ui.offsetX+5, -endOfLine+vw, 0)
	case ev.Key == termbox.KeyArrowUp:
		ui.offsetY = clamp(ui.offsetY-1, 0, maxYOffset)
	case ev.Key == termbox.KeyArrowDown:
		ui.offsetY = clamp(ui.offsetY+1, 0, maxYOffset)
	case ev.Ch == '/', ev.Key == termbox.KeyCtrlR:
		ui.handler = &HandlerFilter{*h}
		ui.offsetY = 0
	case ev.Key == termbox.KeyEnter:
		jsonObj := make(map[string]interface{})

		row := ui.rows[ui.offsetY]
		for i, col := range ui.columns {
			str := row[i]

			if v, err := strconv.ParseInt(str, 10, 64); err == nil {
				jsonObj[col] = v
			} else if v, err := strconv.ParseFloat(str, 64); err == nil {
				jsonObj[col] = v
			} else if v, err := strconv.ParseBool(str); err == nil {
				jsonObj[col] = v
			} else {
				jsonObj[col] = str
			}

		}

		jsonStr, err := json.MarshalIndent(jsonObj, "", "  ")
		// TODO: This should be handled
		if err != nil {
			panic(err)
		}

		ui.handler = NewPopup(h.ui, string(jsonStr))

	case ev.Key == termbox.KeySpace:
		ui.offsetY = clamp(ui.offsetY+vh, 0, maxYOffset)
	case ev.Ch == 'C':
		ui.handler = NewColumnSelect(h.ui)
		ui.offsetX = 0
	case ev.Ch == 'G':
		ui.offsetY = maxYOffset
	case ev.Ch == 'g':
		ui.offsetY = 0
	case ev.Ch == 'Z':
		ui.zebraStripe = !ui.zebraStripe
	case ev.Ch == 'X':
		for i, _ := range ui.columnOpts {
			ui.columnOpts[i].expanded = !globalExpanded
			// FIXME: Possibly not the best behavior
			ui.columnOpts[i].collapsed = false
		}
		globalExpanded = !globalExpanded
	case ev.Ch == '?':
		ui.handler = NewPopup(h.ui, HELP_TEXT)
	}
}

type HandlerFilter struct {
	HandlerDefault
}

func (h *HandlerFilter) Repaint() {
	ui := h.ui
	_, height := termbox.Size()

	line := fmt.Sprintf("FILTER [%d matches]: %s", len(ui.filterMatches), ui.filterString)
	writeLine(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)
	termbox.SetCursor(len(line), height-1)

}

func (h *HandlerFilter) HandleKey(ev termbox.Event) {
	ui := h.ui

	// Ch == 0 implies this was a special key
	if ev.Ch == 0 && ev.Key != termbox.KeySpace {
		if ev.Key == termbox.KeyEsc || ev.Key == termbox.KeyCtrlG {
			ui.switchToDefault()

			ui.filterString = ""
			ui.filterRows(false)
		} else if ev.Key == termbox.KeyEnter {
			ui.switchToDefault()
		} else if ev.Key == termbox.KeyDelete || ev.Key == termbox.KeyBackspace ||
			ev.Key == termbox.KeyBackspace2 {
			if sz := len(ui.filterString); sz > 0 {
				ui.filterString = ui.filterString[:sz-1]
				ui.filterRows(false)
			}
		} else if ev.Key == termbox.KeyCtrlW || ev.Key == termbox.KeyCtrlU {
			ui.filterString = ""
			ui.filterRows(false)
		} else {
			// Fallback to default handling for arrows etc
			// FIXME: is this really the best way to do this in go?
			def := &HandlerDefault{h.ui}
			def.HandleKey(ev)
		}
		return
	}

	if ev.Key == termbox.KeySpace {
		ui.filterString += " "
	} else {
		ui.filterString += string(ev.Ch)
	}

	ui.offsetY = 0
	ui.filterRows(true)
}

type HandlerColumnSelect struct {
	HandlerDefault
	column int
}

func NewColumnSelect(ui *UI) *HandlerColumnSelect {
	h := HandlerColumnSelect{
		HandlerDefault: HandlerDefault{ui},
		column:         0,
	}

	h.selectColumn(0)
	return &h
}

// TODO: also adjust x offset
func (h *HandlerColumnSelect) selectColumn(idx int) {
	h.ui.columnOpts[h.column].highlight = false
	h.column = idx

	if h.column >= 0 {
		h.ui.columnOpts[h.column].highlight = true
	}
}

func (h *HandlerColumnSelect) rowSorter(i, j int) bool {
	ui := h.ui

	row1 := ui.rows[ui.filterMatches[i]]
	row2 := ui.rows[ui.filterMatches[j]]

	v1, err1 := strconv.ParseFloat(row1[h.column], 32)
	v2, err2 := strconv.ParseFloat(row2[h.column], 32)

	if err1 == nil && err2 == nil {
		return v1 < v2
	}

	return row1[h.column] < row2[h.column]
}

func (h *HandlerColumnSelect) Repaint() {
	ui := h.ui
	_, height := termbox.Size()

	line := fmt.Sprintf("COLUMN SELECT (^g quit) [%s] %d", ui.columns[h.column], h.column)
	writeLine(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)
}

func (h *HandlerColumnSelect) HandleKey(ev termbox.Event) {
	ui := h.ui
	savedColumn := -1

	switch {
	case ev.Key == termbox.KeyCtrlA:
		h.selectColumn(ui.findFirstColumn())
	case ev.Key == termbox.KeyCtrlE:
		h.selectColumn(len(ui.columns) - 1)
	case ev.Key == termbox.KeyArrowRight:
		next := ui.findNextColumn(h.column, 1)
		h.selectColumn(clamp(next, 0, len(ui.columns)-1))
	case ev.Key == termbox.KeyArrowLeft:
		next := ui.findNextColumn(h.column, -1)
		h.selectColumn(clamp(next, 0, len(ui.columns)-1))
	case ev.Ch == '<':
		sort.SliceStable(ui.filterMatches, func(i, j int) bool {
			return h.rowSorter(i, j)
		})
	case ev.Ch == '>':
		sort.SliceStable(ui.filterMatches, func(i, j int) bool {
			return h.rowSorter(j, i)
		})
	case ev.Ch == 'w':
		ui.columnOpts[h.column].collapsed = !ui.columnOpts[h.column].collapsed
	case ev.Ch == 'x':
		ui.columnOpts[h.column].expanded = !ui.columnOpts[h.column].expanded
		if ui.columnOpts[h.column].expanded {
			ui.columnOpts[h.column].collapsed = false
		}
	case ev.Ch == '.':
		ui.columnOpts[h.column].pinned = !ui.columnOpts[h.column].pinned

		if ui.columnOpts[h.column].pinned {
			ui.columnOpts[h.column].collapsed = false
		}
	case ev.Ch == 's':
		var (
			min, max, stdev float64
			mean, median    float64
			mode            []float64
			sum, variance   float64
			p90, p95, p99   float64
			quartiles       stats.Quartiles
			err             error
			text            string
		)

		data := make(stats.Float64Data, 0, len(ui.rows)+1)
		for _, row := range ui.rows {
			trimmed := strings.TrimSpace(row[h.column])
			if val, err := strconv.ParseFloat(trimmed, 64); err == nil {
				data = append(data, val)
			}
		}

		if len(data) == 0 {
			data = []float64{0.0, 0.0, 0.0, 0.0}
		}

		// The joy of go
		if min, err = data.Min(); err != nil {
			goto error
		} else if max, err = data.Max(); err != nil {
			goto error
		} else if mean, err = data.Mean(); err != nil {
			goto error
		} else if median, err = data.Median(); err != nil {
			goto error
		} else if mode, err = data.Mode(); err != nil {
			goto error
		} else if stdev, err = data.StandardDeviation(); err != nil {
			goto error
		} else if sum, err = data.Sum(); err != nil {
			goto error
		} else if p90, err = data.Percentile(90); err != nil {
			goto error
		} else if p95, err = data.Percentile(95); err != nil {
			goto error
		} else if p99, err = data.Percentile(99); err != nil {
			goto error
		} else if variance, err = data.Variance(); err != nil {
			goto error
		} else if quartiles, err = stats.Quartile(data); err != nil {
			goto error
		}

		text = fmt.Sprintf(`
  SUMMARY STATISTICS [ %s ]
  ------------------
  rows:         %d
  numeric rows: %d

  min %15.4f  mean   %15.4f
  max %15.4f  median %15.4f
  sum %15.4f  mode   %15.4f

  var %15.4f  std    %15.4f

  p90 %15.4f  q1     %15.4f
  p95 %15.4f  q2     %15.4f
  p99 %15.4f  q3     %15.4f`,
			ui.columns[h.column], len(ui.rows), len(data),
			min, mean, max, median, sum, mode[0], variance, stdev,
			p90, quartiles.Q1, p95, quartiles.Q2, p99, quartiles.Q3)

		ui.handler = NewPopup(ui, text)

		break
		// TODO: handle rror for real
	error:
		fmt.Printf("%v\n", err)
		panic(err)
	case ev.Key == termbox.KeyCtrlG, ev.Key == termbox.KeyEsc:
		savedColumn = h.column
		h.selectColumn(-1)
		ui.switchToDefault()
	default:
		// FIXME: ditto, is this the best way to do this?
		def := &HandlerDefault{h.ui}
		def.HandleKey(ev)
	}

	// find if we've gone off screen and readjust
	// TODO: this bit is buggy
	cursorPosition := 0
	for i, _ := range ui.columns {
		colOpts := ui.columnOpts[i]

		if i == h.column || i == savedColumn {
			break
		}

		if colOpts.collapsed {
			cursorPosition += 1
		} else if !colOpts.expanded {
			cursorPosition += clamp(colOpts.width, 0, MAX_CELL_WIDTH)
		} else {
			cursorPosition += colOpts.width
		}

		cursorPosition += len(CELL_SEPARATOR)
	}

	width, _ := ui.viewSize()
	if cursorPosition > width-ui.offsetX || cursorPosition < -ui.offsetX {
		ui.offsetX = -cursorPosition
	}
}

type HandlerPopup struct {
	HandlerDefault

	content          []string
	offsetX, offsetY int
}

func NewPopup(ui *UI, content string) *HandlerPopup {
	return &HandlerPopup{
		HandlerDefault: HandlerDefault{ui},
		content:        strings.Split(content, "\n"),
	}
}

func (h *HandlerPopup) Repaint() {
	width, height := termbox.Size()

	popupW := clamp(120, 50, width-15)
	popupH := clamp(len(h.content)+2, 10, height-5)

	x := width/2 - popupW/2
	y := height/2 - popupH/2

	fmtString := "%s%-" + strconv.Itoa(popupW) + "s%s"

	borders := [][]string{
		[]string{"┌─", "─┐"},
		[]string{"│ ", " │"},
		[]string{"└─", "─┘"},
	}

	for i := -1; i <= popupH; i += 1 {
		var border []string
		var content string

		if i == -1 {
			border = borders[0]
			content = strings.Repeat("─", popupW)
		} else if i < popupH {
			border = borders[1]
			if i+h.offsetY < len(h.content) {
				content = h.content[i+h.offsetY]
				if len(content) >= popupW {
					content = content[0:popupW]
				}
			} else {
				content = " "
			}
		} else {
			border = borders[2]
			content = strings.Repeat("─", popupW)
		}

		line := fmt.Sprintf(fmtString, border[0], content, border[1])
		writeString(x, y+i, termbox.ColorWhite, termbox.ColorDefault, line)
	}

	line := "POPUP (^g quit)"
	writeLine(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)

}

func (h *HandlerPopup) HandleKey(ev termbox.Event) {
	if ev.Key == termbox.KeyEsc || ev.Key == termbox.KeyCtrlG || ev.Ch == 'q' {
		h.ui.handler = &HandlerDefault{h.ui}
	}

	switch ev.Key {
	case termbox.KeyArrowLeft:
		h.offsetX = clamp(h.offsetX-5, 0, 9999)
	case termbox.KeyArrowRight:
		h.offsetX = clamp(h.offsetX+5, 0, 9999)
	case termbox.KeyArrowUp:
		h.offsetY = clamp(h.offsetY-1, 0, len(h.content)-5)
	case termbox.KeyArrowDown:
		h.offsetY = clamp(h.offsetY+1, 0, len(h.content)-5)
	}
}
<|endoftext|>


<commit_before>package mgo

import (
	"time"

	mgo "gopkg.in/mgo.v2"

	"github.com/henrylee2cn/pholcus/common/pool"
	"github.com/henrylee2cn/pholcus/config"
	"github.com/henrylee2cn/pholcus/logs"
)

type MgoSrc struct {
	*mgo.Session
}

var (
	connGcSecond = time.Duration(config.MGO_CONN_GC_SECOND) * 1e9
	session      *mgo.Session
	err          error
	MgoPool      = pool.ClassicPool(
		config.MGO_CONN_CAP,
		config.MGO_CONN_CAP/5,
		func() (pool.Src, error) {
			if err != nil || session.Ping() != nil {
				session.Close()
				Refresh()
			}
			return &MgoSrc{session.Clone()}, err
		},
		connGcSecond)
)

func Refresh() {
	session, err = mgo.Dial(config.MGO_CONN_STR)
	if err != nil {
		logs.Log.Error("MongoDB：%v\n", err)
	} else if err = session.Ping(); err != nil {
		logs.Log.Error("MongoDB：%v\n", err)
	} else {
		session.SetPoolLimit(config.MGO_CONN_CAP)
	}
}

// 判断资源是否可用
func (self *MgoSrc) Usable() bool {
	if self.Session == nil || self.Session.Ping() != nil {
		return false
	}
	return true
}

// 使用后的重置方法
func (*MgoSrc) Reset() {}

// 被资源池删除前的自毁方法
func (self *MgoSrc) Close() {
	if self.Session == nil {
		return
	}
	self.Session.Close()
}

func Error() error {
	return err
}

// 调用资源池中的资源
func Call(fn func(pool.Src) error) error {
	return MgoPool.Call(fn)
}

// 销毁资源池
func Close() {
	MgoPool.Close()
}

// 返回当前资源数量
func Len() int {
	return MgoPool.Len()
}

// 获取所有数据
func DatabaseNames() (names []string, err error) {
	err = MgoPool.Call(func(src pool.Src) error {
		names, err = src.(*MgoSrc).DatabaseNames()
		return err
	})
	return
}

// 获取数据库集合列表
func CollectionNames(dbname string) (names []string, err error) {
	MgoPool.Call(func(src pool.Src) error {
		names, err = src.(*MgoSrc).DB(dbname).CollectionNames()
		return err
	})
	return
}
<commit_msg>nil session will cause panic, Fix it<commit_after>package mgo

import (
	"time"

	mgo "gopkg.in/mgo.v2"

	"github.com/henrylee2cn/pholcus/common/pool"
	"github.com/henrylee2cn/pholcus/config"
	"github.com/henrylee2cn/pholcus/logs"
)

type MgoSrc struct {
	*mgo.Session
}

var (
	connGcSecond = time.Duration(config.MGO_CONN_GC_SECOND) * 1e9
	session      *mgo.Session
	err          error
	MgoPool      = pool.ClassicPool(
		config.MGO_CONN_CAP,
		config.MGO_CONN_CAP/5,
		func() (pool.Src, error) {
			if err != nil || session.Ping() != nil {
				if session != nil {
					session.Close()
				}
				Refresh()
			}
			return &MgoSrc{session.Clone()}, err
		},
		connGcSecond)
)

func Refresh() {
	session, err = mgo.Dial(config.MGO_CONN_STR)
	if err != nil {
		logs.Log.Error("MongoDB：%v\n", err)
	} else if err = session.Ping(); err != nil {
		logs.Log.Error("MongoDB：%v\n", err)
	} else {
		session.SetPoolLimit(config.MGO_CONN_CAP)
	}
}

// 判断资源是否可用
func (self *MgoSrc) Usable() bool {
	if self.Session == nil || self.Session.Ping() != nil {
		return false
	}
	return true
}

// 使用后的重置方法
func (*MgoSrc) Reset() {}

// 被资源池删除前的自毁方法
func (self *MgoSrc) Close() {
	if self.Session == nil {
		return
	}
	self.Session.Close()
}

func Error() error {
	return err
}

// 调用资源池中的资源
func Call(fn func(pool.Src) error) error {
	return MgoPool.Call(fn)
}

// 销毁资源池
func Close() {
	MgoPool.Close()
}

// 返回当前资源数量
func Len() int {
	return MgoPool.Len()
}

// 获取所有数据
func DatabaseNames() (names []string, err error) {
	err = MgoPool.Call(func(src pool.Src) error {
		names, err = src.(*MgoSrc).DatabaseNames()
		return err
	})
	return
}

// 获取数据库集合列表
func CollectionNames(dbname string) (names []string, err error) {
	MgoPool.Call(func(src pool.Src) error {
		names, err = src.(*MgoSrc).DB(dbname).CollectionNames()
		return err
	})
	return
}
<|endoftext|>


<commit_before>/*
Package sabercat implements net/http.FileSystem to serve
contents directly from MongoDB's GridFS.

To serve all files from a database containing a GridFS
called `fs` via http:

	session, err := mgo.Dial("mongodb://localhost/database")
	if err != nil {
		log.Fatalf("Could not connect to mongodb: %s", err)
	}
	gfs := session.DB("").GridFS("fs")
	http.Handle("/", http.FileServer(sabercat.GridDir{gfs})

Directory listing has not been implemented.
*/
package sabercat

import (
	"labix.org/v2/mgo"
	"log"
	"net/http"
)

const (
	VERSION = "1.4.1"
)

type GridDir struct {
	GridFS *mgo.GridFS
	// If true, the leading slash will be stripped
	StripSlash bool
}

func (gd GridDir) Open(filename string) (http.File, error) {
	if gd.StripSlash && filename[0] == '/' {
		filename = filename[1:]
	}
	f, err := gd.GridFS.Open(filename)
	if err != nil && err != mgo.ErrNotFound {
		// Check if connection is alive
		err = gd.GridFS.Files.Database.Session.Ping()
		if err != nil {
			log.Printf("Refreshing connection...")
			gd.GridFS.Files.Database.Session.Refresh()
			f, err = gd.GridFS.Open(filename)
		}
	}
	return &gridFile{f}, err
}
<commit_msg>Fix malformed mongo-URL bug<commit_after>/*
Package sabercat implements net/http.FileSystem to serve
contents directly from MongoDB's GridFS.

To serve all files from a database containing a GridFS
called `fs` via http:

	session, err := mgo.Dial("mongodb://localhost/database")
	if err != nil {
		log.Fatalf("Could not connect to mongodb: %s", err)
	}
	gfs := session.DB("").GridFS("fs")
	http.Handle("/", http.FileServer(sabercat.GridDir{gfs})

Directory listing has not been implemented.
*/
package sabercat

import (
	"labix.org/v2/mgo"
	"log"
	"net/http"
)

const (
	VERSION = "1.4.1"
)

type GridDir struct {
	GridFS *mgo.GridFS
	// If true, the leading slash will be stripped
	StripSlash bool
}

func (gd GridDir) Open(filename string) (http.File, error) {
	if gd.StripSlash && filename[0] == '/' {
		filename = filename[1:]
	}
	f, err := gd.GridFS.Open(filename)
	if err != nil && err != mgo.ErrNotFound {
		// Check if connection is alive
		pingErr := gd.GridFS.Files.Database.Session.Ping()
		if pingErr != nil {
			log.Printf("Refreshing connection...")
			gd.GridFS.Files.Database.Session.Refresh()
			f, err = gd.GridFS.Open(filename)
		} else {
			log.Printf("Unknown error: %s", err)
		}
	}
	return &gridFile{f}, err
}
<|endoftext|>