<commit_before>package main

import (
	"fmt"

	"github.com/bcicen/ctop/widgets"
	ui "github.com/gizak/termui"
)

type Grid struct {
	cursorID   string // id of currently selected container
	cmap       *ContainerMap
	containers []*Container // sorted slice of containers
	header     *widgets.CTopHeader
}

func NewGrid() *Grid {
	cmap := NewContainerMap()
	containers := cmap.All()
	return &Grid{
		cursorID:   containers[0].id,
		cmap:       cmap,
		containers: containers,
		header:     widgets.NewCTopHeader(),
	}
}

// Return current cursor index
func (g *Grid) cursorIdx() int {
	for n, c := range g.containers {
		if c.id == g.cursorID {
			return n
		}
	}
	return 0
}

func (g *Grid) cursorUp() {
	idx := g.cursorIdx()
	// decrement if possible
	if idx > 0 {
		g.cursorID = g.containers[idx-1].id
		g.redrawCursor()
	}
}

func (g *Grid) cursorDown() {
	idx := g.cursorIdx()
	// increment if possible
	if idx < (len(g.containers) - 1) {
		g.cursorID = g.containers[idx+1].id
		g.redrawCursor()
	}
}

// Redraw the cursor with the currently selected row
func (g *Grid) redrawCursor() {
	for _, c := range g.containers {
		if c.id == g.cursorID {
			c.widgets.Highlight()
		} else {
			c.widgets.UnHighlight()
		}
		ui.Render(ui.Body)
	}
}

func (g *Grid) redrawRows() {
	// reinit body rows
	ui.Body.Rows = []*ui.Row{}
	ui.Clear()

	// build layout
	if GlobalConfig["enableHeader"] == "1" {
		g.header.SetCount(len(g.containers))
		ui.Body.AddRows(g.header.Row())
	}
	ui.Body.AddRows(fieldHeader())
	for _, c := range g.containers {
		ui.Body.AddRows(c.widgets.Row())
	}

	ui.Body.Align()
	ui.Render(ui.Body)
}

func fieldHeader() *ui.Row {
	return ui.NewRow(
		ui.NewCol(2, 0, headerPar("NAME")),
		ui.NewCol(2, 0, headerPar("CID")),
		ui.NewCol(2, 0, headerPar("CPU")),
		ui.NewCol(2, 0, headerPar("MEM")),
		ui.NewCol(2, 0, headerPar("NET RX/TX")),
	)
}

func headerPar(s string) *ui.Par {
	p := ui.NewPar(fmt.Sprintf(" %s", s))
	p.Border = false
	p.Height = 2
	p.Width = 20
	p.TextFgColor = ui.ColorWhite
	return p
}

func ResetView() {
	ui.DefaultEvtStream.ResetHandlers()
	ui.Clear()
}

func OpenMenu(m func()) {
	ResetView()
	defer ResetView()
	m()
}

func (g *Grid) ExpandView() {
	ResetView()
	defer ResetView()
	container := g.cmap.Get(g.cursorID)
	container.Expand()
	container.widgets.Render()
	container.Collapse()
}

func Display(g *Grid) bool {
	var menu func()
	var expand bool

	// calculate layout
	ui.Body.Align()
	g.redrawCursor()
	ui.Render(ui.Body)

	ui.Handle("/sys/kbd/<up>", func(ui.Event) {
		g.cursorUp()
	})
	ui.Handle("/sys/kbd/<down>", func(ui.Event) {
		g.cursorDown()
	})
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		expand = true
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/f", func(ui.Event) {
		menu = FilterMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/h", func(ui.Event) {
		menu = HelpMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/q", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/r", func(e ui.Event) {
		GlobalConfig.toggle("sortReversed")
	})
	ui.Handle("/sys/kbd/s", func(ui.Event) {
		menu = SortMenu
		ui.StopLoop()
	})
	ui.Handle("/timer/1s", func(e ui.Event) {
		g.containers = g.cmap.All() // refresh containers for current sort order
		g.redrawRows()
	})

	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ui.Body.Width = ui.TermWidth()
		ui.Body.Align()
		ui.Clear()
		ui.Render(ui.Body)
	})

	ui.Loop()
	if menu != nil {
		OpenMenu(menu)
		return false
	}
	if expand {
		g.ExpandView()
		return false
	}
	return true
}
<commit_msg>fix panic on no containers<commit_after>package main

import (
	"fmt"

	"github.com/bcicen/ctop/widgets"
	ui "github.com/gizak/termui"
)

type Grid struct {
	cursorID   string // id of currently selected container
	cmap       *ContainerMap
	containers []*Container // sorted slice of containers
	header     *widgets.CTopHeader
}

func NewGrid() *Grid {
	cmap := NewContainerMap()
	g := &Grid{
		cmap:       cmap,
		containers: cmap.All(),
		header:     widgets.NewCTopHeader(),
	}
	// set initial cursor position
	if len(g.containers) > 0 {
		g.cursorID = g.containers[0].id
	}
	return g
}

// Return current cursor index
func (g *Grid) cursorIdx() int {
	for n, c := range g.containers {
		if c.id == g.cursorID {
			return n
		}
	}
	return 0
}

func (g *Grid) cursorUp() {
	idx := g.cursorIdx()
	// decrement if possible
	if idx > 0 {
		g.cursorID = g.containers[idx-1].id
		g.redrawCursor()
	}
}

func (g *Grid) cursorDown() {
	idx := g.cursorIdx()
	// increment if possible
	if idx < (len(g.containers) - 1) {
		g.cursorID = g.containers[idx+1].id
		g.redrawCursor()
	}
}

// Redraw the cursor with the currently selected row
func (g *Grid) redrawCursor() {
	for _, c := range g.containers {
		if c.id == g.cursorID {
			c.widgets.Highlight()
		} else {
			c.widgets.UnHighlight()
		}
		ui.Render(ui.Body)
	}
}

func (g *Grid) redrawRows() {
	// reinit body rows
	ui.Body.Rows = []*ui.Row{}
	ui.Clear()

	// build layout
	if GlobalConfig["enableHeader"] == "1" {
		g.header.SetCount(len(g.containers))
		ui.Body.AddRows(g.header.Row())
	}
	ui.Body.AddRows(fieldHeader())
	for _, c := range g.containers {
		ui.Body.AddRows(c.widgets.Row())
	}

	ui.Body.Align()
	ui.Render(ui.Body)
}

func fieldHeader() *ui.Row {
	return ui.NewRow(
		ui.NewCol(2, 0, headerPar("NAME")),
		ui.NewCol(2, 0, headerPar("CID")),
		ui.NewCol(2, 0, headerPar("CPU")),
		ui.NewCol(2, 0, headerPar("MEM")),
		ui.NewCol(2, 0, headerPar("NET RX/TX")),
	)
}

func headerPar(s string) *ui.Par {
	p := ui.NewPar(fmt.Sprintf(" %s", s))
	p.Border = false
	p.Height = 2
	p.Width = 20
	p.TextFgColor = ui.ColorWhite
	return p
}

func ResetView() {
	ui.DefaultEvtStream.ResetHandlers()
	ui.Clear()
}

func OpenMenu(m func()) {
	ResetView()
	defer ResetView()
	m()
}

func (g *Grid) ExpandView() {
	ResetView()
	defer ResetView()
	container := g.cmap.Get(g.cursorID)
	container.Expand()
	container.widgets.Render()
	container.Collapse()
}

func Display(g *Grid) bool {
	var menu func()
	var expand bool

	// calculate layout
	ui.Body.Align()
	g.redrawCursor()
	ui.Render(ui.Body)

	ui.Handle("/sys/kbd/<up>", func(ui.Event) {
		g.cursorUp()
	})
	ui.Handle("/sys/kbd/<down>", func(ui.Event) {
		g.cursorDown()
	})
	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		expand = true
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/f", func(ui.Event) {
		menu = FilterMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/h", func(ui.Event) {
		menu = HelpMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/q", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/r", func(e ui.Event) {
		GlobalConfig.toggle("sortReversed")
	})
	ui.Handle("/sys/kbd/s", func(ui.Event) {
		menu = SortMenu
		ui.StopLoop()
	})
	ui.Handle("/timer/1s", func(e ui.Event) {
		g.containers = g.cmap.All() // refresh containers for current sort order
		g.redrawRows()
	})

	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ui.Body.Width = ui.TermWidth()
		ui.Body.Align()
		ui.Clear()
		ui.Render(ui.Body)
	})

	ui.Loop()
	if menu != nil {
		OpenMenu(menu)
		return false
	}
	if expand {
		g.ExpandView()
		return false
	}
	return true
}
<|endoftext|>


<commit_before>// +build hack

/*
Copyright 2020 The Knative Authors

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

    https://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 hack is a collection of scripts used to bootstrap CI processes and
// other vital entrypoint functionallity.

package hack
<commit_msg>fix misspelling functionallity -> functionality (#78)<commit_after>// +build hack

/*
Copyright 2020 The Knative Authors

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

    https://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 hack is a collection of scripts used to bootstrap CI processes and
// other vital entrypoint functionality.

package hack
<|endoftext|>


<commit_before>package goDLX

import "errors"

const (
	e_head_set_horz = "Only column of Self can be left or right of Head"
	e_head_set_vert = "No nodes can be above or below Head"
	e_head_locked   = "All Columns must be added before any Rows"
	e_head_row_fail = "Failed to add row to matrix"
)

// Head nodes are the master column headers.
// These nodes form the heart of each sparse matrix
type Head struct {
	left, right node
	locked      bool // locks the matrix from adding new columns once rows are added
}

// New starts a new sparse matrix by creating the Head node.
func New() *Head {
	h := new(Head)
	h.initHead()
	return h
}

func (h *Head) initHead() {
	h.left = h
	h.right = h
}

////
// Satisfy the node interface
///

// lft returns the node to the left of the Head
func (h *Head) lft() node {
	return h.left
}

// setLft sets a node to the left of the Head node.
func (h *Head) setLft(n node) (node, error) {
	switch n.(type) {
	case *Head:
		// only invalid if it is a different Head
		if h != n {
			return nil, errors.New(e_head_set_horz)
		}
	case *column:
		// do nothing, we're good
	default:
		return nil, errors.New(e_head_set_horz)
	}
	h.left = n
	return n, nil
}

// rgt returns the node to the right of the Head
func (h *Head) rgt() node {
	return h.right
}

// setRgt sets the node to the right of the Head
func (h *Head) setRgt(n node) (node, error) {
	switch n.(type) {
	case *Head:
		// only invalid if it is a different Head
		if h != n {
			return nil, errors.New(e_head_set_horz)
		}
	case *column:
		// do nothing, we're good
	default:
		return nil, errors.New(e_head_set_horz)
	}
	h.right = n
	return n, nil
}

// up returns the Head itself as Head nodes are only horizontally linked
func (h *Head) up() node {
	return h
}

// setUp always errors because Head nodes are horizontally linked only
func (h *Head) setUp(n node) (node, error) {
	return nil, errors.New(e_head_set_vert)
}

// dn returns the Head itself as Head nodes are only horizontally linked
func (h *Head) dn() node {
	return h
}

// setDn always errors because Head nodes are horizontally linked only
func (h *Head) setDn(n node) (node, error) {
	return nil, errors.New(e_head_set_vert)
}

// col returns the Head itself as it is the header in the column list
func (h *Head) col() node {
	return h
}

////
// Populating the Matrix
////

// AddCol adds a Column to the matrix.
// Each column has a name for reference and can be set to optional.
// Where required columns have one and only one valid row, optional
// columns have zero or one valid row.
func (h *Head) AddCol(name string, optional bool) error {
	if h.locked {
		return errors.New(e_head_locked)
	}
	c := newColumn(name, optional)
	err := linkHorz(c, h)
	return err
}

// AddRow adds a row to the matrix
// Each row is populated by adding links for every true returned
// from the anonymous function whose arguments are the column
// name and index. Once rows are added, the matrix is locked
// and no further columns may be added.
func (h *Head) AddRow(f func(int, string) bool) error {
	h.locked = true // lock this matrix
	// loop through the columns to check against
	i := 0
	var e *element
	for n := h.rgt(); n != h; n = n.rgt() {
		if c, ok := n.(*column); ok {
			if f(i, c.name) {
				e = newElement(e, c)
			}
		} else {
			return errors.New(e_head_row_fail)
		}
		i++
	}
	if e == nil {
		return errors.New(e_head_row_fail)
	}
	return nil
}
<commit_msg>Refactor `AddRow()` type assertion<commit_after>package goDLX

import "errors"

const (
	e_head_set_horz = "Only column of Self can be left or right of Head"
	e_head_set_vert = "No nodes can be above or below Head"
	e_head_locked   = "All Columns must be added before any Rows"
	e_head_row_fail = "Failed to add row to matrix"
)

// Head nodes are the master column headers.
// These nodes form the heart of each sparse matrix
type Head struct {
	left, right node
	locked      bool // locks the matrix from adding new columns once rows are added
}

// New starts a new sparse matrix by creating the Head node.
func New() *Head {
	h := new(Head)
	h.initHead()
	return h
}

func (h *Head) initHead() {
	h.left = h
	h.right = h
}

////
// Satisfy the node interface
///

// lft returns the node to the left of the Head
func (h *Head) lft() node {
	return h.left
}

// setLft sets a node to the left of the Head node.
func (h *Head) setLft(n node) (node, error) {
	switch n.(type) {
	case *Head:
		// only invalid if it is a different Head
		if h != n {
			return nil, errors.New(e_head_set_horz)
		}
	case *column:
		// do nothing, we're good
	default:
		return nil, errors.New(e_head_set_horz)
	}
	h.left = n
	return n, nil
}

// rgt returns the node to the right of the Head
func (h *Head) rgt() node {
	return h.right
}

// setRgt sets the node to the right of the Head
func (h *Head) setRgt(n node) (node, error) {
	switch n.(type) {
	case *Head:
		// only invalid if it is a different Head
		if h != n {
			return nil, errors.New(e_head_set_horz)
		}
	case *column:
		// do nothing, we're good
	default:
		return nil, errors.New(e_head_set_horz)
	}
	h.right = n
	return n, nil
}

// up returns the Head itself as Head nodes are only horizontally linked
func (h *Head) up() node {
	return h
}

// setUp always errors because Head nodes are horizontally linked only
func (h *Head) setUp(n node) (node, error) {
	return nil, errors.New(e_head_set_vert)
}

// dn returns the Head itself as Head nodes are only horizontally linked
func (h *Head) dn() node {
	return h
}

// setDn always errors because Head nodes are horizontally linked only
func (h *Head) setDn(n node) (node, error) {
	return nil, errors.New(e_head_set_vert)
}

// col returns the Head itself as it is the header in the column list
func (h *Head) col() node {
	return h
}

////
// Populating the Matrix
////

// AddCol adds a Column to the matrix.
// Each column has a name for reference and can be set to optional.
// Where required columns have one and only one valid row, optional
// columns have zero or one valid row.
func (h *Head) AddCol(name string, optional bool) error {
	if h.locked {
		return errors.New(e_head_locked)
	}
	c := newColumn(name, optional)
	err := linkHorz(c, h)
	return err
}

// AddRow adds a row to the matrix
// Each row is populated by adding links for every true returned
// from the anonymous function whose arguments are the column
// name and index. Once rows are added, the matrix is locked
// and no further columns may be added.
func (h *Head) AddRow(f func(int, string) bool) error {
	h.locked = true // lock this matrix
	// loop through the columns to check against
	i := 0
	var e *element
	for c, ok := h.rgt().(*column); ok; c, ok = c.rgt().(*column) {
		if f(i, c.name) {
			e = newElement(e, c)
		}
		i++
	}
	if e == nil {
		return errors.New(e_head_row_fail)
	}
	return nil
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"text/template"
)

// AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var AppHelpTemplate = `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}

VERSION:
   {{.Version}}{{end}}{{end}}{{if .Description}}

DESCRIPTION:
   {{.Description}}{{end}}{{if len .Authors}}

AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
   {{range $index, $author := .Authors}}{{if $index}}
   {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}

GLOBAL OPTIONS:
   {{range $index, $option := .VisibleFlags}}{{if $index}}
   {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}

COPYRIGHT:
   {{.Copyright}}{{end}}
`

// CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var CommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}

CATEGORY:
   {{.Category}}{{end}}{{if .Description}}

DESCRIPTION:
   {{.Description}}{{end}}{{if .VisibleFlags}}

OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

// SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
{{end}}{{if .VisibleFlags}}
OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

var helpCommand = &Command{
	Name:      "help",
	Aliases:   []string{"h"},
	Usage:     "Shows a list of commands or help for one command",
	ArgsUsage: "[command]",
	Action: func(c *Context) error {
		args := c.Args()
		if args.Present() {
			return ShowCommandHelp(c, args.First())
		}

		ShowAppHelp(c)
		return nil
	},
}

var helpSubcommand = &Command{
	Name:      "help",
	Aliases:   []string{"h"},
	Usage:     "Shows a list of commands or help for one command",
	ArgsUsage: "[command]",
	Action: func(c *Context) error {
		args := c.Args()
		if args.Present() {
			return ShowCommandHelp(c, args.First())
		}

		return ShowSubcommandHelp(c)
	},
}

// Prints help for the App or Command
type helpPrinter func(w io.Writer, templ string, data interface{})

// HelpPrinter is a function that writes the help output. If not set a default
// is used. The function signature is:
// func(w io.Writer, templ string, data interface{})
var HelpPrinter helpPrinter = printHelp

// VersionPrinter prints the version for the App
var VersionPrinter = printVersion

// ShowAppHelp is an action that displays the help.
func ShowAppHelp(c *Context) error {
	HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
	return nil
}

// DefaultAppComplete prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) {
	for _, command := range c.App.Commands {
		if command.Hidden {
			continue
		}
		for _, name := range command.Names() {
			fmt.Fprintln(c.App.Writer, name)
		}
	}
}

// ShowCommandHelp prints help for the given command
func ShowCommandHelp(ctx *Context, command string) error {
	// show the subcommand help for a command with subcommands
	if command == "" {
		HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
		return nil
	}

	for _, c := range ctx.App.Commands {
		if c.HasName(command) {
			HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
			return nil
		}
	}

	if ctx.App.CommandNotFound == nil {
		return Exit(fmt.Sprintf("No help topic for '%v'", command), 3)
	}

	ctx.App.CommandNotFound(ctx, command)
	return nil
}

// ShowSubcommandHelp prints help for the given subcommand
func ShowSubcommandHelp(c *Context) error {
	if c == nil {
		return nil
	}

	if c.Command != nil {
		return ShowCommandHelp(c, c.Command.Name)
	}

	return ShowCommandHelp(c, "")
}

// ShowVersion prints the version number of the App
func ShowVersion(c *Context) {
	VersionPrinter(c)
}

func printVersion(c *Context) {
	fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
}

// ShowCompletions prints the lists of commands within a given context
func ShowCompletions(c *Context) {
	a := c.App
	if a != nil && a.ShellComplete != nil {
		a.ShellComplete(c)
	}
}

// ShowCommandCompletions prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) {
	c := ctx.App.Command(command)
	if c != nil && c.ShellComplete != nil {
		c.ShellComplete(ctx)
	}
}

func printHelp(out io.Writer, templ string, data interface{}) {
	funcMap := template.FuncMap{
		"join": strings.Join,
	}

	w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
	t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))

	errDebug := os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != ""

	err := t.Execute(w, data)
	if err != nil {
		if errDebug {
			fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
		}
		return
	}

	w.Flush()
}

func checkVersion(c *Context) bool {
	found := false
	if VersionFlag.Name != "" {
		for _, name := range VersionFlag.Names() {
			if c.Bool(name) {
				found = true
			}
		}
	}
	return found
}

func checkHelp(c *Context) bool {
	found := false
	if HelpFlag.Name != "" {
		for _, name := range HelpFlag.Names() {
			if c.Bool(name) {
				found = true
			}
		}
	}
	return found
}

func checkCommandHelp(c *Context, name string) bool {
	if c.Bool("h") || c.Bool("help") {
		ShowCommandHelp(c, name)
		return true
	}

	return false
}

func checkSubcommandHelp(c *Context) bool {
	if c.Bool("h") || c.Bool("help") {
		ShowSubcommandHelp(c)
		return true
	}

	return false
}

func checkCompletions(c *Context) bool {
	if c.Bool(GenerateCompletionFlag.Name) && c.App.EnableShellCompletion {
		ShowCompletions(c)
		return true
	}

	return false
}

func checkCommandCompletions(c *Context, name string) bool {
	if c.Bool(GenerateCompletionFlag.Name) && c.App.EnableShellCompletion {
		ShowCommandCompletions(c, name)
		return true
	}

	return false
}

func checkInitCompletion(c *Context) (bool, error) {
	if c.IsSet(InitCompletionFlag.Name) {
		shell := c.String(InitCompletionFlag.Name)
		progName := os.Args[0]
		switch shell {
		case "bash":
			fmt.Print(bashCompletionCode(progName))
			return true, nil
		case "zsh":
			fmt.Print(zshCompletionCode(progName))
			return true, nil
		default:
			return false, fmt.Errorf("--init-completion value cannot be '%s'", shell)
		}
	}
	return false, nil
}

func bashCompletionCode(progName string) string {
	var template = `_cli_bash_autocomplete() {
     local cur opts base;
     COMPREPLY=();
     cur="${COMP_WORDS[COMP_CWORD]}";
     opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-completion );
     COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) );
     return 0;
};
complete -F _cli_bash_autocomplete %s`
	return fmt.Sprintf(template, progName)
}

func zshCompletionCode(progName string) string {
	var template = `autoload -U compinit && compinit;
autoload -U bashcompinit && bashcompinit;`

	return template + "\n" + bashCompletionCode(progName)
}
<commit_msg>Remove the error return signature<commit_after>package cli

import (
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"text/template"
)

// AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var AppHelpTemplate = `NAME:
   {{.Name}} - {{.Usage}}

USAGE:
   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}

VERSION:
   {{.Version}}{{end}}{{end}}{{if .Description}}

DESCRIPTION:
   {{.Description}}{{end}}{{if len .Authors}}

AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
   {{range $index, $author := .Authors}}{{if $index}}
   {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}

GLOBAL OPTIONS:
   {{range $index, $option := .VisibleFlags}}{{if $index}}
   {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}

COPYRIGHT:
   {{.Copyright}}{{end}}
`

// CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var CommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}

CATEGORY:
   {{.Category}}{{end}}{{if .Description}}

DESCRIPTION:
   {{.Description}}{{end}}{{if .VisibleFlags}}

OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

// SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME:
   {{.HelpName}} - {{.Usage}}

USAGE:
   {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
{{end}}{{if .VisibleFlags}}
OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

var helpCommand = &Command{
	Name:      "help",
	Aliases:   []string{"h"},
	Usage:     "Shows a list of commands or help for one command",
	ArgsUsage: "[command]",
	Action: func(c *Context) error {
		args := c.Args()
		if args.Present() {
			return ShowCommandHelp(c, args.First())
		}

		ShowAppHelp(c)
		return nil
	},
}

var helpSubcommand = &Command{
	Name:      "help",
	Aliases:   []string{"h"},
	Usage:     "Shows a list of commands or help for one command",
	ArgsUsage: "[command]",
	Action: func(c *Context) error {
		args := c.Args()
		if args.Present() {
			return ShowCommandHelp(c, args.First())
		}

		return ShowSubcommandHelp(c)
	},
}

// Prints help for the App or Command
type helpPrinter func(w io.Writer, templ string, data interface{})

// HelpPrinter is a function that writes the help output. If not set a default
// is used. The function signature is:
// func(w io.Writer, templ string, data interface{})
var HelpPrinter helpPrinter = printHelp

// VersionPrinter prints the version for the App
var VersionPrinter = printVersion

// ShowAppHelp is an action that displays the help.
func ShowAppHelp(c *Context) {
	HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
}

// DefaultAppComplete prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) {
	for _, command := range c.App.Commands {
		if command.Hidden {
			continue
		}
		for _, name := range command.Names() {
			fmt.Fprintln(c.App.Writer, name)
		}
	}
}

// ShowCommandHelp prints help for the given command
func ShowCommandHelp(ctx *Context, command string) error {
	// show the subcommand help for a command with subcommands
	if command == "" {
		HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
		return nil
	}

	for _, c := range ctx.App.Commands {
		if c.HasName(command) {
			HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
			return nil
		}
	}

	if ctx.App.CommandNotFound == nil {
		return Exit(fmt.Sprintf("No help topic for '%v'", command), 3)
	}

	ctx.App.CommandNotFound(ctx, command)
	return nil
}

// ShowSubcommandHelp prints help for the given subcommand
func ShowSubcommandHelp(c *Context) error {
	if c == nil {
		return nil
	}

	if c.Command != nil {
		return ShowCommandHelp(c, c.Command.Name)
	}

	return ShowCommandHelp(c, "")
}

// ShowVersion prints the version number of the App
func ShowVersion(c *Context) {
	VersionPrinter(c)
}

func printVersion(c *Context) {
	fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
}

// ShowCompletions prints the lists of commands within a given context
func ShowCompletions(c *Context) {
	a := c.App
	if a != nil && a.ShellComplete != nil {
		a.ShellComplete(c)
	}
}

// ShowCommandCompletions prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) {
	c := ctx.App.Command(command)
	if c != nil && c.ShellComplete != nil {
		c.ShellComplete(ctx)
	}
}

func printHelp(out io.Writer, templ string, data interface{}) {
	funcMap := template.FuncMap{
		"join": strings.Join,
	}

	w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
	t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))

	errDebug := os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != ""

	err := t.Execute(w, data)
	if err != nil {
		if errDebug {
			fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
		}
		return
	}

	w.Flush()
}

func checkVersion(c *Context) bool {
	found := false
	if VersionFlag.Name != "" {
		for _, name := range VersionFlag.Names() {
			if c.Bool(name) {
				found = true
			}
		}
	}
	return found
}

func checkHelp(c *Context) bool {
	found := false
	if HelpFlag.Name != "" {
		for _, name := range HelpFlag.Names() {
			if c.Bool(name) {
				found = true
			}
		}
	}
	return found
}

func checkCommandHelp(c *Context, name string) bool {
	if c.Bool("h") || c.Bool("help") {
		ShowCommandHelp(c, name)
		return true
	}

	return false
}

func checkSubcommandHelp(c *Context) bool {
	if c.Bool("h") || c.Bool("help") {
		ShowSubcommandHelp(c)
		return true
	}

	return false
}

func checkCompletions(c *Context) bool {
	if c.Bool(GenerateCompletionFlag.Name) && c.App.EnableShellCompletion {
		ShowCompletions(c)
		return true
	}

	return false
}

func checkCommandCompletions(c *Context, name string) bool {
	if c.Bool(GenerateCompletionFlag.Name) && c.App.EnableShellCompletion {
		ShowCommandCompletions(c, name)
		return true
	}

	return false
}

func checkInitCompletion(c *Context) (bool, error) {
	if c.IsSet(InitCompletionFlag.Name) {
		shell := c.String(InitCompletionFlag.Name)
		progName := os.Args[0]
		switch shell {
		case "bash":
			fmt.Print(bashCompletionCode(progName))
			return true, nil
		case "zsh":
			fmt.Print(zshCompletionCode(progName))
			return true, nil
		default:
			return false, fmt.Errorf("--init-completion value cannot be '%s'", shell)
		}
	}
	return false, nil
}

func bashCompletionCode(progName string) string {
	var template = `_cli_bash_autocomplete() {
     local cur opts base;
     COMPREPLY=();
     cur="${COMP_WORDS[COMP_CWORD]}";
     opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-completion );
     COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) );
     return 0;
};
complete -F _cli_bash_autocomplete %s`
	return fmt.Sprintf(template, progName)
}

func zshCompletionCode(progName string) string {
	var template = `autoload -U compinit && compinit;
autoload -U bashcompinit && bashcompinit;`

	return template + "\n" + bashCompletionCode(progName)
}
<|endoftext|>


<commit_before>// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

const (
	help = `
{{binary}} ({{version}})

{{binary}} is a command line tool that will allow any executable program
that accepts input on stdin and produces output on stdout to be turned into
a WebSocket server.

Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

Options:

  --port=PORT                    HTTP port to listen on.

  --address=ADDRESS              Address to bind to (multiple options allowed)
                                 Use square brackets to specify IPv6 address.
                                 Default: "" (all)

  --sameorigin={true,false}      Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to requested HTTP
                                 Host. Default: false.

  --origin=host[:port][,host[:port]...]
                                 Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to one of the host
                                 and port combinations listed. If the port is not
                                 specified, any port number will match.
                                 Default: "" (allow any origin)

  --ssl                          Listen for HTTPS socket instead of HTTP.
  --sslcert=FILE                 All three options must be used or all of
  --sslkey=FILE                  them should be omitted.

  --passenv VAR[,VAR...]         Lists environment variables allowed to be
                                 passed to executed scripts.

  --binary={true,false}          Switches communication to binary, process reads
                                 send to browser as blobs and all reads from the
                                 browser are immediately flushed to the process.
                                 Default: false

  --reverselookup={true,false}   Perform DNS reverse lookups on remote clients.
                                 Default: true

  --dir=DIR                      Allow all scripts in the local directory
                                 to be accessed as WebSockets. If using this,
                                 option, then the standard program and args
                                 options should not be specified.

  --staticdir=DIR                Serve static files in this directory over HTTP.

  --cgidir=DIR                   Serve CGI scripts in this directory over HTTP.

  --header="..."                 Set custom HTTP header to each answer. For
                                 example: --header="Server: someserver/0.0.1"

  --header-ws="...."             Same as --header, just applies to only those
                                 responses that indicate upgrade of TCP connection
                                 to a WebSockets protocol.

  --header-http="...."           Same as --header, just applies to only to plain
                                 HTTP responses that do not indicate WebSockets
                                 upgrade

  --help                         Print help and exit.

  --version                      Print version and exit.

  --license                      Print license and exit.

  --devconsole                   Enable interactive development console.
                                 This enables you to access the websocketd
                                 server with a web-browser and use a
                                 user interface to quickly test WebSocket
                                 endpoints. For example, to test an
                                 endpoint at ws://[host]/foo, you can
                                 visit http://[host]/foo in your browser.
                                 This flag cannot be used in conjunction
                                 with --staticdir or --cgidir.

  --loglevel=LEVEL               Log level to use (default access).
                                 From most to least verbose:
                                 debug, trace, access, info, error, fatal

Full documentation at http://websocketd.com/

Copyright 2013 Joe Walnes and the websocketd team. All rights reserved.
BSD license: Run '{{binary}} --license' for details.
`
	short = `
Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

  Or, show extended help message using:
    {{binary}} --help
`
)

func get_help_message(content string) string {
	msg := strings.Trim(content, " \n")
	msg = strings.Replace(msg, "{{binary}}", HelpProcessName(), -1)
	return strings.Replace(msg, "{{version}}", Version(), -1)
}

func HelpProcessName() string {
	binary := os.Args[0]
	if strings.Contains(binary, "/go-build") { // this was run using "go run", let's use something appropriate
		binary = "websocketd"
	} else {
		binary = filepath.Base(binary)
	}
	return binary
}

func PrintHelp() {
	fmt.Fprintf(os.Stderr, "%s\n", get_help_message(help))
}

func ShortHelp() {
	// Shown after some error
	fmt.Fprintf(os.Stderr, "\n%s\n", get_help_message(short))
}
<commit_msg>Fixed #186, added instructions on --maxforks<commit_after>// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

const (
	help = `
{{binary}} ({{version}})

{{binary}} is a command line tool that will allow any executable program
that accepts input on stdin and produces output on stdout to be turned into
a WebSocket server.

Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

Options:

  --port=PORT                    HTTP port to listen on.

  --address=ADDRESS              Address to bind to (multiple options allowed)
                                 Use square brackets to specify IPv6 address.
                                 Default: "" (all)

  --sameorigin={true,false}      Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to requested HTTP
                                 Host. Default: false.

  --origin=host[:port][,host[:port]...]
                                 Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to one of the host
                                 and port combinations listed. If the port is not
                                 specified, any port number will match.
                                 Default: "" (allow any origin)

  --ssl                          Listen for HTTPS socket instead of HTTP.
  --sslcert=FILE                 All three options must be used or all of
  --sslkey=FILE                  them should be omitted.

  --passenv VAR[,VAR...]         Lists environment variables allowed to be
                                 passed to executed scripts.

  --binary={true,false}          Switches communication to binary, process reads
                                 send to browser as blobs and all reads from the
                                 browser are immediately flushed to the process.
                                 Default: false

  --reverselookup={true,false}   Perform DNS reverse lookups on remote clients.
                                 Default: true

  --dir=DIR                      Allow all scripts in the local directory
                                 to be accessed as WebSockets. If using this,
                                 option, then the standard program and args
                                 options should not be specified.

  --staticdir=DIR                Serve static files in this directory over HTTP.

  --cgidir=DIR                   Serve CGI scripts in this directory over HTTP.

  --maxforks=N                   Limit number of processes that websocketd is
                                 able to execute with WS and CGI handlers.
                                 When maxforks reached the server will be
                                 rejecting requests that require executing
                                 another process (unlimited when 0 or negative).
                                 Default: 0

  --header="..."                 Set custom HTTP header to each answer. For
                                 example: --header="Server: someserver/0.0.1"

  --header-ws="...."             Same as --header, just applies to only those
                                 responses that indicate upgrade of TCP connection
                                 to a WebSockets protocol.

  --header-http="...."           Same as --header, just applies to only to plain
                                 HTTP responses that do not indicate WebSockets
                                 upgrade

  --help                         Print help and exit.

  --version                      Print version and exit.

  --license                      Print license and exit.

  --devconsole                   Enable interactive development console.
                                 This enables you to access the websocketd
                                 server with a web-browser and use a
                                 user interface to quickly test WebSocket
                                 endpoints. For example, to test an
                                 endpoint at ws://[host]/foo, you can
                                 visit http://[host]/foo in your browser.
                                 This flag cannot be used in conjunction
                                 with --staticdir or --cgidir.

  --loglevel=LEVEL               Log level to use (default access).
                                 From most to least verbose:
                                 debug, trace, access, info, error, fatal

Full documentation at http://websocketd.com/

Copyright 2013 Joe Walnes and the websocketd team. All rights reserved.
BSD license: Run '{{binary}} --license' for details.
`
	short = `
Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

  Or, show extended help message using:
    {{binary}} --help
`
)

func get_help_message(content string) string {
	msg := strings.Trim(content, " \n")
	msg = strings.Replace(msg, "{{binary}}", HelpProcessName(), -1)
	return strings.Replace(msg, "{{version}}", Version(), -1)
}

func HelpProcessName() string {
	binary := os.Args[0]
	if strings.Contains(binary, "/go-build") { // this was run using "go run", let's use something appropriate
		binary = "websocketd"
	} else {
		binary = filepath.Base(binary)
	}
	return binary
}

func PrintHelp() {
	fmt.Fprintf(os.Stderr, "%s\n", get_help_message(help))
}

func ShortHelp() {
	// Shown after some error
	fmt.Fprintf(os.Stderr, "\n%s\n", get_help_message(short))
}
<|endoftext|>


<commit_before>package raygunHook

import (
	"github.com/gsblue/raygunHook/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	ray "github.com/gsblue/raygunHook/Godeps/_workspace/src/github.com/gsblue/raygun4go"
	"net/http"
)

type hook struct {
	Client raygunClient
}

//HookConfig is the struct to hold the configuration values required for the hook.
type HookConfig struct {
	APIKey  string   //ApiKey for your raygun account. This field is mandatory.
	AppName string   //AppName is your application name. This field is mandatory.
	Version string   //Version of your application
	Tags    []string //Tags which get added to all the error entries
}

type raygunClient interface {
	CreateErrorEntry(err error) *ray.ErrorEntry
	CreateErrorEntryFromMsg(msg string) *ray.ErrorEntry
	SubmitError(entry *ray.ErrorEntry) error
}

const (
	//ErrorFieldName is the name of the field in logrus.Entry.Data, which should hold the error
	ErrorFieldName = "error"
	//RequestFieldName is the name of the field in logrus.Entry.Data, which should hold the request
	RequestFieldName = "request"
	//UserFieldName is the name of the field in logrus.Entry.Data, which should hold the user identifier
	UserFieldName = "user"
	//CustomDataFieldName is the name of the field in logrus.Entry.Data, which should hold any custom data
	CustomDataFieldName = "customData"
)

//NewHook creates a new raygun logrus.Hook
func NewHook(config *HookConfig) (logrus.Hook, error) {
	c, err := ray.New(config.AppName, config.APIKey)
	if err != nil {
		return nil, err
	}
	c.Version(config.Version).Tags(config.Tags)

	return &hook{Client: c}, nil
}

//Fire sends the error from logrus.Entry to raygun
func (h *hook) Fire(e *logrus.Entry) error {
	var entry *ray.ErrorEntry

	if val, ok := e.Data[ErrorFieldName]; ok {
		if err, ok := val.(error); ok {
			entry = h.Client.CreateErrorEntry(err)
		}
	}

	if entry == nil {
		entry = h.Client.CreateErrorEntryFromMsg(e.Message)
	}

	if val, ok := e.Data[RequestFieldName]; ok {
		if req, ok := val.(*http.Request); ok {
			entry.SetRequest(req)
		}
	}

	if val, ok := e.Data[UserFieldName]; ok {
		if user, ok := val.(string); ok {
			entry.SetUser(user)
		}
	}

	if val, ok := e.Data[CustomDataFieldName]; ok {
		entry.SetCustomData(val)
	}

	return h.Client.SubmitError(entry)
}

//Levels returns the logrus.Level which this raygung hook supports
func (h *hook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}
}
<commit_msg>added comments<commit_after>//Package raygunHook provides the hook for logrus, to send errors to raygun.io
//for more details visit https://www.github.com/gsblue/raygunHook
package raygunHook

import (
	"github.com/gsblue/raygunHook/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	ray "github.com/gsblue/raygunHook/Godeps/_workspace/src/github.com/gsblue/raygun4go"
	"net/http"
)

type hook struct {
	Client raygunClient
}

//HookConfig is the struct to hold the configuration values required for the hook.
type HookConfig struct {
	APIKey  string   //APIKey for your raygun account. This field is mandatory.
	AppName string   //AppName is your application name. This field is mandatory.
	Version string   //Version of your application
	Tags    []string //Tags which get added to all the error entries
}

type raygunClient interface {
	CreateErrorEntry(err error) *ray.ErrorEntry
	CreateErrorEntryFromMsg(msg string) *ray.ErrorEntry
	SubmitError(entry *ray.ErrorEntry) error
}

const (
	//ErrorFieldName is the name of the field in logrus.Entry.Data, which should hold the error
	ErrorFieldName = "error"
	//RequestFieldName is the name of the field in logrus.Entry.Data, which should hold the request
	RequestFieldName = "request"
	//UserFieldName is the name of the field in logrus.Entry.Data, which should hold the user identifier
	UserFieldName = "user"
	//CustomDataFieldName is the name of the field in logrus.Entry.Data, which should hold any custom data
	CustomDataFieldName = "customData"
)

//NewHook creates a new raygun logrus.Hook
func NewHook(config *HookConfig) (logrus.Hook, error) {
	c, err := ray.New(config.AppName, config.APIKey)
	if err != nil {
		return nil, err
	}
	c.Version(config.Version).Tags(config.Tags)

	return &hook{Client: c}, nil
}

//Fire sends the error from logrus.Entry to raygun
func (h *hook) Fire(e *logrus.Entry) error {
	var entry *ray.ErrorEntry

	if val, ok := e.Data[ErrorFieldName]; ok {
		if err, ok := val.(error); ok {
			entry = h.Client.CreateErrorEntry(err)
		}
	}

	if entry == nil {
		entry = h.Client.CreateErrorEntryFromMsg(e.Message)
	}

	if val, ok := e.Data[RequestFieldName]; ok {
		if req, ok := val.(*http.Request); ok {
			entry.SetRequest(req)
		}
	}

	if val, ok := e.Data[UserFieldName]; ok {
		if user, ok := val.(string); ok {
			entry.SetUser(user)
		}
	}

	if val, ok := e.Data[CustomDataFieldName]; ok {
		entry.SetCustomData(val)
	}

	return h.Client.SubmitError(entry)
}

//Levels returns the logrus.Level which this raygung hook supports
func (h *hook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/http"
	"path"

	"github.com/naoina/denco"
	"github.com/pocke/hlog"
	"github.com/skratchdot/open-golang/open"
	"github.com/yosssi/ace"
)

// Server is HTTP server.
type Server struct {
	storage *Storage
}

func NewServer(port int) *Server {
	s := &Server{
		storage: NewStorage(),
	}

	wsm := NewWSManager(s.storage.OnUpdate())

	mux := denco.NewMux()
	f, err := mux.Build([]denco.Handler{
		mux.GET("/", s.indexHandler),
		mux.POST("/auth", s.authHandler),
		mux.GET("/files/*path", s.ServeFile),
		mux.GET("/ws", func(w http.ResponseWriter, r *http.Request, _ denco.Params) { wsm.ServeHTTP(w, r) }),
		mux.GET("/:type/:fname", s.serveAsset),
	})
	if err != nil {
		panic(err)
	}

	handler := f.ServeHTTP
	if DEBUG {
		handler = hlog.Wrap(f.ServeHTTP)
	}
	url, err := serve(handler, port)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Open: %s\n", url)
	open.Start(url)

	return s
}

func serve(f func(w http.ResponseWriter, r *http.Request), port int) (string, error) {
	p := fmt.Sprintf(":%d", port)
	l, err := net.Listen("tcp", p)
	if err != nil {
		return "", err
	}
	url := fmt.Sprintf("http://%s", l.Addr())
	go func() {
		http.Serve(l, http.HandlerFunc(f))
	}()
	return url, nil
}

// ServeFile serves parsed markdown.
func (s *Server) ServeFile(w http.ResponseWriter, r *http.Request, p denco.Params) {
	path := p.Get("path")
	f, exist := s.storage.Get(path)
	if !exist {
		http.Error(w, fmt.Sprintf("%s page not found", path), http.StatusNotFound)
		return
	}
	if f.err != nil {
		http.Error(w, f.err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write([]byte(f.html))
}

// authHandler get and save GitHub access token.
func (s *Server) authHandler(w http.ResponseWriter, r *http.Request, _ denco.Params) {
	r.ParseForm()
	v := r.PostForm
	user := v.Get("username")
	pass := v.Get("password")

	err := s.storage.token.Init(user, pass)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	s.storage.AddAll()
	http.Redirect(w, r, "/", http.StatusFound)
}

func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request, _ denco.Params) {
	if s.storage.token.hasToken() {
		loadAce(w, "index", s.storage.Index())
	} else {
		loadAce(w, "before_auth", nil)
	}
}

func loadAce(w http.ResponseWriter, action string, data interface{}) {
	tpl, err := ace.Load("assets/base", "assets/"+action, &ace.Options{
		DynamicReload: DEBUG,
		Asset:         Asset,
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = tpl.Execute(w, data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func (_ *Server) serveAsset(w http.ResponseWriter, r *http.Request, p denco.Params) {
	t := p.Get("type")
	fname := p.Get("fname")
	file, err := Asset(path.Join("assets", t, fname))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var contentType string
	switch t {
	case "js":
		contentType = "application/javascript"
	case "css":
		contentType = "text/css"
	}
	w.Header().Set("Content-Type", contentType)
	w.Write(file)
}
<commit_msg>Loopback addr<commit_after>package main

import (
	"fmt"
	"net"
	"net/http"
	"path"

	"github.com/naoina/denco"
	"github.com/pocke/hlog"
	"github.com/skratchdot/open-golang/open"
	"github.com/yosssi/ace"
)

// Server is HTTP server.
type Server struct {
	storage *Storage
}

func NewServer(port int) *Server {
	s := &Server{
		storage: NewStorage(),
	}

	wsm := NewWSManager(s.storage.OnUpdate())

	mux := denco.NewMux()
	f, err := mux.Build([]denco.Handler{
		mux.GET("/", s.indexHandler),
		mux.POST("/auth", s.authHandler),
		mux.GET("/files/*path", s.ServeFile),
		mux.GET("/ws", func(w http.ResponseWriter, r *http.Request, _ denco.Params) { wsm.ServeHTTP(w, r) }),
		mux.GET("/:type/:fname", s.serveAsset),
	})
	if err != nil {
		panic(err)
	}

	handler := f.ServeHTTP
	if DEBUG {
		handler = hlog.Wrap(f.ServeHTTP)
	}
	url, err := serve(handler, port)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Open: %s\n", url)
	open.Start(url)

	return s
}

func serve(f func(w http.ResponseWriter, r *http.Request), port int) (string, error) {
	p := fmt.Sprintf("127.0.0.1:%d", port)
	l, err := net.Listen("tcp", p)
	if err != nil {
		return "", err
	}
	url := fmt.Sprintf("http://%s", l.Addr())
	go func() {
		http.Serve(l, http.HandlerFunc(f))
	}()
	return url, nil
}

// ServeFile serves parsed markdown.
func (s *Server) ServeFile(w http.ResponseWriter, r *http.Request, p denco.Params) {
	path := p.Get("path")
	f, exist := s.storage.Get(path)
	if !exist {
		http.Error(w, fmt.Sprintf("%s page not found", path), http.StatusNotFound)
		return
	}
	if f.err != nil {
		http.Error(w, f.err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write([]byte(f.html))
}

// authHandler get and save GitHub access token.
func (s *Server) authHandler(w http.ResponseWriter, r *http.Request, _ denco.Params) {
	r.ParseForm()
	v := r.PostForm
	user := v.Get("username")
	pass := v.Get("password")

	err := s.storage.token.Init(user, pass)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	s.storage.AddAll()
	http.Redirect(w, r, "/", http.StatusFound)
}

func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request, _ denco.Params) {
	if s.storage.token.hasToken() {
		loadAce(w, "index", s.storage.Index())
	} else {
		loadAce(w, "before_auth", nil)
	}
}

func loadAce(w http.ResponseWriter, action string, data interface{}) {
	tpl, err := ace.Load("assets/base", "assets/"+action, &ace.Options{
		DynamicReload: DEBUG,
		Asset:         Asset,
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = tpl.Execute(w, data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func (_ *Server) serveAsset(w http.ResponseWriter, r *http.Request, p denco.Params) {
	t := p.Get("type")
	fname := p.Get("fname")
	file, err := Asset(path.Join("assets", t, fname))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var contentType string
	switch t {
	case "js":
		contentType = "application/javascript"
	case "css":
		contentType = "text/css"
	}
	w.Header().Set("Content-Type", contentType)
	w.Write(file)
}
<|endoftext|>


<commit_before>// Copyright 2016, Marc Lavergne <mlavergn@gmail.com>. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package goweb

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"godom"
	. "golog"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"crypto/tls"
	"strconv"
	"strings"
)

const (
	HTTP_GET  = "GET"
	HTTP_POST = "POST"
)

const (
	CONTENT_TYPE_DEFAULT     = ""
	CONTENT_TYPE_FORM        = "application/x-www-form-urlencoded"
	CONTENT_TYPE_JSON        = "application/json"
	CONTENT_TYPE_FORM_MULTI  = "multipart/form-data"
)

type HTTP struct {
	Method    string
	URL       *url.URL
	ProxyURL  *url.URL
	cookieJar http.CookieJar
	req       *http.Request
	resp      *http.Response
	Contents  string
}

//
// Constructor
//
func NewHTTP() *HTTP {
	self := &HTTP{}
	self.cookieJar, _ = cookiejar.New(nil)

	return self
}

//
// Tidy the URL such that it is minimally valid
//
func (self *HTTP) _tidyURL(urlString string) (err error) {
	LogDebugf("tidyURL input: %s", urlString)
	url, err := url.Parse(urlString)

	if self.URL == nil {
		if len(url.Scheme) == 0 {
			url.Scheme = "http"
		}

		self.URL = url
	} else {
		if len(url.Scheme) == 0 {
			url.Scheme = self.URL.Scheme
		}

		if len(url.Host) == 0 {
			url.Host = self.URL.Host
		}

		if !strings.HasPrefix(url.Path, "/") {
			components := strings.Split(self.URL.Path, "/")
			maxComponent := len(components)
			path := ""
			for i, component := range components {
				if i < maxComponent-1 {
					if len(component) > 0 {
						path += "/" + component
					}
				}
			}

			path += "/" + url.Path
			url.Path = path
		}

		LogDebugf("tidyURL putput: %s", url)
		self.URL = url
	}

	return err
}

func (self *HTTP) URLString() (urlString string) {
	urlString = fmt.Sprintf("%s", self.URL)

	return urlString
}

//
// Fetch: POST request
//
func (self *HTTP) Post(urlString string, args map[string]string) (result string) {
	content := _formatArgs(args)
	result =  self.PostContent(urlString, CONTENT_TYPE_DEFAULT, content)

	return result
}

func (self *HTTP) PostString(urlString string, contentType string, contentString string) (result string) {
	content := bytes.NewBuffer([]byte(contentString))
	result =  self.PostContent(urlString, contentType, content)

	return result
}

func (self *HTTP) PostData(urlString string, contentType string, contentBytes []byte) (result string) {
	content := bytes.NewBuffer(contentBytes)
	result =  self.PostContent(urlString, contentType, content)

	return result
}

//
// Fetch: POST request
//
func (self *HTTP) PostContent(urlString string, contentType string, content *bytes.Buffer) (result string) {
	self.Method = HTTP_POST
	self._tidyURL(urlString)

	result = self.prepareAndExecuteRequest(contentType, content)
	LogDebugf("\t%d", self.Status())

	return result
}

//
// Fetch: GET request
//
func (self *HTTP) Get(urlString string) (result string) {
	self.Method = HTTP_GET
	self._tidyURL(urlString)

	result = self.prepareAndExecuteRequest(CONTENT_TYPE_DEFAULT, nil)
	LogDebugf("\t%d", self.Status())

	return result
}

//
//
//
func _formatArgs(args map[string]string) (content *bytes.Buffer) {
	if len(args) > 0 {
		argString := ""
		for key, val := range args {
			argString += key + "=" + val + "&"
		}
		content = bytes.NewBuffer([]byte(argString))
	} else {
		content = bytes.NewBuffer([]byte(""))
	}

	return content
}

//
// Fetch: Prepare and execute HTTP request
// NOTE: This is the work horse, all requests filter through here
//
func (self *HTTP) prepareAndExecuteRequest(contentType string, content *bytes.Buffer) string {
	LogDebug(self.Method + ": " + self.URLString())

	RedirectAttemptedError := errors.New("redirect")

	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			// return http.ErrUseLastResponse -- Go 1.7+ only
			return RedirectAttemptedError
		},
		Jar: self.cookieJar,
	}

	// if we're proxying, we're going to disable the TLS cert verification
	if self.detectProxy() {
		self.ProxyURL, _ = url.Parse("http://127.0.0.1:8080")
		client.Transport = &http.Transport{
			Proxy: http.ProxyURL(self.ProxyURL),
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	self.req, _ = http.NewRequest(self.Method, self.URLString(), content)

	if len(contentType) > 0 {
		self.req.Header.Add("Content-Type", contentType)
	}
	self.req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50")
	// self.req.Header.Add("Referer", referrer)
	var err error
	self.resp, err = client.Do(self.req)

	if err != nil && !strings.HasSuffix(err.Error(), " redirect") {
		LogFatal(err)
	} else {
		bytes, _ := ioutil.ReadAll(self.resp.Body)
		self.Contents = string(bytes)
	}
	// defer s.resp.Body.Close()

	// at this point we have the request and response, save a record if configured
	output := "<!--\nMethod: " + self.Method + "\nURL: " + self.URLString() + "\nStatus: " + strconv.Itoa(self.Status()) + "\n-->\n\n" + self.Contents
	LogDumpFile("mnet", output)

	// handle redirects
	self.handleRedirection()

	return self.Contents
}

//
// Handler: proxies
// Assumes a local HTTP proxy is anything listening locally on port 8080
//
func (self *HTTP) detectProxy() bool {
	result := true

	conn, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		result = false
	} else {
		conn.Close()
	}

	return result
}

//
// Handler: redirections
//
func (self *HTTP) handleRedirection() string {
	var result string

	switch self.Status() {
	case 200:
		// OK
		if self.isHTML() {
			LogDebug("HTML detected")
			h := NewHTML()
			s := godom.NewDOM()
			s.SetContents(self.Contents)
			url := h.ParseRedirect(s)
			if len(url) > 0 {
				self.Get(url)
				result = self.handleRedirection()
			}
		} else if self.isJSON() {
			LogDebug("JSON detected")
			data := self.JSON()
			if len(data) > 0 {
				result = self.Contents
			}
		} else {
			LogDebug("Unhandled content type detected: " + self.ContentType())
			result = self.Contents
		}
		break
	case 302:
		// MOVED
		url := self.Location()
		self.Get(url)
		result = self.handleRedirection()
		break
	default:
		LogWarn("Unhandled status")
	}

	return result
}

//
// Contents: Determine if the contents are JSON
//
func (self *HTTP) isHTML() (result bool) {
	result = false
	if strings.HasPrefix(self.ContentType(), "text/html") {
		result = true
	}

	return result
}

//
// Contents: Determine if the contents are JSON
//
func (self *HTTP) isJSON() (result bool) {
	result = false
	if strings.HasPrefix(self.ContentType(), "application/json") {
		result = true
	}

	return result
}

//
// Contents: Determine if the contents are XML
//
func (self *HTTP) isXML() (result bool) {
	result = false
	if strings.HasPrefix(self.ContentType(), "text/xml") {
		result = true
	}

	return result
}

//
// Contents: Marshall contents from JSON to a map if possible
//
func (self *HTTP) JSON() map[string]interface{} {
	var result map[string]interface{}

	if self.isJSON() {
		bytes := []byte(self.Contents)
		json.Unmarshal(bytes, &result)
	}
	return result
}

//
// Header: Extract Content-Type
//
func (self *HTTP) ContentType() string {
	return self.getHeaderValue("Content-Type")
}

//
// Header: Extract Hosts
//
func (self *HTTP) Host() string {
	return self.getHeaderValue("Host")
}

//
// Header: Extract Status
//
func (self *HTTP) Status() int {
	return self.resp.StatusCode
}

//
// Header: Extract Location
//
func (self *HTTP) Location() string {
	return self.getHeaderValue("Location")
}

//
// Header: Extracts a header by key from the response
//
func (self *HTTP) getHeaderValue(key string) string {
	var result string = ""
	varr := self.resp.Header[key]
	if len(varr) > 0 {
		result = varr[0]
	}
	return result
}
<commit_msg>nils are bad, mmmmkay<commit_after>// Copyright 2016, Marc Lavergne <mlavergn@gmail.com>. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package goweb

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"godom"
	. "golog"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"crypto/tls"
	"strconv"
	"strings"
)

const (
	HTTP_GET  = "GET"
	HTTP_POST = "POST"
)

const (
	CONTENT_TYPE_DEFAULT     = ""
	CONTENT_TYPE_FORM        = "application/x-www-form-urlencoded"
	CONTENT_TYPE_JSON        = "application/json"
	CONTENT_TYPE_FORM_MULTI  = "multipart/form-data"
)

type HTTP struct {
	Method    string
	URL       *url.URL
	ProxyURL  *url.URL
	cookieJar http.CookieJar
	req       *http.Request
	resp      *http.Response
	Contents  string
}

//
// Constructor
//
func NewHTTP() *HTTP {
	self := &HTTP{}
	self.cookieJar, _ = cookiejar.New(nil)

	return self
}

//
// Tidy the URL such that it is minimally valid
//
func (self *HTTP) _tidyURL(urlString string) (err error) {
	LogDebugf("tidyURL input: %s", urlString)
	url, err := url.Parse(urlString)

	if self.URL == nil {
		if len(url.Scheme) == 0 {
			url.Scheme = "http"
		}

		self.URL = url
	} else {
		if len(url.Scheme) == 0 {
			url.Scheme = self.URL.Scheme
		}

		if len(url.Host) == 0 {
			url.Host = self.URL.Host
		}

		if !strings.HasPrefix(url.Path, "/") {
			components := strings.Split(self.URL.Path, "/")
			maxComponent := len(components)
			path := ""
			for i, component := range components {
				if i < maxComponent-1 {
					if len(component) > 0 {
						path += "/" + component
					}
				}
			}

			path += "/" + url.Path
			url.Path = path
		}

		LogDebugf("tidyURL putput: %s", url)
		self.URL = url
	}

	return err
}

func (self *HTTP) URLString() (urlString string) {
	urlString = fmt.Sprintf("%s", self.URL)

	return urlString
}

//
// Fetch: POST request
//
func (self *HTTP) Post(urlString string, args map[string]string) (result string) {
	content := _formatArgs(args)
	result =  self.PostContent(urlString, CONTENT_TYPE_DEFAULT, content)

	return result
}

func (self *HTTP) PostString(urlString string, contentType string, contentString string) (result string) {
	content := bytes.NewBuffer([]byte(contentString))
	result =  self.PostContent(urlString, contentType, content)

	return result
}

func (self *HTTP) PostData(urlString string, contentType string, contentBytes []byte) (result string) {
	content := bytes.NewBuffer(contentBytes)
	result =  self.PostContent(urlString, contentType, content)

	return result
}

//
// Fetch: POST request
//
func (self *HTTP) PostContent(urlString string, contentType string, content *bytes.Buffer) (result string) {
	self.Method = HTTP_POST
	self._tidyURL(urlString)

	result = self.prepareAndExecuteRequest(contentType, content)
	LogDebugf("\t%d", self.Status())

	return result
}

//
// Fetch: GET request
//
func (self *HTTP) Get(urlString string) (result string) {
	self.Method = HTTP_GET
	self._tidyURL(urlString)

	result = self.prepareAndExecuteRequest(CONTENT_TYPE_DEFAULT, nil)
	LogDebugf("\t%d", self.Status())

	return result
}

//
//
//
func _formatArgs(args map[string]string) (content *bytes.Buffer) {
	if len(args) > 0 {
		argString := ""
		for key, val := range args {
			argString += key + "=" + val + "&"
		}
		content = bytes.NewBuffer([]byte(argString))
	} else {
		content = bytes.NewBuffer([]byte(""))
	}

	return content
}

//
// Fetch: Prepare and execute HTTP request
// NOTE: This is the work horse, all requests filter through here
//
func (self *HTTP) prepareAndExecuteRequest(contentType string, content *bytes.Buffer) string {
	LogDebug(self.Method + ": " + self.URLString())

	RedirectAttemptedError := errors.New("redirect")

	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			// return http.ErrUseLastResponse -- Go 1.7+ only
			return RedirectAttemptedError
		},
		Jar: self.cookieJar,
	}

	// if we're proxying, we're going to disable the TLS cert verification
	if self.detectProxy() {
		self.ProxyURL, _ = url.Parse("http://127.0.0.1:8080")
		client.Transport = &http.Transport{
			Proxy: http.ProxyURL(self.ProxyURL),
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	// ensure we don't pass a nil ByteBuffer into http.NewRequest
	if content == nil {
		content = bytes.NewBuffer([]byte(""))
	}

	self.req, _ = http.NewRequest(self.Method, self.URLString(), content)

	if len(contentType) > 0 {
		self.req.Header.Add("Content-Type", contentType)
	}
	self.req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50")
	// self.req.Header.Add("Referer", referrer)
	var err error
	self.resp, err = client.Do(self.req)

	if err != nil && !strings.HasSuffix(err.Error(), " redirect") {
		LogFatal(err)
	} else {
		bytes, _ := ioutil.ReadAll(self.resp.Body)
		self.Contents = string(bytes)
	}
	// defer s.resp.Body.Close()

	// at this point we have the request and response, save a record if configured
	output := "<!--\nMethod: " + self.Method + "\nURL: " + self.URLString() + "\nStatus: " + strconv.Itoa(self.Status()) + "\n-->\n\n" + self.Contents
	LogDumpFile("mnet", output)

	// handle redirects
	self.handleRedirection()

	return self.Contents
}

//
// Handler: proxies
// Assumes a local HTTP proxy is anything listening locally on port 8080
//
func (self *HTTP) detectProxy() bool {
	result := true

	conn, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		result = false
	} else {
		conn.Close()
	}

	return result
}

//
// Handler: redirections
//
func (self *HTTP) handleRedirection() string {
	var result string

	switch self.Status() {
	case 200:
		// OK
		if self.isHTML() {
			LogDebug("HTML detected")
			h := NewHTML()
			s := godom.NewDOM()
			s.SetContents(self.Contents)
			url := h.ParseRedirect(s)
			if len(url) > 0 {
				self.Get(url)
				result = self.handleRedirection()
			}
		} else if self.isJSON() {
			LogDebug("JSON detected")
			data := self.JSON()
			if len(data) > 0 {
				result = self.Contents
			}
		} else {
			LogDebug("Unhandled content type detected: " + self.ContentType())
			result = self.Contents
		}
		break
	case 302:
		// MOVED
		url := self.Location()
		self.Get(url)
		result = self.handleRedirection()
		break
	default:
		LogWarn("Unhandled status")
	}

	return result
}

//
// Contents: Determine if the contents are JSON
//
func (self *HTTP) isHTML() (result bool) {
	result = false
	if strings.HasPrefix(self.ContentType(), "text/html") {
		result = true
	}

	return result
}

//
// Contents: Determine if the contents are JSON
//
func (self *HTTP) isJSON() (result bool) {
	result = false
	if strings.HasPrefix(self.ContentType(), "application/json") {
		result = true
	}

	return result
}

//
// Contents: Determine if the contents are XML
//
func (self *HTTP) isXML() (result bool) {
	result = false
	if strings.HasPrefix(self.ContentType(), "text/xml") {
		result = true
	}

	return result
}

//
// Contents: Marshall contents from JSON to a map if possible
//
func (self *HTTP) JSON() map[string]interface{} {
	var result map[string]interface{}

	if self.isJSON() {
		bytes := []byte(self.Contents)
		json.Unmarshal(bytes, &result)
	}
	return result
}

//
// Header: Extract Content-Type
//
func (self *HTTP) ContentType() string {
	return self.getHeaderValue("Content-Type")
}

//
// Header: Extract Hosts
//
func (self *HTTP) Host() string {
	return self.getHeaderValue("Host")
}

//
// Header: Extract Status
//
func (self *HTTP) Status() int {
	return self.resp.StatusCode
}

//
// Header: Extract Location
//
func (self *HTTP) Location() string {
	return self.getHeaderValue("Location")
}

//
// Header: Extracts a header by key from the response
//
func (self *HTTP) getHeaderValue(key string) string {
	var result string = ""
	varr := self.resp.Header[key]
	if len(varr) > 0 {
		result = varr[0]
	}
	return result
}
<|endoftext|>


<commit_before>package hugo

import (
	"net/http"

	"github.com/spf13/hugo/commands"

	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
)

// Setup function
func Setup(c *setup.Controller) (middleware.Middleware, error) {
	commands.Execute()

	return func(next middleware.Handler) middleware.Handler {
		return &handler{
			Next: next,
		}
	}, nil
}

type handler struct{ Next middleware.Handler }
type adminHandler struct{}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// do path matching
	if middleware.Path(r.URL.Path).Matches("/admin") {
		a := new(adminHandler)
		return a.ServeHTTP(w, r)
	}

	return h.Next.ServeHTTP(w, r)
}

func (a adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	w.Write([]byte("Admin area"))
	return 200, nil
}
<commit_msg>updates<commit_after>package hugo

import (
	"net/http"
	"strings"

	"github.com/spf13/hugo/commands"

	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
)

// Setup function
func Setup(c *setup.Controller) (middleware.Middleware, error) {
	commands.Execute()

	return func(next middleware.Handler) middleware.Handler {
		return &handler{Next: next}
	}, nil
}

type handler struct{ Next middleware.Handler }

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	if middleware.Path(r.URL.Path).Matches("/admin") {

		if middleware.Path(r.URL.Path).Matches("/admin/new") {
			w.Write([]byte("New"))
		} else if middleware.Path(r.URL.Path).Matches("/admin/edit") {
			var fileName string

			fileName = strings.Replace(r.URL.Path, "/admin/edit", "", 1)

			w.Write([]byte("Edit " + fileName))
		} else {
			w.Write([]byte("Admin"))
		}

		return 200, nil
	}

	return h.Next.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package httpServerStoppableListener
// Taken from https://github.com/hydrogen18/stoppableListener/blob/master/example/example.go

import (
	"errors"
	"net"
	"time"
)

type StoppableListener struct {
	*net.TCPListener          //Wrapped listener
	stop             chan int //Channel used only to indicate listener should shutdown
}

func New(l net.Listener) (*StoppableListener, error) {
	tcpL, ok := l.(*net.TCPListener)

	if !ok {
		return nil, errors.New("Cannot wrap listener")
	}

	retval := &StoppableListener{}
	retval.TCPListener = tcpL
	retval.stop = make(chan int)

	return retval, nil
}

var StoppedError = errors.New("Listener stopped")

func (sl *StoppableListener) Accept() (net.Conn, error) {

	for {
		//Wait up to one second for a new connection
		sl.SetDeadline(time.Now().Add(time.Second))

		newConn, err := sl.TCPListener.Accept()

		//Check for the channel being closed
		select {
		case <-sl.stop:
			return nil, StoppedError
		default:
		//If the channel is still open, continue as normal
		}

		if err != nil {
			netErr, ok := err.(net.Error)

			//If this is a timeout, then continue to wait for
			//new connections
			if ok && netErr.Timeout() && netErr.Temporary() {
				continue
			}
		}

		return newConn, err
	}
}

func (sl *StoppableListener) Stop() {
	close(sl.stop)
}<commit_msg>BUGFIX: (test) ensure channel is closed<commit_after>package httpServerStoppableListener
// Taken from https://github.com/hydrogen18/stoppableListener/blob/master/example/example.go

import (
	"errors"
	"net"
	"time"
)

type StoppableListener struct {
	*net.TCPListener          //Wrapped listener
	stop             chan int //Channel used only to indicate listener should shutdown
	isStopChanClosed bool
}

func New(l net.Listener) (*StoppableListener, error) {
	tcpL, ok := l.(*net.TCPListener)

	if !ok {
		return nil, errors.New("Cannot wrap listener")
	}

	retval := &StoppableListener{}
	retval.TCPListener = tcpL
	retval.stop = make(chan int)

	return retval, nil
}

var StoppedError = errors.New("Listener stopped")

func (sl *StoppableListener) Accept() (net.Conn, error) {

	for {
		//Wait up to one second for a new connection
		sl.SetDeadline(time.Now().Add(time.Second))

		newConn, err := sl.TCPListener.Accept()

		//Check for the channel being closed
		select {
		case <-sl.stop:
			return nil, StoppedError
		default:
		//If the channel is still open, continue as normal
		}

		if err != nil {
			netErr, ok := err.(net.Error)

			//If this is a timeout, then continue to wait for
			//new connections
			if ok && netErr.Timeout() && netErr.Temporary() {
				continue
			}
		}

		return newConn, err
	}
}

func (sl *StoppableListener) Stop() {
	if (!sl.isStopChanClosed) {
		close(sl.stop)
	}

	sl.isStopChanClosed = true
}<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"html/template"

	"golang.org/x/text/language"
)

var translatePrio = []language.Tag{
	language.AmericanEnglish,
	language.Japanese,
}

var translateData = map[language.Tag]map[string]string{
	language.AmericanEnglish: {},
	language.Japanese: {
		"lang":                        "ja",
		"Preferences":                 "環境設定",
		"Database":                    "データベース",
		"Outputs":                     "出力",
		"Information":                 "情報",
		"ReloadApplication":           "再読み込み",
		"Appearance":                  "外観の設定",
		"General":                     "一般",
		"Theme":                       "テーマ",
		"ThemeLight":                  "ライト",
		"ThemeDark":                   "ダーク",
		"ThemeSystem":                 "システムに合わせる",
		"ThemeCoverArt":               "カバーアートに合わせる",
		"CoverArtColorThreshold":      "色閾値",
		"Animation":                   "アニメーション",
		"BackgroundImage":             "背景画像を表示",
		"BackgroundImageBlur":         "背景のブラーエフェクト",
		"CircledImage":                "カバーアートを丸く表示",
		"CrossfadingImage":            "画像のクロスフェード",
		"GridviewAlbum":               "アルバム一覧をグリッド表示",
		"AutoHideScrollbar":           "自動的にスクロールバーを非表示",
		"Playlist":                    "プレイリスト",
		"PlaybackRange":               "再生範囲",
		"PlayAllTracks":               "すべての曲を再生",
		"PlaySelectedList":            "現在のリストを再生",
		"PlaybackRangeHelp":           "次曲選択以降に有効になります。",
		"PlayCustomList":              "カスタム",
		"playbackTreeLabelPrefix":     "",
		"playbackTreeLabelSuffix":     "ツリー",
		"allTracks":                   "すべての曲",
		"Library":                     "ライブラリ",
		"Rescan":                      "ライブラリを再読み込み",
		"Songs":                       "曲",
		"RescanSongs":                 "再読み込み",
		"CoverArt":                    "カバーアート",
		"RescanCoverArt":              "再読み込み",
		"Playback":                    "再生",
		"ListviewFollowsPlayback":     "次に再生する曲に自動で移動",
		"Volume":                      "音量",
		"ShowVolumeNob":               "音量バーを表示",
		"MaxVolume":                   "音量の最大値",
		"Configure":                   "設定",
		"Devices":                     "デバイス",
		"DeviceDoPHelp":               "DSDストリームをPCMフレームに乗せて送信し、受信側のDACでDSDストリームに戻します。DoPに対応していないDACでは使用しないでください",
		"DeviceAllowedFormats":        "出力フォーマット",
		"DeviceAllowedFormatsHelp":    "出力を許可するフォーマットを指定します",
		"AllowedFormats":              "出力フォーマット",
		"AllowedFormatsAuto":          "自動",
		"AllowedFormatsCustom":        "カスタム",
		"Tracks":                      "トラック数:",
		"Artists":                     "アーティスト数:",
		"Albums":                      "アルバム数:",
		"TotalLength":                 "総トラック時間:",
		"TotalPlaytime":               "総再生時間:",
		"Uptime":                      "起動時間:",
		"LastLibraryUpdate":           "最終更新:",
		"Websockets":                  "Websocket接続数",
		"Storage":                     "ストレージ",
		"UnmountStorage":              "削除",
		"MountStorage":                "ストレージを追加",
		"StoragePath":                 "ストレージ名",
		"StorageURI":                  "URI",
		"Options":                     "オプション",
		"ReplayGain":                  "リプレイゲイン",
		"ReplayGainOff":               "オフ",
		"ReplayGainTrack":             "トラック",
		"ReplayGainAlbum":             "アルバム",
		"Crossfade":                   "クロスフェード",
		"ClientOutput":                "クライアント出力",
		"ClientOutputSource":          "ソース",
		"ClientOutputSourceHelp":      "HTTP出力を再生します",
		"HTTPStreamDisabled":          "無効",
		"HTTPStreamVolume":            "音量",
		"ThisApplication":             "このアプリケーションについて",
		"Compiler":                    "コンパイラ",
		"BSD3ClauseLicense":           "修正BSDライセンス",
		"Renderer":                    "ブラウザ",
		"Credits":                     "クレジット",
		"MITLicense":                  "MIT ライセンス",
		"BSD2ClauseLicense":           "二条項BSDライセンス",
		"Help":                        "ヘルプ",
		"KeyboardShortcuts":           "キーボード・ショートカット",
		"PlayOrPauseSong":             "再生・一時停止",
		"GoToNextSong":                "次の曲",
		"GoToPreviousSong":            "前の曲",
		"MoveListViewCursor":          "カーソル移動",
		"ActivateListViewCursorItem":  "カーソル上の曲を再生/下のディレクトリへ移動",
		"ShowListParent":              "上のディレクトリへ移動",
		"ShowNowPlayingItem":          "現在再生中の曲へ移動",
		"ShowThisHelp":                "このヘルプを表示",
		"ariaLabelBackTo":             "%s 一覧に移動",
		"ariaLabelShowNowPlayingItem": "現在再生中の曲へ移動",
		"ariaLabelShowSettingsWindow": "設定ウィンドウを表示",
		"ariaLabelCloseThisWindow":    "ウィンドウを閉じる",
		"ariaLabelGoToPreviousSong":   "前の曲に戻る",
		"ariaLabelGoToNextSong":       "次の曲を再生",
		"ariaLabelPauseSong":          "一時停止",
		"ariaLabelPlaySong":           "再生",
		"ariaLabelTurnOnRepeat":       "リピート再生を有効にする",
		"ariaLabelTurnOnRepeat1":      "1曲リピート再生を有効にする",
		"ariaLabelTurnOffRepeat":      "リピート再生を無効にする",
		"ariaLabelTurnOnRandom":       "ランダム再生を有効にする",
		"ariaLabelTurnOffRandom":      "ランダム再生を無効にする",
		"titleFormatBackTo":           "%s に戻る",
		"titleShowNowPlayingItem":     "現在再生中の曲へ移動",
		"titleShowSettingsWindow":     "設定",
		"titleClose":                  "閉じる",
		"titlePrevious":               "前の曲",
		"titlePlayOrPause":            "再生・一時停止",
		"titleNext":                   "次の曲",
		"titleRepeat":                 "リピート",
		"titleRandom":                 "ランダム",
		"SongInfoTitle":               "名前",
		"SongInfoArtist":              "アーティスト",
		"SongInfoAlbum":               "アルバム",
		"SongInfoAlbumArtist":         "アルバムアーティスト",
		"SongInfoComposer":            "作曲者",
		"SongInfoPerformer":           "演奏者",
		"SongInfoDate":                "日付",
		"SongInfoDisc":                "ディスク",
		"SongInfoTrack":               "トラック",
		"SongInfoLength":              "時間",
		"SongInfoGenre":               "ジャンル",
		"NotifyNetwork":               "ネットワーク",
		"NotifyNetworkTimeoutRetry":   "タイムアウト. 再接続中...",
		"NotifyNetworkTimeout":        "タイムアウト",
		"NotifyNetworkClosed":         "再接続中...",
		"NotifyNetworkDoesNotRespond": "再接続中...",
	},
}

func translate(html []byte, lang language.Tag, extra map[string]string) ([]byte, error) {
	t, err := template.New("webpage").Parse(string(html))
	if err != nil {
		return nil, err
	}
	buf := new(bytes.Buffer)
	m, ok := translateData[lang]
	if ok {
		for k, v := range extra {
			m[k] = v
		}
	} else {
		m = extra
	}
	err = t.Execute(buf, m)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
<commit_msg>add ja translation for NotifyLibrary and NotifyCoverArt<commit_after>package main

import (
	"bytes"
	"html/template"

	"golang.org/x/text/language"
)

var translatePrio = []language.Tag{
	language.AmericanEnglish,
	language.Japanese,
}

var translateData = map[language.Tag]map[string]string{
	language.AmericanEnglish: {},
	language.Japanese: {
		"lang":                        "ja",
		"Preferences":                 "環境設定",
		"Database":                    "データベース",
		"Outputs":                     "出力",
		"Information":                 "情報",
		"ReloadApplication":           "再読み込み",
		"Appearance":                  "外観の設定",
		"General":                     "一般",
		"Theme":                       "テーマ",
		"ThemeLight":                  "ライト",
		"ThemeDark":                   "ダーク",
		"ThemeSystem":                 "システムに合わせる",
		"ThemeCoverArt":               "カバーアートに合わせる",
		"CoverArtColorThreshold":      "色閾値",
		"Animation":                   "アニメーション",
		"BackgroundImage":             "背景画像を表示",
		"BackgroundImageBlur":         "背景のブラーエフェクト",
		"CircledImage":                "カバーアートを丸く表示",
		"CrossfadingImage":            "画像のクロスフェード",
		"GridviewAlbum":               "アルバム一覧をグリッド表示",
		"AutoHideScrollbar":           "自動的にスクロールバーを非表示",
		"Playlist":                    "プレイリスト",
		"PlaybackRange":               "再生範囲",
		"PlayAllTracks":               "すべての曲を再生",
		"PlaySelectedList":            "現在のリストを再生",
		"PlaybackRangeHelp":           "次曲選択以降に有効になります。",
		"PlayCustomList":              "カスタム",
		"playbackTreeLabelPrefix":     "",
		"playbackTreeLabelSuffix":     "ツリー",
		"allTracks":                   "すべての曲",
		"Library":                     "ライブラリ",
		"Rescan":                      "ライブラリを再読み込み",
		"Songs":                       "曲",
		"RescanSongs":                 "再読み込み",
		"CoverArt":                    "カバーアート",
		"RescanCoverArt":              "再読み込み",
		"Playback":                    "再生",
		"ListviewFollowsPlayback":     "次に再生する曲に自動で移動",
		"Volume":                      "音量",
		"ShowVolumeNob":               "音量バーを表示",
		"MaxVolume":                   "音量の最大値",
		"Configure":                   "設定",
		"Devices":                     "デバイス",
		"DeviceDoPHelp":               "DSDストリームをPCMフレームに乗せて送信し、受信側のDACでDSDストリームに戻します。DoPに対応していないDACでは使用しないでください",
		"DeviceAllowedFormats":        "出力フォーマット",
		"DeviceAllowedFormatsHelp":    "出力を許可するフォーマットを指定します",
		"AllowedFormats":              "出力フォーマット",
		"AllowedFormatsAuto":          "自動",
		"AllowedFormatsCustom":        "カスタム",
		"Tracks":                      "トラック数:",
		"Artists":                     "アーティスト数:",
		"Albums":                      "アルバム数:",
		"TotalLength":                 "総トラック時間:",
		"TotalPlaytime":               "総再生時間:",
		"Uptime":                      "起動時間:",
		"LastLibraryUpdate":           "最終更新:",
		"Websockets":                  "Websocket接続数",
		"Storage":                     "ストレージ",
		"UnmountStorage":              "削除",
		"MountStorage":                "ストレージを追加",
		"StoragePath":                 "ストレージ名",
		"StorageURI":                  "URI",
		"Options":                     "オプション",
		"ReplayGain":                  "リプレイゲイン",
		"ReplayGainOff":               "オフ",
		"ReplayGainTrack":             "トラック",
		"ReplayGainAlbum":             "アルバム",
		"Crossfade":                   "クロスフェード",
		"ClientOutput":                "クライアント出力",
		"ClientOutputSource":          "ソース",
		"ClientOutputSourceHelp":      "HTTP出力を再生します",
		"HTTPStreamDisabled":          "無効",
		"HTTPStreamVolume":            "音量",
		"ThisApplication":             "このアプリケーションについて",
		"Compiler":                    "コンパイラ",
		"BSD3ClauseLicense":           "修正BSDライセンス",
		"Renderer":                    "ブラウザ",
		"Credits":                     "クレジット",
		"MITLicense":                  "MIT ライセンス",
		"BSD2ClauseLicense":           "二条項BSDライセンス",
		"Help":                        "ヘルプ",
		"KeyboardShortcuts":           "キーボード・ショートカット",
		"PlayOrPauseSong":             "再生・一時停止",
		"GoToNextSong":                "次の曲",
		"GoToPreviousSong":            "前の曲",
		"MoveListViewCursor":          "カーソル移動",
		"ActivateListViewCursorItem":  "カーソル上の曲を再生/下のディレクトリへ移動",
		"ShowListParent":              "上のディレクトリへ移動",
		"ShowNowPlayingItem":          "現在再生中の曲へ移動",
		"ShowThisHelp":                "このヘルプを表示",
		"ariaLabelBackTo":             "%s 一覧に移動",
		"ariaLabelShowNowPlayingItem": "現在再生中の曲へ移動",
		"ariaLabelShowSettingsWindow": "設定ウィンドウを表示",
		"ariaLabelCloseThisWindow":    "ウィンドウを閉じる",
		"ariaLabelGoToPreviousSong":   "前の曲に戻る",
		"ariaLabelGoToNextSong":       "次の曲を再生",
		"ariaLabelPauseSong":          "一時停止",
		"ariaLabelPlaySong":           "再生",
		"ariaLabelTurnOnRepeat":       "リピート再生を有効にする",
		"ariaLabelTurnOnRepeat1":      "1曲リピート再生を有効にする",
		"ariaLabelTurnOffRepeat":      "リピート再生を無効にする",
		"ariaLabelTurnOnRandom":       "ランダム再生を有効にする",
		"ariaLabelTurnOffRandom":      "ランダム再生を無効にする",
		"titleFormatBackTo":           "%s に戻る",
		"titleShowNowPlayingItem":     "現在再生中の曲へ移動",
		"titleShowSettingsWindow":     "設定",
		"titleClose":                  "閉じる",
		"titlePrevious":               "前の曲",
		"titlePlayOrPause":            "再生・一時停止",
		"titleNext":                   "次の曲",
		"titleRepeat":                 "リピート",
		"titleRandom":                 "ランダム",
		"SongInfoTitle":               "名前",
		"SongInfoArtist":              "アーティスト",
		"SongInfoAlbum":               "アルバム",
		"SongInfoAlbumArtist":         "アルバムアーティスト",
		"SongInfoComposer":            "作曲者",
		"SongInfoPerformer":           "演奏者",
		"SongInfoDate":                "日付",
		"SongInfoDisc":                "ディスク",
		"SongInfoTrack":               "トラック",
		"SongInfoLength":              "時間",
		"SongInfoGenre":               "ジャンル",
		"NotifyNetwork":               "ネットワーク",
		"NotifyNetworkTimeoutRetry":   "タイムアウト. 再接続中...",
		"NotifyNetworkTimeout":        "タイムアウト",
		"NotifyNetworkClosed":         "再接続中...",
		"NotifyNetworkDoesNotRespond": "再接続中...",
		"NotifyLibrary":               "ライブラリ",
		"NotifyLibraryUpdating":       "更新中...",
		"NotifyLibraryUpdated":        "更新済み",
		"NotifyCoverArt":              "カバーアート",
		"NotifyCoverArtUpdating":      "更新中...",
		"NotifyCoverArtUpdated":       "更新済み",
	},
}

func translate(html []byte, lang language.Tag, extra map[string]string) ([]byte, error) {
	t, err := template.New("webpage").Parse(string(html))
	if err != nil {
		return nil, err
	}
	buf := new(bytes.Buffer)
	m, ok := translateData[lang]
	if ok {
		for k, v := range extra {
			m[k] = v
		}
	} else {
		m = extra
	}
	err = t.Execute(buf, m)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
<|endoftext|>


<commit_before>package searchAPI

import (
	"github.com/eliothedeman/common-practice/search"
)

var (
	HANDLERS map[string]APIHandler
	DBS      map[string]*search.SearchDb
)

// setup global vars
func init() {
	// set up handlers
	HANDLERS = make(map[string]APIHandler)
	HANDLERS["insert"] = Insert
	HANDLERS["select"] = Select
	// setup SearchDbs
	DBS = make(map[string]*search.SearchDb)
	// absolute
	DBS["a-pitch"] = search.NewSearchDb("a-pitch")
	DBS["a-rhythm"] = search.NewSearchDb("a-rhythm")
	// realitive
	DBS["r-pitch"] = search.NewSearchDb("r-pitch")
	DBS["r-rhythm"] = search.NewSearchDb("r-rhythm")
	DBS["rhythm"] = search.NewSearchDb("rhythm")
	DBS["key"] = search.NewSearchDb("key")
}
<commit_msg>updated global docs<commit_after>package searchAPI

import (
	"github.com/eliothedeman/common-practice/search"
)

var (
	HANDLERS map[string]APIHandler
	DBS      map[string]*search.SearchDb
)

// setup global vars
func init() {
	// set up handlers
	HANDLERS = make(map[string]APIHandler)
	HANDLERS["insert"] = Insert
	HANDLERS["select"] = Select
	// setup SearchDbs
	DBS = make(map[string]*search.SearchDb)
	// absolute
	DBS["a-pitch"] = search.NewSearchDb("a-pitch")
	DBS["a-rhythm"] = search.NewSearchDb("a-rhythm")
	// realitive
	DBS["r-pitch"] = search.NewSearchDb("r-pitch")
	DBS["r-rhythm"] = search.NewSearchDb("r-rhythm")
	// single
	DBS["rhythm"] = search.NewSearchDb("rhythm")
	DBS["key"] = search.NewSearchDb("key")
}
<|endoftext|>


<commit_before>package putio

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	defaultUserAgent = "go-putio"
	defaultMediaType = "application/json"
	defaultBaseURL   = "https://api.put.io"
	defaultUploadURL = "https://upload.put.io"
)

// Client manages communication with Put.io v2 API.
type Client struct {
	// HTTP client used to communicate with Put.io API
	client *http.Client

	// Base URL for API requests
	BaseURL *url.URL

	// base url for upload requests
	uploadURL *url.URL

	// User agent for client
	UserAgent string

	// ExtraHeaders are passed to the API server on every request.
	ExtraHeaders http.Header

	// Services used for communicating with the API
	Account   *AccountService
	Files     *FilesService
	Transfers *TransfersService
	Zips      *ZipsService
	Friends   *FriendsService
	Events    *EventsService
}

// NewClient returns a new Put.io API client, using the htttpClient, which must
// be a new Oauth2 enabled http.Client. If httpClient is not defined, default
// HTTP client is used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	baseURL, _ := url.Parse(defaultBaseURL)
	uploadURL, _ := url.Parse(defaultUploadURL)
	c := &Client{
		client:       httpClient,
		BaseURL:      baseURL,
		uploadURL:    uploadURL,
		UserAgent:    defaultUserAgent,
		ExtraHeaders: make(http.Header),
	}

	c.Account = &AccountService{client: c}
	c.Files = &FilesService{client: c}
	c.Transfers = &TransfersService{client: c}
	c.Zips = &ZipsService{client: c}
	c.Friends = &FriendsService{client: c}
	c.Events = &EventsService{client: c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided via
// relURL, which will be resolved to the BaseURL of the Client.
func (c *Client) NewRequest(ctx context.Context, method, relURL string, body io.Reader) (*http.Request, error) {
	rel, err := url.Parse(relURL)
	if err != nil {
		return nil, err
	}

	var u *url.URL
	// XXX: workaroud for upload endpoint. upload method has a different base url,
	// so we've a special case for testing purposes.
	if relURL == "/v2/files/upload" {
		u = c.uploadURL.ResolveReference(rel)
	} else {
		u = c.BaseURL.ResolveReference(rel)
	}

	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return nil, err
	}

	if ctx == nil {
		ctx = context.Background()
	}
	req = req.WithContext(ctx)

	req.Header.Set("Accept", defaultMediaType)
	req.Header.Set("User-Agent", c.UserAgent)

	// merge headers with extra headers
	for header, values := range c.ExtraHeaders {
		for _, value := range values {
			req.Header.Add(header, value)
		}
	}

	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred. Response body is closed at all cases except
// v is nil. If v is nil, response body is not closed and the body can be used
// for streaming.
func (c *Client) Do(r *http.Request, v interface{}) (*http.Response, error) {
	resp, err := c.client.Do(r)
	if err != nil {
		return nil, err
	}

	err = checkResponse(resp)
	if err != nil {
		// close the body at all times if there is an http error
		resp.Body.Close()
		return resp, err
	}

	if v == nil {
		return resp, nil
	}

	// close the body for all cases from here
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(v)
	if err != nil {
		return resp, err
	}

	return resp, nil
}

// checkResponse is the entrypoint to reading the API response. If the response
// status code is not in success range, it will try to return a structured
// error.
func checkResponse(r *http.Response) error {
	status := r.StatusCode
	switch {
	case status >= 200 && status <= 399:
		return nil
	case status >= 400 && status <= 599:
		// server returns json
	default:
		return fmt.Errorf("unexpected status code: %d", status)
	}
	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return fmt.Errorf("body read error: %s. status: %v. Details: %v:", err, status, string(data[:250]))
	}
	if len(data) > 0 {
		err = json.Unmarshal(data, errorResponse)
		if err != nil {
			return fmt.Errorf("json decod error: %s. status: %v. Details: %v:", err, status, string(data[:250]))
		}
	}
	return errorResponse
}
<commit_msg>add Client.ValidateToken method<commit_after>package putio

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	defaultUserAgent = "go-putio"
	defaultMediaType = "application/json"
	defaultBaseURL   = "https://api.put.io"
	defaultUploadURL = "https://upload.put.io"
)

// Client manages communication with Put.io v2 API.
type Client struct {
	// HTTP client used to communicate with Put.io API
	client *http.Client

	// Base URL for API requests
	BaseURL *url.URL

	// base url for upload requests
	uploadURL *url.URL

	// User agent for client
	UserAgent string

	// ExtraHeaders are passed to the API server on every request.
	ExtraHeaders http.Header

	// Services used for communicating with the API
	Account   *AccountService
	Files     *FilesService
	Transfers *TransfersService
	Zips      *ZipsService
	Friends   *FriendsService
	Events    *EventsService
}

// NewClient returns a new Put.io API client, using the htttpClient, which must
// be a new Oauth2 enabled http.Client. If httpClient is not defined, default
// HTTP client is used.
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	baseURL, _ := url.Parse(defaultBaseURL)
	uploadURL, _ := url.Parse(defaultUploadURL)
	c := &Client{
		client:       httpClient,
		BaseURL:      baseURL,
		uploadURL:    uploadURL,
		UserAgent:    defaultUserAgent,
		ExtraHeaders: make(http.Header),
	}

	c.Account = &AccountService{client: c}
	c.Files = &FilesService{client: c}
	c.Transfers = &TransfersService{client: c}
	c.Zips = &ZipsService{client: c}
	c.Friends = &FriendsService{client: c}
	c.Events = &EventsService{client: c}

	return c
}

func (c *Client) ValidateToken(ctx context.Context) (ok bool, err error) {
	req, err := c.NewRequest(ctx, "GET", "/v2/oauth2/validate", nil)
	if err != nil {
		return
	}
	var r struct {
		Result bool `json:"result"`
	}
	_, err = c.Do(req, &r)
	return r.Result, err
}

// NewRequest creates an API request. A relative URL can be provided via
// relURL, which will be resolved to the BaseURL of the Client.
func (c *Client) NewRequest(ctx context.Context, method, relURL string, body io.Reader) (*http.Request, error) {
	rel, err := url.Parse(relURL)
	if err != nil {
		return nil, err
	}

	var u *url.URL
	// XXX: workaroud for upload endpoint. upload method has a different base url,
	// so we've a special case for testing purposes.
	if relURL == "/v2/files/upload" {
		u = c.uploadURL.ResolveReference(rel)
	} else {
		u = c.BaseURL.ResolveReference(rel)
	}

	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return nil, err
	}

	if ctx == nil {
		ctx = context.Background()
	}
	req = req.WithContext(ctx)

	req.Header.Set("Accept", defaultMediaType)
	req.Header.Set("User-Agent", c.UserAgent)

	// merge headers with extra headers
	for header, values := range c.ExtraHeaders {
		for _, value := range values {
			req.Header.Add(header, value)
		}
	}

	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred. Response body is closed at all cases except
// v is nil. If v is nil, response body is not closed and the body can be used
// for streaming.
func (c *Client) Do(r *http.Request, v interface{}) (*http.Response, error) {
	resp, err := c.client.Do(r)
	if err != nil {
		return nil, err
	}

	err = checkResponse(resp)
	if err != nil {
		// close the body at all times if there is an http error
		resp.Body.Close()
		return resp, err
	}

	if v == nil {
		return resp, nil
	}

	// close the body for all cases from here
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(v)
	if err != nil {
		return resp, err
	}

	return resp, nil
}

// checkResponse is the entrypoint to reading the API response. If the response
// status code is not in success range, it will try to return a structured
// error.
func checkResponse(r *http.Response) error {
	status := r.StatusCode
	switch {
	case status >= 200 && status <= 399:
		return nil
	case status >= 400 && status <= 599:
		// server returns json
	default:
		return fmt.Errorf("unexpected status code: %d", status)
	}
	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return fmt.Errorf("body read error: %s. status: %v. Details: %v:", err, status, string(data[:250]))
	}
	if len(data) > 0 {
		err = json.Unmarshal(data, errorResponse)
		if err != nil {
			return fmt.Errorf("json decod error: %s. status: %v. Details: %v:", err, status, string(data[:250]))
		}
	}
	return errorResponse
}
<|endoftext|>