<commit_before>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dbf

import (
	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// global auxiliary variables
var (
	G_extraindent string // extra indentation
)

// P holds material parameter names and values
type P struct {

	// input
	N      string  `json:"n"`      // name of parameter
	V      float64 `json:"v"`      // value of parameter
	Min    float64 `json:"min"`    // min value
	Max    float64 `json:"max"`    // max value
	S      float64 `json:"s"`      // standard deviation
	D      string  `json:"d"`      // probability distribution type
	U      string  `json:"u"`      // unit (not verified)
	Adj    int     `json:"adj"`    // adjustable: unique ID (greater than zero)
	Dep    int     `json:"dep"`    // depends on "adj"
	Extra  string  `json:"extra"`  // extra data
	Inact  bool    `json:"inact"`  // parameter is inactive in optimisation
	SetDef bool    `json:"setdef"` // tells model to use a default value

	// auxiliary
	Fcn   T  // a function y=f(t,x)
	Other *P // dependency: connected parameter

	// derived
	conn []*float64 // connected variables to V
}

// Connect connects parameter to variable
func (o *P) Connect(V *float64) {
	o.conn = append(o.conn, V)
	*V = o.V
}

// Set sets parameter, including connected variables
func (o *P) Set(V float64) {
	o.V = V
	for _, v := range o.conn {
		*v = V
	}
}

// Params holds many parameters
type Params []*P

// Find finds a parameter by name
//  Note: returns nil if not found
func (o *Params) Find(name string) *P {
	for _, p := range *o {
		if p.N == name {
			return p
		}
	}
	return nil
}

// CheckLimits check limits of variables given in Min/Max
// Will panic if values are outside corresponding Min/Max range.
func (o *Params) CheckLimits() {
	for _, p := range *o {
		if p.V < p.Min {
			chk.Panic("parameter %q has value smaller than minimum. %v < %v is not aceptable", p.N, p.V, p.Min)
		}
		if p.V > p.Max {
			chk.Panic("parameter %q has value greater than maximum. %v > %v is not aceptable", p.N, p.V, p.Max)
		}
	}
}

// GetValues get parameter values
func (o *Params) GetValues(names []string) (values []float64, found []bool) {
	n := len(names)
	values = make([]float64, n)
	found = make([]bool, n)
	for i, name := range names {
		prm := o.Find(name)
		if prm != nil {
			values[i] = prm.V
			found[i] = true
		}
	}
	return
}

// CheckAndGetValues check min/max limits and return values.
// Will panic if values are outside corresponding min/max range.
// Will also panic if a parameter name is not found.
func (o *Params) CheckAndGetValues(names []string) (values []float64) {
	n := len(names)
	values = make([]float64, n)
	for i, name := range names {
		prm := o.Find(name)
		if prm == nil {
			chk.Panic("cannot find parameter named %q", name)
		}
		if prm.V < prm.Min {
			chk.Panic("parameter %q has value smaller than minimum. %v < %v is not aceptable", name, prm.V, prm.Min)
		}
		if prm.V > prm.Max {
			chk.Panic("parameter %q has value greater than maximum. %v > %v is not aceptable", name, prm.V, prm.Max)
		}
		values[i] = prm.V
	}
	return
}

// CheckAndSetVars get parameter values and check limits defined in Min and Max
// Will panic if values are outside corresponding Min/Max range.
// Will also panic if a parameter name is not found.
func (o *Params) CheckAndSetVars(names []string, variables []*float64) {
	n := len(names)
	if len(variables) != n {
		chk.Panic("array of variables must have the same size as the slice of names. %d != %d", len(variables), n)
	}
	for i, name := range names {
		prm := o.Find(name)
		if prm == nil {
			chk.Panic("cannot find parameter named %q", name)
		}
		if prm.V < prm.Min {
			chk.Panic("parameter %q has value smaller than minimum. %v < %v is not aceptable", name, prm.V, prm.Min)
		}
		if prm.V > prm.Max {
			chk.Panic("parameter %q has value greater than maximum. %v > %v is not aceptable", name, prm.V, prm.Max)
		}
		if variables[i] == nil {
			chk.Panic("array of variables must not have nil entries")
		}
		*variables[i] = prm.V
	}
	return
}

// Connect connects parameter
func (o *Params) Connect(V *float64, name, caller string) (err string) {
	prm := o.Find(name)
	if prm == nil {
		return io.Sf("cannot find parameter named %q as requested by %q\n", name, caller)
	}
	prm.Connect(V)
	return
}

// ConnectSet connects set of parameters
func (o *Params) ConnectSet(V []*float64, names []string, caller string) (err string) {
	chk.IntAssert(len(V), len(names))
	for i, name := range names {
		prm := o.Find(name)
		if prm == nil {
			return io.Sf("cannot find parameter named %q as requested by %q\n", name, caller)
		}
		prm.Connect(V[i])
	}
	return
}

func (o Params) String() (l string) {
	for i, prm := range o {
		if i > 0 {
			l += ",\n"
		}
		l += io.Sf(G_extraindent + "{")
		l += io.Sf(`"n":%q, `, prm.N)
		l += io.Sf(`"v":%v, `, prm.V)
		l += io.Sf(`"min":%v, `, prm.Min)
		l += io.Sf(`"max":%v, `, prm.Max)
		l += io.Sf(`"s":%v, `, prm.S)
		l += io.Sf(`"d":%q, `, prm.D)
		l += io.Sf(`"u":%q, `, prm.U)
		l += io.Sf(`"adj":%v, `, prm.Adj)
		l += io.Sf(`"dep":%v, `, prm.Dep)
		l += io.Sf(`"extra":%q, `, prm.Extra)
		l += io.Sf(`"inact":%v, `, prm.Inact)
		l += io.Sf(`"setdef":%v`, prm.SetDef)
		l += io.Sf("}")
	}
	return
}
<commit_msg>Remove global ariable in fun/dbf<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dbf

import (
	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// P holds material parameter names and values
type P struct {

	// input
	N      string  `json:"n"`      // name of parameter
	V      float64 `json:"v"`      // value of parameter
	Min    float64 `json:"min"`    // min value
	Max    float64 `json:"max"`    // max value
	S      float64 `json:"s"`      // standard deviation
	D      string  `json:"d"`      // probability distribution type
	U      string  `json:"u"`      // unit (not verified)
	Adj    int     `json:"adj"`    // adjustable: unique ID (greater than zero)
	Dep    int     `json:"dep"`    // depends on "adj"
	Extra  string  `json:"extra"`  // extra data
	Inact  bool    `json:"inact"`  // parameter is inactive in optimisation
	SetDef bool    `json:"setdef"` // tells model to use a default value

	// auxiliary
	Fcn   T  // a function y=f(t,x)
	Other *P // dependency: connected parameter

	// derived
	conn []*float64 // connected variables to V
}

// Connect connects parameter to variable
func (o *P) Connect(V *float64) {
	o.conn = append(o.conn, V)
	*V = o.V
}

// Set sets parameter, including connected variables
func (o *P) Set(V float64) {
	o.V = V
	for _, v := range o.conn {
		*v = V
	}
}

// Params holds many parameters
type Params []*P

// Find finds a parameter by name
//  Note: returns nil if not found
func (o *Params) Find(name string) *P {
	for _, p := range *o {
		if p.N == name {
			return p
		}
	}
	return nil
}

// CheckLimits check limits of variables given in Min/Max
// Will panic if values are outside corresponding Min/Max range.
func (o *Params) CheckLimits() {
	for _, p := range *o {
		if p.V < p.Min {
			chk.Panic("parameter %q has value smaller than minimum. %v < %v is not aceptable", p.N, p.V, p.Min)
		}
		if p.V > p.Max {
			chk.Panic("parameter %q has value greater than maximum. %v > %v is not aceptable", p.N, p.V, p.Max)
		}
	}
}

// GetValues get parameter values
func (o *Params) GetValues(names []string) (values []float64, found []bool) {
	n := len(names)
	values = make([]float64, n)
	found = make([]bool, n)
	for i, name := range names {
		prm := o.Find(name)
		if prm != nil {
			values[i] = prm.V
			found[i] = true
		}
	}
	return
}

// CheckAndGetValues check min/max limits and return values.
// Will panic if values are outside corresponding min/max range.
// Will also panic if a parameter name is not found.
func (o *Params) CheckAndGetValues(names []string) (values []float64) {
	n := len(names)
	values = make([]float64, n)
	for i, name := range names {
		prm := o.Find(name)
		if prm == nil {
			chk.Panic("cannot find parameter named %q", name)
		}
		if prm.V < prm.Min {
			chk.Panic("parameter %q has value smaller than minimum. %v < %v is not aceptable", name, prm.V, prm.Min)
		}
		if prm.V > prm.Max {
			chk.Panic("parameter %q has value greater than maximum. %v > %v is not aceptable", name, prm.V, prm.Max)
		}
		values[i] = prm.V
	}
	return
}

// CheckAndSetVars get parameter values and check limits defined in Min and Max
// Will panic if values are outside corresponding Min/Max range.
// Will also panic if a parameter name is not found.
func (o *Params) CheckAndSetVars(names []string, variables []*float64) {
	n := len(names)
	if len(variables) != n {
		chk.Panic("array of variables must have the same size as the slice of names. %d != %d", len(variables), n)
	}
	for i, name := range names {
		prm := o.Find(name)
		if prm == nil {
			chk.Panic("cannot find parameter named %q", name)
		}
		if prm.V < prm.Min {
			chk.Panic("parameter %q has value smaller than minimum. %v < %v is not aceptable", name, prm.V, prm.Min)
		}
		if prm.V > prm.Max {
			chk.Panic("parameter %q has value greater than maximum. %v > %v is not aceptable", name, prm.V, prm.Max)
		}
		if variables[i] == nil {
			chk.Panic("array of variables must not have nil entries")
		}
		*variables[i] = prm.V
	}
	return
}

// Connect connects parameter
func (o *Params) Connect(V *float64, name, caller string) (err string) {
	prm := o.Find(name)
	if prm == nil {
		return io.Sf("cannot find parameter named %q as requested by %q\n", name, caller)
	}
	prm.Connect(V)
	return
}

// ConnectSet connects set of parameters
func (o *Params) ConnectSet(V []*float64, names []string, caller string) (err string) {
	chk.IntAssert(len(V), len(names))
	for i, name := range names {
		prm := o.Find(name)
		if prm == nil {
			return io.Sf("cannot find parameter named %q as requested by %q\n", name, caller)
		}
		prm.Connect(V[i])
	}
	return
}

func (o Params) String() (l string) {
	for i, prm := range o {
		if i > 0 {
			l += ",\n"
		}
		l += io.Sf("{")
		l += io.Sf(`"n":%q, `, prm.N)
		l += io.Sf(`"v":%v, `, prm.V)
		l += io.Sf(`"min":%v, `, prm.Min)
		l += io.Sf(`"max":%v, `, prm.Max)
		l += io.Sf(`"s":%v, `, prm.S)
		l += io.Sf(`"d":%q, `, prm.D)
		l += io.Sf(`"u":%q, `, prm.U)
		l += io.Sf(`"adj":%v, `, prm.Adj)
		l += io.Sf(`"dep":%v, `, prm.Dep)
		l += io.Sf(`"extra":%q, `, prm.Extra)
		l += io.Sf(`"inact":%v, `, prm.Inact)
		l += io.Sf(`"setdef":%v`, prm.SetDef)
		l += io.Sf("}")
	}
	return
}
<|endoftext|>


<commit_before>package mirango

import (
	"fmt"

	"github.com/mirango/defaults"
	"github.com/mirango/framework"
)

type Operations struct {
	operations []*Operation
	index      map[string]int
}

func NewOperations() *Operations {
	return &Operations{
		index: map[string]int{},
	}
}

func (ops *Operations) Append(operations ...*Operation) {
	le := len(ops.operations)
	for i := 0; i < len(operations); i++ {
		name := operations[i].name
		if name == "" {
			ops.operations = append(ops.operations, operations[i])
			continue
		} else {
			if _, ok := ops.index[name]; ok {
				panic(fmt.Sprintf("Detected 2 operations with the same name: \"%s\".", name))
			}
			ops.operations = append(ops.operations, operations[i])
			ops.index[name] = le + i
		}
	}
}

func (ops *Operations) Set(operations ...*Operation) {
	ops.operations = nil
	ops.index = map[string]int{}
	ops.Append(operations...)
}

func (ops *Operations) Get(name string) *Operation {
	return ops.operations[ops.index[name]]
}

func (ops *Operations) GetAll() []*Operation {
	return ops.operations
}

func (ops *Operations) Union(nops *Operations) {
	for _, o := range nops.operations {
		ops.Append(o)
	}
}

func (ops *Operations) Clone() *Operations {
	nops := NewOperations()
	nops.Union(ops)
	return nops
}

func (ops *Operations) GetByIndex(i int) *Operation {
	return ops.operations[i]
}

func (ops *Operations) GetByMethod(method string) *Operation {
	for _, o := range ops.operations {
		for _, m := range o.methods {
			if m == method {
				return o
			}
		}
	}
	return nil
}

func (ops *Operations) Len() int {
	return len(ops.operations)
}

type Operation struct {
	name          string
	handler       Handler
	route         *Route
	methods       []string
	schemes       []string
	accepts       []string
	returns       []string
	middleware    []Middleware
	params        *Params
	renders       string
	mimeTypeIn    paramIn
	mimeTypeParam string
}

func NewOperation(r *Route, h interface{}) *Operation {
	handler, err := handler(h)
	if err != nil {
		panic(err.Error())
	}
	o := &Operation{
		methods: []string{"GET"},
		handler: handler,
		route:   r,
		params:  NewParams(),
		schemes: defaults.Schemes,
		accepts: defaults.Accepts,
		returns: defaults.Returns,
	}
	return o
}

func GET(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("GET")
}

func POST(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("POST")
}

func PUT(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("PUT")
}

func DELETE(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("DELETE")
}

func (o *Operation) Uses(h interface{}) *Operation { //interface
	handler, err := handler(h)
	if err != nil {
		panic(err.Error())
	}
	o.handler = handler
	return o
}

func getHandler(h interface{}, mw []interface{}) (Handler, error) {
	final, err := handler(h)
	if err != nil {
		return nil, err
	}
	for i := len(mw) - 1; i >= 0; i-- {
		switch t := mw[i].(type) {
		case Middleware:
			final = t.Run(final)
		case MiddlewareFunc:
			final = t(final)
		case func(Handler) Handler:
			final = t(final)
		default:
			return nil, fmt.Errorf("invalid type for middleware")
		}
	}
	return final, nil
}

func (o *Operation) With(mw ...interface{}) *Operation {
	handler, err := getHandler(o.handler, mw)
	if err != nil {
		panic(err.Error())
	}
	o.handler = handler
	return o
}

func (o *Operation) Apply(temps ...func(*Operation)) *Operation {
	for i := 0; i < len(temps); i++ {
		temps[i](o)
	}
	return o
}

func (o *Operation) Route() framework.Route {
	return o.route
}

func (o *Operation) Name(name string) *Operation {
	o.name = name
	return o
}

func (o *Operation) GetName() string {
	return o.name
}

func (o *Operation) Methods(methods ...string) *Operation {
	o.methods = methods
	return o
}

func (o *Operation) Params(params ...*Param) *Operation {
	o.params.Set(params...)
	return o
}

func (o *Operation) GetParams() *Params {
	return o.params
}

func (o *Operation) GetAllParams() *Params {
	params := o.params.Clone()
	params.Union(o.route.GetAllParams())
	return params
}

func (o *Operation) Schemes(schemes ...string) *Operation {
	o.schemes = schemes
	return o
}

func (o *Operation) Accepts(accepts ...string) *Operation {
	o.accepts = accepts
	return o
}

func (o *Operation) Returns(returns ...string) *Operation {
	o.returns = returns
	return o
}

func (o *Operation) PathParam(name string) *Param {
	p := PathParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) QueryParam(name string) *Param {
	p := QueryParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) HeaderParam(name string) *Param {
	p := HeaderParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) BodyParam(name string) *Param {
	p := BodyParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) GetMethods() []string {
	return o.methods
}

func (o *Operation) BuildPath(v ...interface{}) string {
	return o.route.BuildPath(v...)
}

func (o *Operation) GetPath() string {
	return o.route.path
}

func (o *Operation) GetFullPath() string {
	return o.route.FullPath()
}

func (o *Operation) ServeHTTP(c *Context) interface{} {
	c.operation = o
	return o.handler.ServeHTTP(c)
}

type middlewareContainer struct {
	middleware []interface{}
}

func With(mw ...interface{}) *middlewareContainer {
	return &middlewareContainer{middleware: mw}
}

func (c middlewareContainer) Handle(operations ...*Operation) []*Operation {
	for i := 0; i < len(operations); i++ {
		operations[i].With(c.middleware...)
	}
	return operations
}

type templateContainer struct {
	templates []func(*Operation)
}

func Apply(temps ...func(*Operation)) *templateContainer {
	return &templateContainer{templates: temps}
}

func (c templateContainer) To(operations ...*Operation) []*Operation {
	for i := 0; i < len(operations); i++ {
		operations[i].Apply(c.templates...)
	}
	return operations
}
<commit_msg>Edited Operations struct<commit_after>package mirango

import (
	"fmt"

	"github.com/mirango/defaults"
	"github.com/mirango/framework"
)

type Operations struct {
	operations []*Operation
	index      map[string]int
}

func NewOperations() *Operations {
	return &Operations{
		index: map[string]int{},
	}
}

func (ops *Operations) Append(operations ...*Operation) {
	le := len(ops.operations)
	for i := 0; i < len(operations); i++ {
		name := operations[i].name
		if name == "" {
			ops.operations = append(ops.operations, operations[i])
			continue
		} else {
			if _, ok := ops.index[name]; ok {
				panic(fmt.Sprintf("Detected 2 operations with the same name: \"%s\".", name))
			}
			ops.operations = append(ops.operations, operations[i])
			ops.index[name] = le + i
		}
	}
}

func (ops *Operations) Set(operations ...*Operation) {
	ops.operations = nil
	ops.index = map[string]int{}
	ops.Append(operations...)
}

func (ops *Operations) Get(name string) *Operation {
	return ops.operations[ops.index[name]]
}

func (ops *Operations) GetAll() []*Operation {
	return ops.operations
}

func (ops *Operations) Union(nops *Operations) {
	for _, o := range nops.operations {
		ops.Append(o)
	}
}

func (ops *Operations) Clone() *Operations {
	nops := NewOperations()
	nops.Union(ops)
	return nops
}

func (ops *Operations) GetByIndex(i int) *Operation {
	return ops.operations[i]
}

func (ops *Operations) GetByMethod(method string) *Operation {
	for _, o := range ops.operations {
		for _, m := range o.methods {
			if m == method {
				return o
			}
		}
	}
	return nil
}

func (ops *Operations) Len() int {
	return len(ops.operations)
}

type Operation struct {
	name          string
	handler       Handler
	route         *Route
	methods       []string
	schemes       []string
	accepts       []string
	returns       []string
	middleware    []Middleware
	params        *Params
	renders       string
	mimeTypeIn    paramIn
	mimeTypeParam string

	allParams  *Params
	allSchemes []string
	allAccepts []string
	allReturns []string
}

func NewOperation(r *Route, h interface{}) *Operation {
	handler, err := handler(h)
	if err != nil {
		panic(err.Error())
	}
	o := &Operation{
		methods: []string{"GET"},
		handler: handler,
		route:   r,
		params:  NewParams(),
		schemes: defaults.Schemes,
		accepts: defaults.Accepts,
		returns: defaults.Returns,
	}
	o.middleware = []Middleware{CheckReturns(o), CheckSchemes(o), CheckAccepts(o), CheckParams(o)}
	return o
}

func GET(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("GET")
}

func POST(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("POST")
}

func PUT(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("PUT")
}

func DELETE(r *Route, h interface{}) *Operation {
	return NewOperation(r, h).Methods("DELETE")
}

func (o *Operation) Uses(h interface{}) *Operation { //interface
	handler, err := handler(h)
	if err != nil {
		panic(err.Error())
	}
	o.handler = handler
	return o
}

func getHandler(h interface{}, mw []Middleware) (Handler, error) {
	final, err := handler(h)
	if err != nil {
		return nil, err
	}
	for i := len(mw) - 1; i >= 0; i-- {
		final = mw[i].Run(final)
	}
	return final, nil
}

func (o *Operation) With(mw ...interface{}) *Operation {
	for i := 0; i < len(mw); i++ {
		switch t := mw[i].(type) {
		case Middleware:
			o.middleware = append(o.middleware, t)
		case MiddlewareFunc:
			o.middleware = append(o.middleware, t)
		case func(Handler) Handler:
			o.middleware = append(o.middleware, MiddlewareFunc(t))
		}
	}
	return o
}

func (o *Operation) with() {
	handler, err := getHandler(o.handler, o.middleware)
	if err != nil {
		panic(err.Error())
	}
	o.handler = handler
}

func (o *Operation) Apply(temps ...func(*Operation)) *Operation {
	for i := 0; i < len(temps); i++ {
		temps[i](o)
	}
	return o
}

func (o *Operation) Route() framework.Route {
	return o.route
}

func (o *Operation) Name(name string) *Operation {
	o.name = name
	return o
}

func (o *Operation) GetName() string {
	return o.name
}

func (o *Operation) Methods(methods ...string) *Operation {
	o.methods = methods
	return o
}

func (o *Operation) Params(params ...*Param) *Operation {
	o.params.Set(params...)
	return o
}

func (o *Operation) GetParams() *Params {
	return o.params
}

func (o *Operation) GetAllParams() *Params {
	params := o.params.Clone()
	params.Union(o.route.GetAllParams())
	return params
}

func (o *Operation) Schemes(schemes ...string) *Operation {
	o.schemes = schemes
	return o
}

func (o *Operation) Accepts(accepts ...string) *Operation {
	o.accepts = accepts
	return o
}

func (o *Operation) Returns(returns ...string) *Operation {
	o.returns = returns
	return o
}

func (o *Operation) PathParam(name string) *Param {
	p := PathParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) QueryParam(name string) *Param {
	p := QueryParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) HeaderParam(name string) *Param {
	p := HeaderParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) BodyParam(name string) *Param {
	p := BodyParam(name)
	o.params.Append(p)
	return p
}

func (o *Operation) GetMethods() []string {
	return o.methods
}

func (o *Operation) BuildPath(v ...interface{}) string {
	return o.route.BuildPath(v...)
}

func (o *Operation) GetPath() string {
	return o.route.path
}

func (o *Operation) GetFullPath() string {
	return o.route.FullPath()
}

func (o *Operation) ServeHTTP(c *Context) interface{} {
	c.operation = o
	return o.handler.ServeHTTP(c)
}

type middlewareContainer struct {
	middleware []interface{}
}

func With(mw ...interface{}) *middlewareContainer {
	return &middlewareContainer{middleware: mw}
}

func (c middlewareContainer) Handle(operations ...*Operation) []*Operation {
	for i := 0; i < len(operations); i++ {
		operations[i].With(c.middleware...)
	}
	return operations
}

type templateContainer struct {
	templates []func(*Operation)
}

func Apply(temps ...func(*Operation)) *templateContainer {
	return &templateContainer{templates: temps}
}

func (c templateContainer) To(operations ...*Operation) []*Operation {
	for i := 0; i < len(operations); i++ {
		operations[i].Apply(c.templates...)
	}
	return operations
}
<|endoftext|>


<commit_before>package jwt

import (
	"fmt"
	"github.com/pkg/errors"
	// jose "gopkg.in/square/go-jose.v2"
	"net/http"
	"time"
	// jwt "gopkg.in/square/go-jose.v2/jwt"
)

// Auth is a struct that provides jwt based authentication.
type Auth struct {
	authStore    *jwtStore
	refreshStore *jwtStore
	csrfStore    *mixStore

	options Options

	// Handlers for when an error occurs
	errorHandler        http.Handler
	unauthorizedHandler http.Handler

	// funcs for verifiing and revoking tokens
	revokeTokenByID    TokenRevoker
	checkTokenId       TokenIdChecker
	getTokenId         TokenIdGetter
	verifyAuthToken    func(r *http.Request) error
	verifyRefreshToken func(r *http.Request) error

	// CsrfEncrypter aead
	// csrfEncrypter jose.Encrypter
}

const (
	defaultRefreshTokenValidTime  = 72 * time.Hour
	defaultAuthTokenValidTime     = 10 * time.Minute
	defaultBearerAuthTokenName    = "X-Auth-Token"
	defaultBearerRefreshTokenName = "X-Refresh-Token"
	defaultCSRFTokenName          = "X-CSRF-Token"
	defaultCookieAuthTokenName    = "AuthToken"
	defaultCookieRefreshTokenName = "RefreshToken"
)

// CSRF token length in bytes.
const tokenLength = 32

const (
	AuthToken    = 0
	RefreshToken = 1
)

var (
	UnauthorizedRequest = errors.New("Unauthorized Request")
)

func defaultTokenRevoker(tokenId string) error {
	return nil
}

// TokenRevoker : a type to revoke tokens
type TokenRevoker func(tokenId string) error

func defaultCheckTokenId(tokenId string) bool {
	// return true if the token id is valid (has not been revoked). False otherwise
	return true
}

// TokenIdChecker : a type to check tokens
type TokenIdChecker func(tokenId string) bool

func defaultGetTokenId() string {
	// return empty string
	return ""
}

// TokenIdGetter : a type to get token ids
type TokenIdGetter func() string

func defaultErrorHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Internal Server Error", 500)
	return
}

func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Unauthorized", 401)
	return
}

// func defaultValidator(a *Auth, r *http.Request) error {
// 	u := r.URL
// 	err := c.Claims.Validate(jwt.Expected{
// 		Issuer:  a.options.Issuer,
// 		Subject: from(r),
// 		Time:    time.Now().UTC(),
// 	})
// 	return err
// }

// New constructs a new Auth instance with supplied options.
func New(o ...Options) (*Auth, error) {
	var opts Options
	var err error
	if len(o) == 0 {
		opts = Options{}
		err = DevelOpts(&opts)
		if err != nil {
			return nil, errors.Wrap(err, "Error init development options")
		}
	} else {
		opts = (o[0])
		err = DefOpts(&opts)
		if err != nil {
			return nil, errors.Wrap(err, "Error init auth options")
		}
	}
	auth := &Auth{}
	err = auth.setOptions(&opts)
	if err != nil {
		return nil, errors.Wrap(err, "Error setting auth options")
	}
	return auth, nil
}

func (a *Auth) setOptions(o *Options) error {
	aus, err := NewJWTStore(o, o.AuthTokenName, AuthToken, false)
	if err != nil {
		return errors.Wrap(err, "Error creating auth store")
	}
	a.authStore = aus
	rs, err := NewJWTStore(o, o.RefreshTokenName, RefreshToken, true)
	if err != nil {
		return errors.Wrap(err, "Error creating refresh store")
	}
	a.refreshStore = rs
	a.csrfStore = &mixStore{o.CSRFTokenName}

	a.options = *o

	a.errorHandler = http.HandlerFunc(defaultErrorHandler)
	a.unauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
	a.revokeTokenByID = defaultTokenRevoker
	a.checkTokenId = defaultCheckTokenId
	a.getTokenId = defaultGetTokenId
	// a.verifyAuthToken = defaultValidator
	// a.verifyRefreshToken = defaultValidator
	return nil
}

// SetErrorHandler : add methods to allow the changing of default functions
func (a *Auth) SetErrorHandler(handler http.Handler) {
	a.errorHandler = handler
}

// SetUnauthorizedHandler : set the 401 handler
func (a *Auth) SetUnauthorizedHandler(handler http.Handler) {
	a.unauthorizedHandler = handler
}

// SetRevokeTokenFunction : set the function which revokes a token
func (a *Auth) SetRevokeTokenFunction(revoker TokenRevoker) {
	a.revokeTokenByID = revoker
}

// SetCheckTokenIdFunction : set the function which checks token id's
func (a *Auth) SetCheckTokenIdFunction(checker TokenIdChecker) {
	a.checkTokenId = checker
}

func (a *Auth) SetVerifyAuthFunction(fn func(r *http.Request) error) {
	a.verifyAuthToken = fn
}

func (a *Auth) SetVerifyRefreshFunction(fn func(r *http.Request) error) {
	a.verifyRefreshToken = fn
}

func (a *Auth) SetBearerTokens(bt bool) error {
	if a.authStore == nil || a.refreshStore == nil {
		return errors.New("Auth.SetBearerTokens error: token store is not initialized")
	}
	a.options.BearerTokens = bt
	a.authStore.bearerTokens = bt
	a.refreshStore.bearerTokens = bt
	var authName, refreshName string
	if bt {
		authName = defaultBearerAuthTokenName
		refreshName = defaultBearerRefreshTokenName
	} else {
		authName = defaultCookieAuthTokenName
		refreshName = defaultCookieRefreshTokenName
	}
	a.options.AuthTokenName = authName
	a.options.RefreshTokenName = refreshName
	a.authStore.tokenName = authName
	a.authStore.cookieStore.name = authName
	a.refreshStore.tokenName = refreshName
	a.refreshStore.cookieStore.name = refreshName
	return nil
}

// Handler implements the http.HandlerFunc for integration with the standard net/http lib.
func (a *Auth) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Process the request. If it returns an error,
		// that indicates the request should not continue.
		id, err := a.Process(w, r)

		// If there was an error, do not continue.
		if err != nil {
			a.NullifyTokens(id, w)
			if err == UnauthorizedRequest {
				fmt.Println("Unauthorized processing")
				a.unauthorizedHandler.ServeHTTP(w, r)
				return
			}
			fmt.Println("Error processing")
			fmt.Printf("%#v\n", err)
			a.errorHandler.ServeHTTP(w, r)
			return
		}

		h.ServeHTTP(w, r)
	})
}

// HandlerFunc works identically to Handler, but takes a HandlerFunc instead of a Handler.
func (a *Auth) HandlerFunc(fn http.HandlerFunc) http.Handler {
	if fn == nil {
		return a.Handler(nil)
	}
	return a.Handler(fn)
}

// HandlerFuncWithNext is a special implementation for Negroni, but could be used elsewhere.
func (a *Auth) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	id, err := a.Process(w, r)

	// If there was an error, do not call next.
	if err == nil && next != nil {
		next(w, r)
	} else {
		_ = a.NullifyTokens(id, w)
		if err == UnauthorizedRequest {
			a.unauthorizedHandler.ServeHTTP(w, r)
		} else {
			a.errorHandler.ServeHTTP(w, r)
		}
	}
}

// Process runs the actual checks and returns an error if the middleware chain should stop.
func (a *Auth) Process(w http.ResponseWriter, r *http.Request) (string, error) {
	// cookies aren't included with options, so simply pass through
	if r.Method == "OPTIONS" {
		return "", nil
	}

	// grab the credentials from the request
	var c credentials
	if err := a.getCredentials(r, &c); err != nil {
		return "", errors.Wrap(err, "Auth.Process: Error getting auth credentials")
	}
	fmt.Printf("%#v\n", c.AuthToken)

	// // check the credential's validity; updating expiry's if necessary and/or allowed
	if err := c.Validate(r); err != nil {
		if err == AuthTokenExpired {
			fmt.Println("Auth token is expired. Renew Auth token")
			err = c.RenewAuthToken(r)
			if err != nil {
				return c.AuthToken.ID, errors.Wrap(err, "Invalid credentials")
			}
		}
		return c.AuthToken.ID, errors.Wrap(err, "Invalid credentials")
	}
	fmt.Println("Auth token is not expired. Process...")

	// // if we've made it this far, everything is valid!
	// // And tokens have been refreshed if need-be
	if !a.options.VerifyOnlyServer {
		if err := a.setCredentials(w, &c); err != nil {
			return c.AuthToken.ID, errors.Wrap(err, "Error setting credentials")
		}
	}

	return c.AuthToken.ID, nil
}

// IssueNewTokens : and also modify create refresh and auth token functions!
func (a *Auth) IssueNewTokens(w http.ResponseWriter, claims *ClaimsType) error {
	if a.options.VerifyOnlyServer {
		return errors.New("Auth.IssueNewTokens: Server is not authorized to issue new tokens")

	}

	var c credentials
	err := a.newCredentials(&c, claims)
	if err != nil {
		return errors.Wrap(err, "Error creating new credentials")
	}

	err = a.setCredentials(w, &c)
	if err != nil {
		return errors.Wrap(err, "Error setting credentials")
	}

	return nil
}

// NullifyTokens : invalidate tokens
// note @adam-hanna: what if there are no credentials in the request?
func (a *Auth) NullifyTokens(tokenID string, w http.ResponseWriter) error {
	a.authStore.Revoke(w)
	a.refreshStore.Revoke(w)
	a.csrfStore.Save("", w)

	err := a.revokeTokenByID(tokenID)
	if err != nil {
		return errors.Wrap(err, "Auth.NullifyTokens: Error revoking token")
	}

	return nil
}

// GrabTokenClaims : extract the claims from the request
// note: we always grab from the authToken
func (a *Auth) GrabTokenClaims(r *http.Request) (*ClaimsType, error) {
	ca, err := a.authStore.Get(r)
	if err != nil {
		return nil, errors.Wrap(err, "Auth.GrabTokenClaims: Error getting auth claims")
	}

	return ca, nil
}
<commit_msg>Del fmt marks<commit_after>package jwt

import (
	"fmt"
	"github.com/pkg/errors"
	// jose "gopkg.in/square/go-jose.v2"
	"net/http"
	"time"
	// jwt "gopkg.in/square/go-jose.v2/jwt"
)

// Auth is a struct that provides jwt based authentication.
type Auth struct {
	authStore    *jwtStore
	refreshStore *jwtStore
	csrfStore    *mixStore

	options Options

	// Handlers for when an error occurs
	errorHandler        http.Handler
	unauthorizedHandler http.Handler

	// funcs for verifiing and revoking tokens
	revokeTokenByID    TokenRevoker
	checkTokenId       TokenIdChecker
	getTokenId         TokenIdGetter
	verifyAuthToken    func(r *http.Request) error
	verifyRefreshToken func(r *http.Request) error

	// CsrfEncrypter aead
	// csrfEncrypter jose.Encrypter
}

const (
	defaultRefreshTokenValidTime  = 72 * time.Hour
	defaultAuthTokenValidTime     = 10 * time.Minute
	defaultBearerAuthTokenName    = "X-Auth-Token"
	defaultBearerRefreshTokenName = "X-Refresh-Token"
	defaultCSRFTokenName          = "X-CSRF-Token"
	defaultCookieAuthTokenName    = "AuthToken"
	defaultCookieRefreshTokenName = "RefreshToken"
)

// CSRF token length in bytes.
const tokenLength = 32

const (
	AuthToken    = 0
	RefreshToken = 1
)

var (
	UnauthorizedRequest = errors.New("Unauthorized Request")
)

func defaultTokenRevoker(tokenId string) error {
	return nil
}

// TokenRevoker : a type to revoke tokens
type TokenRevoker func(tokenId string) error

func defaultCheckTokenId(tokenId string) bool {
	// return true if the token id is valid (has not been revoked). False otherwise
	return true
}

// TokenIdChecker : a type to check tokens
type TokenIdChecker func(tokenId string) bool

func defaultGetTokenId() string {
	// return empty string
	return ""
}

// TokenIdGetter : a type to get token ids
type TokenIdGetter func() string

func defaultErrorHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Internal Server Error", 500)
	return
}

func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Unauthorized", 401)
	return
}

// func defaultValidator(a *Auth, r *http.Request) error {
// 	u := r.URL
// 	err := c.Claims.Validate(jwt.Expected{
// 		Issuer:  a.options.Issuer,
// 		Subject: from(r),
// 		Time:    time.Now().UTC(),
// 	})
// 	return err
// }

// New constructs a new Auth instance with supplied options.
func New(o ...Options) (*Auth, error) {
	var opts Options
	var err error
	if len(o) == 0 {
		opts = Options{}
		err = DevelOpts(&opts)
		if err != nil {
			return nil, errors.Wrap(err, "Error init development options")
		}
	} else {
		opts = (o[0])
		err = DefOpts(&opts)
		if err != nil {
			return nil, errors.Wrap(err, "Error init auth options")
		}
	}
	auth := &Auth{}
	err = auth.setOptions(&opts)
	if err != nil {
		return nil, errors.Wrap(err, "Error setting auth options")
	}
	return auth, nil
}

func (a *Auth) setOptions(o *Options) error {
	aus, err := NewJWTStore(o, o.AuthTokenName, AuthToken, false)
	if err != nil {
		return errors.Wrap(err, "Error creating auth store")
	}
	a.authStore = aus
	rs, err := NewJWTStore(o, o.RefreshTokenName, RefreshToken, true)
	if err != nil {
		return errors.Wrap(err, "Error creating refresh store")
	}
	a.refreshStore = rs
	a.csrfStore = &mixStore{o.CSRFTokenName}

	a.options = *o

	a.errorHandler = http.HandlerFunc(defaultErrorHandler)
	a.unauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
	a.revokeTokenByID = defaultTokenRevoker
	a.checkTokenId = defaultCheckTokenId
	a.getTokenId = defaultGetTokenId
	// a.verifyAuthToken = defaultValidator
	// a.verifyRefreshToken = defaultValidator
	return nil
}

// SetErrorHandler : add methods to allow the changing of default functions
func (a *Auth) SetErrorHandler(handler http.Handler) {
	a.errorHandler = handler
}

// SetUnauthorizedHandler : set the 401 handler
func (a *Auth) SetUnauthorizedHandler(handler http.Handler) {
	a.unauthorizedHandler = handler
}

// SetRevokeTokenFunction : set the function which revokes a token
func (a *Auth) SetRevokeTokenFunction(revoker TokenRevoker) {
	a.revokeTokenByID = revoker
}

// SetCheckTokenIdFunction : set the function which checks token id's
func (a *Auth) SetCheckTokenIdFunction(checker TokenIdChecker) {
	a.checkTokenId = checker
}

func (a *Auth) SetVerifyAuthFunction(fn func(r *http.Request) error) {
	a.verifyAuthToken = fn
}

func (a *Auth) SetVerifyRefreshFunction(fn func(r *http.Request) error) {
	a.verifyRefreshToken = fn
}

func (a *Auth) SetBearerTokens(bt bool) error {
	if a.authStore == nil || a.refreshStore == nil {
		return errors.New("Auth.SetBearerTokens error: token store is not initialized")
	}
	a.options.BearerTokens = bt
	a.authStore.bearerTokens = bt
	a.refreshStore.bearerTokens = bt
	var authName, refreshName string
	if bt {
		authName = defaultBearerAuthTokenName
		refreshName = defaultBearerRefreshTokenName
	} else {
		authName = defaultCookieAuthTokenName
		refreshName = defaultCookieRefreshTokenName
	}
	a.options.AuthTokenName = authName
	a.options.RefreshTokenName = refreshName
	a.authStore.tokenName = authName
	a.authStore.cookieStore.name = authName
	a.refreshStore.tokenName = refreshName
	a.refreshStore.cookieStore.name = refreshName
	return nil
}

// Handler implements the http.HandlerFunc for integration with the standard net/http lib.
func (a *Auth) Handler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Process the request. If it returns an error,
		// that indicates the request should not continue.
		id, err := a.Process(w, r)

		// If there was an error, do not continue.
		if err != nil {
			a.NullifyTokens(id, w)
			if err == UnauthorizedRequest {
				fmt.Println("Unauthorized processing")
				a.unauthorizedHandler.ServeHTTP(w, r)
				return
			}
			fmt.Println("Error processing")
			fmt.Printf("%#v\n", err)
			a.errorHandler.ServeHTTP(w, r)
			return
		}

		h.ServeHTTP(w, r)
	})
}

// HandlerFunc works identically to Handler, but takes a HandlerFunc instead of a Handler.
func (a *Auth) HandlerFunc(fn http.HandlerFunc) http.Handler {
	if fn == nil {
		return a.Handler(nil)
	}
	return a.Handler(fn)
}

// HandlerFuncWithNext is a special implementation for Negroni, but could be used elsewhere.
func (a *Auth) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	id, err := a.Process(w, r)

	// If there was an error, do not call next.
	if err == nil && next != nil {
		next(w, r)
	} else {
		_ = a.NullifyTokens(id, w)
		if err == UnauthorizedRequest {
			a.unauthorizedHandler.ServeHTTP(w, r)
		} else {
			a.errorHandler.ServeHTTP(w, r)
		}
	}
}

// Process runs the actual checks and returns an error if the middleware chain should stop.
func (a *Auth) Process(w http.ResponseWriter, r *http.Request) (string, error) {
	// cookies aren't included with options, so simply pass through
	if r.Method == "OPTIONS" {
		return "", nil
	}

	// grab the credentials from the request
	var c credentials
	if err := a.getCredentials(r, &c); err != nil {
		return "", errors.Wrap(err, "Auth.Process: Error getting auth credentials")
	}

	// // check the credential's validity; updating expiry's if necessary and/or allowed
	if err := c.Validate(r); err != nil {
		if err == AuthTokenExpired {
			fmt.Println("Auth token is expired. Renew Auth token")
			err = c.RenewAuthToken(r)
			if err != nil {
				return c.AuthToken.ID, errors.Wrap(err, "Invalid credentials")
			}
		}
		return c.AuthToken.ID, errors.Wrap(err, "Invalid credentials")
	}

	// // if we've made it this far, everything is valid!
	// // And tokens have been refreshed if need-be
	if !a.options.VerifyOnlyServer {
		if err := a.setCredentials(w, &c); err != nil {
			return c.AuthToken.ID, errors.Wrap(err, "Error setting credentials")
		}
	}

	return c.AuthToken.ID, nil
}

// IssueNewTokens : and also modify create refresh and auth token functions!
func (a *Auth) IssueNewTokens(w http.ResponseWriter, claims *ClaimsType) error {
	if a.options.VerifyOnlyServer {
		return errors.New("Auth.IssueNewTokens: Server is not authorized to issue new tokens")

	}

	var c credentials
	err := a.newCredentials(&c, claims)
	if err != nil {
		return errors.Wrap(err, "Error creating new credentials")
	}

	err = a.setCredentials(w, &c)
	if err != nil {
		return errors.Wrap(err, "Error setting credentials")
	}

	return nil
}

// NullifyTokens : invalidate tokens
// note @adam-hanna: what if there are no credentials in the request?
func (a *Auth) NullifyTokens(tokenID string, w http.ResponseWriter) error {
	a.authStore.Revoke(w)
	a.refreshStore.Revoke(w)
	a.csrfStore.Save("", w)

	err := a.revokeTokenByID(tokenID)
	if err != nil {
		return errors.Wrap(err, "Auth.NullifyTokens: Error revoking token")
	}

	return nil
}

// GrabTokenClaims : extract the claims from the request
// note: we always grab from the authToken
func (a *Auth) GrabTokenClaims(r *http.Request) (*ClaimsType, error) {
	ca, err := a.authStore.Get(r)
	if err != nil {
		return nil, errors.Wrap(err, "Auth.GrabTokenClaims: Error getting auth claims")
	}

	return ca, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/geek1011/kepubify/kepub"

	cli "gopkg.in/urfave/cli.v1"
)

var version = "dev"

// exists checks whether a path exists
func exists(path string) bool {
	if _, err := os.Stat(path); !os.IsNotExist(err) {
		return true
	}
	return false
}

// isDir checks if a exists and is a dir
func isDir(path string) bool {
	if fi, err := os.Stat(path); err == nil && fi.IsDir() {
		return true
	}
	return false
}

func convert(c *cli.Context) error {
	if len(c.Args()) < 1 || len(c.Args()) > 2 {
		return fmt.Errorf("Invalid number of arguments. Usage: kepubify EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]")
	}

	infile := c.Args().Get(0)
	if infile == "" {
		return fmt.Errorf("Input file path must not be blank.")
	}

	infile, err := filepath.Abs(infile)
	if err != nil {
		return fmt.Errorf("Error resolving input file path: %s.", err)
	}

	if !exists(infile) {
		return fmt.Errorf("Input file does not exist.")
	}

	if isDir(infile) {
		return fmt.Errorf("Input file must be a file, not a directory.")
	}

	if filepath.Ext(infile) != ".epub" {
		return fmt.Errorf("Input file must have the extension \".epub\".")
	}

	outfile := fmt.Sprintf("%s.kepub.epub", strings.Replace(filepath.Base(infile), ".epub", "", -1))
	if len(c.Args()) == 2 {
		if exists(c.Args().Get(1)) {
			if isDir(c.Args().Get(1)) {
				outfile = path.Join(c.Args().Get(1), outfile)
			} else {
				return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
			}
		} else {
			if strings.HasSuffix(c.Args().Get(1), ".kepub.epub") {
				outfile = c.Args().Get(1)
			} else {
				return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
			}
		}
	}

	outfile, err = filepath.Abs(outfile)
	if err != nil {
		return fmt.Errorf("Error resolving output file path: %s.", err)
	}

	fmt.Printf("Input file: %s\n", infile)
	fmt.Printf("Output file: %s\n", outfile)
	fmt.Println()

	err = kepub.Kepubify(infile, outfile, true)
	if err != nil {
		return fmt.Errorf("Error converting epub to kepub: %s.", err)
	}

	fmt.Printf("Succesfully converted \"%s\" to a kepub.\nYou can find the converted file at \"%s\"\n", infile, outfile)

	if runtime.GOOS == "windows" {
		time.Sleep(5000 * time.Second)
	}

	return nil
}

func main() {
	app := cli.NewApp()

	app.Name = "kepubify"
	app.Description = "Convert your ePubs into kepubs, with a easy-to-use command-line tool."
	app.Version = version

	app.ArgsUsage = "EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]"
	app.Action = convert

	app.Run(os.Args)
}
<commit_msg>Fix spelling mistake<commit_after>package main

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/geek1011/kepubify/kepub"

	cli "gopkg.in/urfave/cli.v1"
)

var version = "dev"

// exists checks whether a path exists
func exists(path string) bool {
	if _, err := os.Stat(path); !os.IsNotExist(err) {
		return true
	}
	return false
}

// isDir checks if a exists and is a dir
func isDir(path string) bool {
	if fi, err := os.Stat(path); err == nil && fi.IsDir() {
		return true
	}
	return false
}

func convert(c *cli.Context) error {
	if len(c.Args()) < 1 || len(c.Args()) > 2 {
		return fmt.Errorf("Invalid number of arguments. Usage: kepubify EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]")
	}

	infile := c.Args().Get(0)
	if infile == "" {
		return fmt.Errorf("Input file path must not be blank.")
	}

	infile, err := filepath.Abs(infile)
	if err != nil {
		return fmt.Errorf("Error resolving input file path: %s.", err)
	}

	if !exists(infile) {
		return fmt.Errorf("Input file does not exist.")
	}

	if isDir(infile) {
		return fmt.Errorf("Input file must be a file, not a directory.")
	}

	if filepath.Ext(infile) != ".epub" {
		return fmt.Errorf("Input file must have the extension \".epub\".")
	}

	outfile := fmt.Sprintf("%s.kepub.epub", strings.Replace(filepath.Base(infile), ".epub", "", -1))
	if len(c.Args()) == 2 {
		if exists(c.Args().Get(1)) {
			if isDir(c.Args().Get(1)) {
				outfile = path.Join(c.Args().Get(1), outfile)
			} else {
				return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
			}
		} else {
			if strings.HasSuffix(c.Args().Get(1), ".kepub.epub") {
				outfile = c.Args().Get(1)
			} else {
				return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
			}
		}
	}

	outfile, err = filepath.Abs(outfile)
	if err != nil {
		return fmt.Errorf("Error resolving output file path: %s.", err)
	}

	fmt.Printf("Input file: %s\n", infile)
	fmt.Printf("Output file: %s\n", outfile)
	fmt.Println()

	err = kepub.Kepubify(infile, outfile, true)
	if err != nil {
		return fmt.Errorf("Error converting epub to kepub: %s.", err)
	}

	fmt.Printf("Successfully converted \"%s\" to a kepub.\nYou can find the converted file at \"%s\"\n", infile, outfile)

	if runtime.GOOS == "windows" {
		time.Sleep(5000 * time.Second)
	}

	return nil
}

func main() {
	app := cli.NewApp()

	app.Name = "kepubify"
	app.Description = "Convert your ePubs into kepubs, with a easy-to-use command-line tool."
	app.Version = version

	app.ArgsUsage = "EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]"
	app.Action = convert

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


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/mattn/go-zglob"

	"github.com/geek1011/kepubify/kepub"

	cli "gopkg.in/urfave/cli.v1"
)

var version = "dev"

// exists checks whether a path exists
func exists(path string) bool {
	if _, err := os.Stat(path); !os.IsNotExist(err) {
		return true
	}
	return false
}

// isDir checks if a exists and is a dir
func isDir(path string) bool {
	if fi, err := os.Stat(path); err == nil && fi.IsDir() {
		return true
	}
	return false
}

func convert(c *cli.Context) error {
	if len(c.Args()) < 1 || len(c.Args()) > 2 {
		return fmt.Errorf("Invalid number of arguments. Usage: kepubify EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]")
	}

	infile := c.Args().Get(0)
	if infile == "" {
		return fmt.Errorf("Input path must not be blank.")
	}

	infile, err := filepath.Abs(infile)
	if err != nil {
		return fmt.Errorf("Error resolving input path: %s.", err)
	}

	if !exists(infile) {
		return fmt.Errorf("Input file or directory does not exist.")
	}

	if isDir(infile) {
		if len(c.Args()) != 2 {
			return fmt.Errorf("Because input is a dir, a second argument must be supplied with a nonexistent dir for the conversion output.")
		}

		outdir := c.Args().Get(1)
		if exists(outdir) {
			return fmt.Errorf("Because input is a dir, a second argument must be supplied with a nonexistent dir for the conversion output.")
		}

		outdir, err = filepath.Abs(outdir)
		if err != nil {
			return fmt.Errorf("Error resolving output dir path: %s.", err)
		}

		err := os.Mkdir(outdir, os.ModePerm)
		if err != nil {
			return fmt.Errorf("Error creating output dir: %s.", err)
		}

		epubs, err := zglob.Glob(filepath.Join(infile, "**", "*.epub"))
		if err != nil {
			return fmt.Errorf("Error searching for epubs in input dir: %s.", err)
		}

		errs := map[string]error{}
		for i, epub := range epubs {
			rel, err := filepath.Rel(infile, epub)
			if err != nil {
				fmt.Printf("[%v/%v] Error resolving relative path of %s: %v\n", i+1, len(epubs), epub, err)
				errs[epub] = err
				continue
			}

			err = os.MkdirAll(filepath.Join(outdir, rel), os.ModePerm)
			if err != nil {
				fmt.Printf("[%v/%v] Error creating output dir for %s: %v\n", i+1, len(epubs), epub, err)
				errs[rel] = err
				continue
			}

			outfile := fmt.Sprintf("%s.kepub.epub", filepath.Join(outdir, strings.Replace(rel, ".epub", "", -1)))
			fmt.Printf("[%v/%v] Converting %s\n", i+1, len(epubs), rel)

			err = kepub.Kepubify(epub, outfile, false)
			if err != nil {
				fmt.Printf("[%v/%v] Error converting %s: %v\n", i+1, len(epubs), rel, err)
				errs[rel] = err
				continue
			}
		}

		fmt.Printf("\nSucessfully converted %v of %v ebooks\n", len(epubs)-len(errs), len(epubs))
		if len(errs) > 0 {
			fmt.Printf("Errors:\n")
			for epub, err := range errs {
				fmt.Printf("%s: %v\n", epub, err)
			}
		}
	} else {
		if filepath.Ext(infile) != ".epub" {
			return fmt.Errorf("Input file must have the extension \".epub\".")
		}

		outfile := fmt.Sprintf("%s.kepub.epub", strings.Replace(filepath.Base(infile), ".epub", "", -1))
		if len(c.Args()) == 2 {
			if exists(c.Args().Get(1)) {
				if isDir(c.Args().Get(1)) {
					outfile = path.Join(c.Args().Get(1), outfile)
				} else {
					return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
				}
			} else {
				if strings.HasSuffix(c.Args().Get(1), ".kepub.epub") {
					outfile = c.Args().Get(1)
				} else {
					return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
				}
			}
		}

		outfile, err = filepath.Abs(outfile)
		if err != nil {
			return fmt.Errorf("Error resolving output file path: %s.", err)
		}

		fmt.Printf("Input file: %s\n", infile)
		fmt.Printf("Output file: %s\n", outfile)
		fmt.Println()

		err = kepub.Kepubify(infile, outfile, true)
		if err != nil {
			return fmt.Errorf("Error converting epub to kepub: %s.", err)
		}

		fmt.Printf("Successfully converted \"%s\" to a kepub.\nYou can find the converted file at \"%s\"\n", infile, outfile)
	}
	return nil
}

func main() {
	app := cli.NewApp()

	app.Name = "kepubify"
	app.Usage = "Convert epub to kepub"
	app.Description = "Convert your ePubs into kepubs, with a easy-to-use command-line tool."
	app.Version = version

	app.ArgsUsage = "EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]"
	app.Action = func(c *cli.Context) error {
		err := convert(c)
		if err != nil {
			fmt.Println(err)
		}

		if runtime.GOOS == "windows" && len(c.Args()) == 1 {
			time.Sleep(1 * time.Second)
		}

		return err
	}

	app.Run(os.Args)
}
<commit_msg>Fixed typo<commit_after>package main

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/mattn/go-zglob"

	"github.com/geek1011/kepubify/kepub"

	cli "gopkg.in/urfave/cli.v1"
)

var version = "dev"

// exists checks whether a path exists
func exists(path string) bool {
	if _, err := os.Stat(path); !os.IsNotExist(err) {
		return true
	}
	return false
}

// isDir checks if a exists and is a dir
func isDir(path string) bool {
	if fi, err := os.Stat(path); err == nil && fi.IsDir() {
		return true
	}
	return false
}

func convert(c *cli.Context) error {
	if len(c.Args()) < 1 || len(c.Args()) > 2 {
		return fmt.Errorf("Invalid number of arguments. Usage: kepubify EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]")
	}

	infile := c.Args().Get(0)
	if infile == "" {
		return fmt.Errorf("Input path must not be blank.")
	}

	infile, err := filepath.Abs(infile)
	if err != nil {
		return fmt.Errorf("Error resolving input path: %s.", err)
	}

	if !exists(infile) {
		return fmt.Errorf("Input file or directory does not exist.")
	}

	if isDir(infile) {
		if len(c.Args()) != 2 {
			return fmt.Errorf("Because input is a dir, a second argument must be supplied with a nonexistent dir for the conversion output.")
		}

		outdir := c.Args().Get(1)
		if exists(outdir) {
			return fmt.Errorf("Because input is a dir, a second argument must be supplied with a nonexistent dir for the conversion output.")
		}

		outdir, err = filepath.Abs(outdir)
		if err != nil {
			return fmt.Errorf("Error resolving output dir path: %s.", err)
		}

		err := os.Mkdir(outdir, os.ModePerm)
		if err != nil {
			return fmt.Errorf("Error creating output dir: %s.", err)
		}

		epubs, err := zglob.Glob(filepath.Join(infile, "**", "*.epub"))
		if err != nil {
			return fmt.Errorf("Error searching for epubs in input dir: %s.", err)
		}

		errs := map[string]error{}
		for i, epub := range epubs {
			rel, err := filepath.Rel(infile, epub)
			if err != nil {
				fmt.Printf("[%v/%v] Error resolving relative path of %s: %v\n", i+1, len(epubs), epub, err)
				errs[epub] = err
				continue
			}

			err = os.MkdirAll(filepath.Join(outdir, filepath.Dir(rel)), os.ModePerm)
			if err != nil {
				fmt.Printf("[%v/%v] Error creating output dir for %s: %v\n", i+1, len(epubs), epub, err)
				errs[rel] = err
				continue
			}

			outfile := fmt.Sprintf("%s.kepub.epub", filepath.Join(outdir, strings.Replace(rel, ".epub", "", -1)))
			fmt.Printf("[%v/%v] Converting %s\n", i+1, len(epubs), rel)

			err = kepub.Kepubify(epub, outfile, false)
			if err != nil {
				fmt.Printf("[%v/%v] Error converting %s: %v\n", i+1, len(epubs), rel, err)
				errs[rel] = err
				continue
			}
		}

		fmt.Printf("\nSucessfully converted %v of %v ebooks\n", len(epubs)-len(errs), len(epubs))
		if len(errs) > 0 {
			fmt.Printf("Errors:\n")
			for epub, err := range errs {
				fmt.Printf("%s: %v\n", epub, err)
			}
		}
	} else {
		if filepath.Ext(infile) != ".epub" {
			return fmt.Errorf("Input file must have the extension \".epub\".")
		}

		outfile := fmt.Sprintf("%s.kepub.epub", strings.Replace(filepath.Base(infile), ".epub", "", -1))
		if len(c.Args()) == 2 {
			if exists(c.Args().Get(1)) {
				if isDir(c.Args().Get(1)) {
					outfile = path.Join(c.Args().Get(1), outfile)
				} else {
					return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
				}
			} else {
				if strings.HasSuffix(c.Args().Get(1), ".kepub.epub") {
					outfile = c.Args().Get(1)
				} else {
					return fmt.Errorf("Output path must be a nonexistent file ending with .kepub.epub, or be an existing directory")
				}
			}
		}

		outfile, err = filepath.Abs(outfile)
		if err != nil {
			return fmt.Errorf("Error resolving output file path: %s.", err)
		}

		fmt.Printf("Input file: %s\n", infile)
		fmt.Printf("Output file: %s\n", outfile)
		fmt.Println()

		err = kepub.Kepubify(infile, outfile, true)
		if err != nil {
			return fmt.Errorf("Error converting epub to kepub: %s.", err)
		}

		fmt.Printf("Successfully converted \"%s\" to a kepub.\nYou can find the converted file at \"%s\"\n", infile, outfile)
	}
	return nil
}

func main() {
	app := cli.NewApp()

	app.Name = "kepubify"
	app.Usage = "Convert epub to kepub"
	app.Description = "Convert your ePubs into kepubs, with a easy-to-use command-line tool."
	app.Version = version

	app.ArgsUsage = "EPUB_INPUT_PATH [KEPUB_OUTPUT_PATH]"
	app.Action = func(c *cli.Context) error {
		err := convert(c)
		if err != nil {
			fmt.Println(err)
		}

		if runtime.GOOS == "windows" && len(c.Args()) == 1 {
			time.Sleep(1 * time.Second)
		}

		return err
	}

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


<commit_before>package cmd

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"
	"github.com/spf13/cobra"
	"go.pedge.io/lion"
	"go.pedge.io/pb/go/google/protobuf"
	"go.pedge.io/pkg/cobra"
	"go.pedge.io/pkg/exec"
	"go.pedge.io/proto/version"
	"golang.org/x/net/context"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	var verbose bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Envronment variables:
  ADDRESS=0.0.0.0:30650, the server to connect to.
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				grpclog.SetLogger(log.New(ioutil.Discard, "", 0))
				// Silence our FUSE logs
				lion.SetLevel(lion.LevelNone)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")

	pfsCmds := pfscmds.Cmds(address)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	rootCmd.AddCommand(deploycmds.DeployCmd())

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &google_protobuf.Empty{})

			if err != nil {
				fmt.Fprintf(writer, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", address, sanitizeErr(err))
				return writer.Flush()
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewFromAddress(address)
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl get pod -l app=pachd |  awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl port-forward "$pod" %d:650
`, port))
			fmt.Println("Port forwarded, CTRL-C to exit.")
			return pkgexec.RunIO(pkgexec.IO{
				Stdin:  stdin,
				Stderr: os.Stderr,
			}, "sh")
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (protoversion.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return protoversion.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *protoversion.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<commit_msg>Update cmd.go<commit_after>package cmd

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"
	"github.com/spf13/cobra"
	"go.pedge.io/lion"
	"go.pedge.io/pb/go/google/protobuf"
	"go.pedge.io/pkg/cobra"
	"go.pedge.io/pkg/exec"
	"go.pedge.io/proto/version"
	"golang.org/x/net/context"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	var verbose bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Environment variables (and defaults):
  ADDRESS=0.0.0.0:30650, the server to connect to.
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				grpclog.SetLogger(log.New(ioutil.Discard, "", 0))
				// Silence our FUSE logs
				lion.SetLevel(lion.LevelNone)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")

	pfsCmds := pfscmds.Cmds(address)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	rootCmd.AddCommand(deploycmds.DeployCmd())

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &google_protobuf.Empty{})

			if err != nil {
				fmt.Fprintf(writer, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", address, sanitizeErr(err))
				return writer.Flush()
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewFromAddress(address)
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl get pod -l app=pachd |  awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl port-forward "$pod" %d:650
`, port))
			fmt.Println("Port forwarded, CTRL-C to exit.")
			return pkgexec.RunIO(pkgexec.IO{
				Stdin:  stdin,
				Stderr: os.Stderr,
			}, "sh")
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (protoversion.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return protoversion.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *protoversion.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<|endoftext|>


<commit_before>package rabbithole_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/streadway/amqp"
	. "rabbithole"
)

func FindQueueByName(qs []QueueInfo, name string) QueueInfo {
	var q QueueInfo
	for _, i := range qs {
		if i.Name == name {
			q = i
		}
	}

	return q
}

var _ = Describe("Client", func() {
	var (
		rmqc *Client
	)

	BeforeEach(func() {
		rmqc, _ = NewClient("http://127.0.0.1:15672", "guest", "guest")
	})

	Context("GET /overview", func() {
		It("returns decoded response", func() {
			res, err := rmqc.Overview()

			Ω(err).Should(BeNil())

			Ω(res.Node).ShouldNot(BeNil())
			Ω(res.StatisticsDBNode).ShouldNot(BeNil())

			fanoutExchange := ExchangeType{Name: "fanout", Description: "AMQP fanout exchange, as per the AMQP specification", Enabled: true}
			Ω(res.ExchangeTypes).Should(ContainElement(fanoutExchange))

		})
	})

	Context("GET /nodes", func() {
		It("returns decoded response", func() {
			xs, err := rmqc.ListNodes()
			res := xs[0]

			Ω(err).Should(BeNil())

			Ω(res.Name).ShouldNot(BeNil())
			Ω(res.NodeType).Should(Equal("disc"))

			Ω(res.FdUsed).Should(BeNumerically(">=", 0))
			Ω(res.FdTotal).Should(BeNumerically(">", 64))

			Ω(res.MemUsed).Should(BeNumerically(">", 10*1024*1024))
			Ω(res.MemLimit).Should(BeNumerically(">", 64*1024*1024))
			Ω(res.MemAlarm).Should(Equal(false))

			Ω(res.IsRunning).Should(Equal(true))

			Ω(res.SocketsUsed).Should(BeNumerically(">=", 0))
			Ω(res.SocketsTotal).Should(BeNumerically(">=", 1))

		})
	})

	Context("GET /connections when there are active connections", func() {
		It("returns decoded response", func() {
			// this really should be tested with > 1 connection and channel. MK.
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			conn2, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn2.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch2, err := conn2.Channel()
			Ω(err).Should(BeNil())
			defer ch2.Close()

			ch3, err := conn2.Channel()
			Ω(err).Should(BeNil())
			defer ch3.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListConnections()
			Ω(err).Should(BeNil())

			info := xs[0]
			Ω(info.Name).ShouldNot(BeNil())
			Ω(info.Host).Should(Equal("127.0.0.1"))
			Ω(info.UsesTLS).Should(Equal(false))
		})
	})

	Context("GET /channels when there are active connections with open channels", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch2, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch2.Close()

			ch3, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch3.Close()

			ch4, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch4.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			err = ch2.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListChannels()
			Ω(err).Should(BeNil())

			info := xs[0]
			Ω(info.Node).ShouldNot(BeNil())
			Ω(info.User).Should(Equal("guest"))
			Ω(info.Vhost).Should(Equal("/"))

			Ω(info.Transactional).Should(Equal(false))

			Ω(info.UnacknowledgedMessageCount).Should(Equal(0))
			Ω(info.UnconfirmedMessageCount).Should(Equal(0))
			Ω(info.UncommittedMessageCount).Should(Equal(0))
			Ω(info.UncommittedAckCount).Should(Equal(0))
		})
	})

	Context("GET /connections/{name] when connection exists", func() {
		It("returns decoded response", func() {
			// this really should be tested with > 1 connection and channel. MK.
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListConnections()
			Ω(err).Should(BeNil())

			c1 := xs[0]
			info, err := rmqc.GetConnection(c1.Name)
			Ω(err).Should(BeNil())
			Ω(info.Protocol).Should(Equal("AMQP 0-9-1"))
			Ω(info.User).Should(Equal("guest"))
		})
	})

	Context("GET /channels/{name} when channel exists", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListChannels()
			Ω(err).Should(BeNil())

			x := xs[0]
			info, err := rmqc.GetChannel(x.Name)

			Ω(info.Node).ShouldNot(BeNil())
			Ω(info.User).Should(Equal("guest"))
			Ω(info.Vhost).Should(Equal("/"))

			Ω(info.Transactional).Should(Equal(false))

			Ω(info.UnacknowledgedMessageCount).Should(Equal(0))
			Ω(info.UnconfirmedMessageCount).Should(Equal(0))
			Ω(info.UncommittedMessageCount).Should(Equal(0))
			Ω(info.UncommittedAckCount).Should(Equal(0))
		})
	})

	Context("GET /exchanges", func() {
		It("returns decoded response", func() {
			xs, err := rmqc.ListExchanges()
			Ω(err).Should(BeNil())

			x := xs[0]
			Ω(x.Name).Should(Equal(""))
			Ω(x.Durable).Should(Equal(true))
		})
	})

	Context("GET /exchanges/{vhost}", func() {
		It("returns decoded response", func() {
			xs, err := rmqc.ListExchangesIn("/")
			Ω(err).Should(BeNil())

			x := xs[0]
			Ω(x.Name).Should(Equal(""))
			Ω(x.Durable).Should(Equal(true))
		})
	})

	Context("GET /exchanges/{vhost}/{name}", func() {
		It("returns decoded response", func() {
			x, err := rmqc.GetExchange("rabbit/hole", "amq.fanout")
			Ω(err).Should(BeNil())
			Ω(x.Name).Should(Equal("amq.fanout"))
			Ω(x.Durable).Should(Equal(true))
			Ω(x.Type).Should(Equal("fanout"))
			Ω(x.Vhost).Should(Equal("rabbit/hole"))
		})
	})

	Context("GET /queues", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch.QueueDeclare(
				"q1",  // name
				false, // durable
				false, // delete when usused
				true,  // exclusive
				false,
				nil)

			qs, err := rmqc.ListQueues()
			Ω(err).Should(BeNil())

			q := qs[0]
			Ω(q.Name).ShouldNot(Equal(""))
			Ω(q.Node).ShouldNot(BeNil())
			Ω(q.Durable).ShouldNot(BeNil())
			Ω(q.Status).ShouldNot(BeNil())
		})
	})

	Context("GET /queues/{vhost}", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/rabbit%2Fhole")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch.QueueDeclare(
				"q1",  // name
				false, // durable
				false, // delete when usused
				true,  // exclusive
				false,
				nil)

			qs, err := rmqc.ListQueuesIn("rabbit/hole")
			Ω(err).Should(BeNil())

			q := FindQueueByName(qs, "q1")
			Ω(q.Name).Should(Equal("q1"))
			Ω(q.Vhost).Should(Equal("rabbit/hole"))
			Ω(q.Durable).Should(Equal(false))
			Ω(q.Status).ShouldNot(BeNil())
		})
	})

	Context("GET /queues/{vhost}/{name}", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/rabbit%2Fhole")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch.QueueDeclare(
				"q1",  // name
				false, // durable
				false, // delete when usused
				true,  // exclusive
				false,
				nil)

			q, err := rmqc.GetQueue("rabbit/hole", "q1")
			Ω(err).Should(BeNil())
			Ω(q).Should(BeNil())

			Ω(q.Name).Should(Equal("q1"))
			Ω(q.Vhost).Should(Equal("rabbit/hole"))
			Ω(q.Durable).Should(Equal(false))
			Ω(q.Status).ShouldNot(BeNil())
		})
	})
})
<commit_msg>Remove a debug line<commit_after>package rabbithole_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/streadway/amqp"
	. "rabbithole"
)

func FindQueueByName(qs []QueueInfo, name string) QueueInfo {
	var q QueueInfo
	for _, i := range qs {
		if i.Name == name {
			q = i
		}
	}

	return q
}

var _ = Describe("Client", func() {
	var (
		rmqc *Client
	)

	BeforeEach(func() {
		rmqc, _ = NewClient("http://127.0.0.1:15672", "guest", "guest")
	})

	Context("GET /overview", func() {
		It("returns decoded response", func() {
			res, err := rmqc.Overview()

			Ω(err).Should(BeNil())

			Ω(res.Node).ShouldNot(BeNil())
			Ω(res.StatisticsDBNode).ShouldNot(BeNil())

			fanoutExchange := ExchangeType{Name: "fanout", Description: "AMQP fanout exchange, as per the AMQP specification", Enabled: true}
			Ω(res.ExchangeTypes).Should(ContainElement(fanoutExchange))

		})
	})

	Context("GET /nodes", func() {
		It("returns decoded response", func() {
			xs, err := rmqc.ListNodes()
			res := xs[0]

			Ω(err).Should(BeNil())

			Ω(res.Name).ShouldNot(BeNil())
			Ω(res.NodeType).Should(Equal("disc"))

			Ω(res.FdUsed).Should(BeNumerically(">=", 0))
			Ω(res.FdTotal).Should(BeNumerically(">", 64))

			Ω(res.MemUsed).Should(BeNumerically(">", 10*1024*1024))
			Ω(res.MemLimit).Should(BeNumerically(">", 64*1024*1024))
			Ω(res.MemAlarm).Should(Equal(false))

			Ω(res.IsRunning).Should(Equal(true))

			Ω(res.SocketsUsed).Should(BeNumerically(">=", 0))
			Ω(res.SocketsTotal).Should(BeNumerically(">=", 1))

		})
	})

	Context("GET /connections when there are active connections", func() {
		It("returns decoded response", func() {
			// this really should be tested with > 1 connection and channel. MK.
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			conn2, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn2.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch2, err := conn2.Channel()
			Ω(err).Should(BeNil())
			defer ch2.Close()

			ch3, err := conn2.Channel()
			Ω(err).Should(BeNil())
			defer ch3.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListConnections()
			Ω(err).Should(BeNil())

			info := xs[0]
			Ω(info.Name).ShouldNot(BeNil())
			Ω(info.Host).Should(Equal("127.0.0.1"))
			Ω(info.UsesTLS).Should(Equal(false))
		})
	})

	Context("GET /channels when there are active connections with open channels", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch2, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch2.Close()

			ch3, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch3.Close()

			ch4, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch4.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			err = ch2.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListChannels()
			Ω(err).Should(BeNil())

			info := xs[0]
			Ω(info.Node).ShouldNot(BeNil())
			Ω(info.User).Should(Equal("guest"))
			Ω(info.Vhost).Should(Equal("/"))

			Ω(info.Transactional).Should(Equal(false))

			Ω(info.UnacknowledgedMessageCount).Should(Equal(0))
			Ω(info.UnconfirmedMessageCount).Should(Equal(0))
			Ω(info.UncommittedMessageCount).Should(Equal(0))
			Ω(info.UncommittedAckCount).Should(Equal(0))
		})
	})

	Context("GET /connections/{name] when connection exists", func() {
		It("returns decoded response", func() {
			// this really should be tested with > 1 connection and channel. MK.
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListConnections()
			Ω(err).Should(BeNil())

			c1 := xs[0]
			info, err := rmqc.GetConnection(c1.Name)
			Ω(err).Should(BeNil())
			Ω(info.Protocol).Should(Equal("AMQP 0-9-1"))
			Ω(info.User).Should(Equal("guest"))
		})
	})

	Context("GET /channels/{name} when channel exists", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			err = ch.Publish("",
				"",
				false,
				false,
				amqp.Publishing{Body: []byte("")})
			Ω(err).Should(BeNil())

			xs, err := rmqc.ListChannels()
			Ω(err).Should(BeNil())

			x := xs[0]
			info, err := rmqc.GetChannel(x.Name)

			Ω(info.Node).ShouldNot(BeNil())
			Ω(info.User).Should(Equal("guest"))
			Ω(info.Vhost).Should(Equal("/"))

			Ω(info.Transactional).Should(Equal(false))

			Ω(info.UnacknowledgedMessageCount).Should(Equal(0))
			Ω(info.UnconfirmedMessageCount).Should(Equal(0))
			Ω(info.UncommittedMessageCount).Should(Equal(0))
			Ω(info.UncommittedAckCount).Should(Equal(0))
		})
	})

	Context("GET /exchanges", func() {
		It("returns decoded response", func() {
			xs, err := rmqc.ListExchanges()
			Ω(err).Should(BeNil())

			x := xs[0]
			Ω(x.Name).Should(Equal(""))
			Ω(x.Durable).Should(Equal(true))
		})
	})

	Context("GET /exchanges/{vhost}", func() {
		It("returns decoded response", func() {
			xs, err := rmqc.ListExchangesIn("/")
			Ω(err).Should(BeNil())

			x := xs[0]
			Ω(x.Name).Should(Equal(""))
			Ω(x.Durable).Should(Equal(true))
		})
	})

	Context("GET /exchanges/{vhost}/{name}", func() {
		It("returns decoded response", func() {
			x, err := rmqc.GetExchange("rabbit/hole", "amq.fanout")
			Ω(err).Should(BeNil())
			Ω(x.Name).Should(Equal("amq.fanout"))
			Ω(x.Durable).Should(Equal(true))
			Ω(x.Type).Should(Equal("fanout"))
			Ω(x.Vhost).Should(Equal("rabbit/hole"))
		})
	})

	Context("GET /queues", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch.QueueDeclare(
				"q1",  // name
				false, // durable
				false, // delete when usused
				true,  // exclusive
				false,
				nil)

			qs, err := rmqc.ListQueues()
			Ω(err).Should(BeNil())

			q := qs[0]
			Ω(q.Name).ShouldNot(Equal(""))
			Ω(q.Node).ShouldNot(BeNil())
			Ω(q.Durable).ShouldNot(BeNil())
			Ω(q.Status).ShouldNot(BeNil())
		})
	})

	Context("GET /queues/{vhost}", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/rabbit%2Fhole")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch.QueueDeclare(
				"q1",  // name
				false, // durable
				false, // delete when usused
				true,  // exclusive
				false,
				nil)

			qs, err := rmqc.ListQueuesIn("rabbit/hole")
			Ω(err).Should(BeNil())

			q := FindQueueByName(qs, "q1")
			Ω(q.Name).Should(Equal("q1"))
			Ω(q.Vhost).Should(Equal("rabbit/hole"))
			Ω(q.Durable).Should(Equal(false))
			Ω(q.Status).ShouldNot(BeNil())
		})
	})

	Context("GET /queues/{vhost}/{name}", func() {
		It("returns decoded response", func() {
			conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/rabbit%2Fhole")
			Ω(err).Should(BeNil())
			defer conn.Close()

			ch, err := conn.Channel()
			Ω(err).Should(BeNil())
			defer ch.Close()

			ch.QueueDeclare(
				"q1",  // name
				false, // durable
				false, // delete when usused
				true,  // exclusive
				false,
				nil)

			q, err := rmqc.GetQueue("rabbit/hole", "q1")
			Ω(err).Should(BeNil())

			Ω(q.Name).Should(Equal("q1"))
			Ω(q.Vhost).Should(Equal("rabbit/hole"))
			Ω(q.Durable).Should(Equal(false))
			Ω(q.Status).ShouldNot(BeNil())
		})
	})
})
<|endoftext|>


<commit_before>// The term package implements rendering HTML content for the terminal.
package term

import (
	"bytes"
	"errors"
	"io"
	"runtime"
	"strings"
	"unicode"

	"github.com/PuerkitoBio/goquery"
	"github.com/andybalholm/cascadia"
	"golang.org/x/net/html"
)

// Renderer implements a rendering HTML content in a stripped down format
// suitable for the terminal.
type Renderer struct {
	// Width sets the assumed width of the terminal, which aids in wrapping
	// text. If == 0, then the width is treated as 80. If < 0, then the width is
	// treated as unbounded.
	Width int
	// TabSize indicates the number of characters that the terminal uses to
	// render a tab. If <= 0, then tabs are assumed to have a size of 8.
	TabSize int
}

var (
	stop         = errors.New("stop")          // Stop walking without erroring.
	skipChildren = errors.New("skip children") // Skip all child nodes.
	skipText     = errors.New("skip text")     // Skip child text nodes.
)

type walker func(node *html.Node, entering bool) error

func walk(node *html.Node, cb walker) error {
	err := cb(node, true)
	if err != nil && err != skipChildren && err != skipText {
		return err
	}
	if err != skipChildren {
		for c := node.FirstChild; c != nil; c = c.NextSibling {
			if c.Type == html.TextNode && err == skipText {
				continue
			}
			if err := walk(c, cb); err != nil {
				return err
			}
		}
	}
	if err = cb(node, false); err != nil && err != skipChildren && err != skipText {
		return err
	}
	return err
}

// Facilitates wrapped text.
type writer struct {
	bytes.Buffer
	w io.Writer

	// If wrapping, wrap to this width.
	width int

	// Size of tab character.
	tabSize int
}

func (w *writer) Flush() error {
	b := w.Buffer.Bytes()
	defer w.Buffer.Reset()

	b = bytes.TrimSpace(b)
	var paragraphs [][]rune
	for i := 0; i < len(b); {
		if b[i] == '\n' {
			// Bounds check unneeded; if b[i], being a \n, was at the end, it
			// would have been trimmed.
			if b[i+1] == '\n' {
				// New paragraph.
				paragraphs = append(paragraphs, []rune(string(b[:i])))
				i += 2
				for b[i] == '\n' {
					// Collapse extra newlines.
					i++
				}
				b = b[i:]
				i = 0
				continue
			} else {
				// Unwrap.
				b[i] = ' '
			}
		}
		i++
	}
	paragraphs = append(paragraphs, []rune(string(b)))
	if w.width > 0 {
		for j, p := range paragraphs {
			for i := 0; i < len(p); {
				n := i + w.width
				if n+1 >= len(p) {
					break
				}
				for n > i && !unicode.IsSpace(p[n]) {
					n--
				}
				if n <= i {
					// Long word.
					n = i + w.width
					p = append(p, 0)
					copy(p[n+1:], p[n:])
					p[n] = '\n'
					i = n + 1
				} else {
					p[n] = '\n'
					i = n + 1
				}
			}
			paragraphs[j] = p
		}
		if runtime.GOOS == "windows" {
			// Remove newlines that are inserted just after the edge of the
			// width. This prevents the terminal from incorrectly producing an
			// extra gap when wrapping. This removes the newline entirely, so it
			// is assumed that the wrapping will separate the previous word from
			// the next word.
			for i, p := range paragraphs {
				for h, i := 0, 0; i < len(p); i++ {
					if p[i] != '\n' {
						continue
					}
					if i-h >= w.width {
						copy(p[i:], p[i+1:])
						p = p[:len(p)-1]
						h = i
						i--
					} else {
						h = i + 1
					}
				}
				paragraphs[i] = p
			}
		}
	}
	for i, p := range paragraphs {
		if i > 0 {
			w.w.Write([]byte{'\n', '\n'})
		}
		if _, err := w.w.Write([]byte(string(p))); err != nil {
			return err
		}
	}
	return nil
}

var sectionCounter = cascadia.MustCompile("body > section")

func (r Renderer) Render(w io.Writer, s *goquery.Selection) error {
	buf := &writer{
		w:       w,
		width:   r.Width,
		tabSize: r.TabSize,
	}
	var state walkState
	for _, node := range s.Nodes {
		if node.Type == html.TextNode {
			continue
		}
		// If the body contains more than one section, increase the depth to
		// force the section names to be rendered.
		isRoot := s.FindMatcher(sectionCounter).Length() > 1
		if isRoot {
			state.depth++
		}
		err := walk(node, func(node *html.Node, entering bool) error {
			switch node.Type {
			case html.ErrorNode:
			case html.TextNode:
				if entering {
					buf.WriteString(node.Data)
				}
			case html.DocumentNode:
			case html.ElementNode:
				h := handlers[elementMatcher{node.Data, entering}]
				if h != nil {
					return h(buf, node, &state)
				}
			case html.CommentNode:
			case html.DoctypeNode:
			case html.RawNode:
			}
			return nil
		})
		if err != nil && err != stop {
			return err
		}
		if isRoot {
			state.depth--
		}
	}
	return buf.Flush()
}

type elementMatcher struct {
	data     string
	entering bool
}

type walkState struct {
	depth int
}

type nodeHandlers map[elementMatcher]func(w *writer, node *html.Node, s *walkState) error

func isElement(node *html.Node, tag string) bool {
	return node.Type == html.ElementNode && node.Data == tag
}

func sectionName(node *html.Node) string {
	for _, attr := range node.Attr {
		if attr.Key == "data-name" {
			return attr.Val
		}
	}
	return ""
}

var handlers = nodeHandlers{
	{"code", true}: func(w *writer, node *html.Node, s *walkState) error {
		if isElement(node.Parent, "pre") { // May have syntax: `class="language-*"`
			// Block
			w.WriteString(node.FirstChild.Data)
			return skipChildren
		} else {
			// Inline
			return w.WriteByte('`')
		}
	},
	{"code", false}: func(w *writer, node *html.Node, s *walkState) error {
		if isElement(node.Parent, "pre") {
			// Block
		} else {
			// Inline
			return w.WriteByte('`')
		}
		return nil
	},
	{"section", true}: func(w *writer, node *html.Node, s *walkState) error {
		if s.depth > 0 {
			if name := sectionName(node); name != "" {
				w.WriteString(strings.Repeat("#", s.depth))
				w.WriteString(" ")
				w.WriteString(name)
				w.WriteString("\n\n")
			}
		}
		s.depth++
		return skipText
	},
	{"section", false}: func(w *writer, node *html.Node, s *walkState) error {
		s.depth--
		return nil
	},
	{"p", true}: func(w *writer, node *html.Node, s *walkState) error {
		_, err := w.WriteString("\n\n")
		return err
	},
	{"p", false}: func(w *writer, node *html.Node, s *walkState) error {
		_, err := w.WriteString("\n\n")
		return err
	},
}
<commit_msg>Render with current terminal width if specified width is less than 0.<commit_after>// The term package implements rendering HTML content for the terminal.
package term

import (
	"bytes"
	"errors"
	"io"
	"os"
	"runtime"
	"strings"
	"unicode"

	"github.com/PuerkitoBio/goquery"
	"github.com/andybalholm/cascadia"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/net/html"
)

// Renderer implements a rendering HTML content in a stripped down format
// suitable for the terminal.
type Renderer struct {
	// Width sets the assumed width of the terminal, which aids in wrapping
	// text. If == 0, then the width is treated as 80. If < 0, then the width is
	// treated as unbounded.
	Width int
	// TabSize indicates the number of characters that the terminal uses to
	// render a tab. If <= 0, then tabs are assumed to have a size of 8.
	TabSize int
}

var (
	stop         = errors.New("stop")          // Stop walking without erroring.
	skipChildren = errors.New("skip children") // Skip all child nodes.
	skipText     = errors.New("skip text")     // Skip child text nodes.
)

type walker func(node *html.Node, entering bool) error

func walk(node *html.Node, cb walker) error {
	err := cb(node, true)
	if err != nil && err != skipChildren && err != skipText {
		return err
	}
	if err != skipChildren {
		for c := node.FirstChild; c != nil; c = c.NextSibling {
			if c.Type == html.TextNode && err == skipText {
				continue
			}
			if err := walk(c, cb); err != nil {
				return err
			}
		}
	}
	if err = cb(node, false); err != nil && err != skipChildren && err != skipText {
		return err
	}
	return err
}

// Facilitates wrapped text.
type writer struct {
	bytes.Buffer
	w io.Writer

	// If wrapping, wrap to this width.
	width int

	// Size of tab character.
	tabSize int
}

func (w *writer) Flush() error {
	b := w.Buffer.Bytes()
	defer w.Buffer.Reset()

	b = bytes.TrimSpace(b)
	var paragraphs [][]rune
	for i := 0; i < len(b); {
		if b[i] == '\n' {
			// Bounds check unneeded; if b[i], being a \n, was at the end, it
			// would have been trimmed.
			if b[i+1] == '\n' {
				// New paragraph.
				paragraphs = append(paragraphs, []rune(string(b[:i])))
				i += 2
				for b[i] == '\n' {
					// Collapse extra newlines.
					i++
				}
				b = b[i:]
				i = 0
				continue
			} else {
				// Unwrap.
				b[i] = ' '
			}
		}
		i++
	}
	paragraphs = append(paragraphs, []rune(string(b)))
	if w.width > 0 {
		for j, p := range paragraphs {
			for i := 0; i < len(p); {
				n := i + w.width
				if n+1 >= len(p) {
					break
				}
				for n > i && !unicode.IsSpace(p[n]) {
					n--
				}
				if n <= i {
					// Long word.
					n = i + w.width
					p = append(p, 0)
					copy(p[n+1:], p[n:])
					p[n] = '\n'
					i = n + 1
				} else {
					p[n] = '\n'
					i = n + 1
				}
			}
			paragraphs[j] = p
		}
		if runtime.GOOS == "windows" {
			// Remove newlines that are inserted just after the edge of the
			// width. This prevents the terminal from incorrectly producing an
			// extra gap when wrapping. This removes the newline entirely, so it
			// is assumed that the wrapping will separate the previous word from
			// the next word.
			for i, p := range paragraphs {
				for h, i := 0, 0; i < len(p); i++ {
					if p[i] != '\n' {
						continue
					}
					if i-h >= w.width {
						copy(p[i:], p[i+1:])
						p = p[:len(p)-1]
						h = i
						i--
					} else {
						h = i + 1
					}
				}
				paragraphs[i] = p
			}
		}
	}
	for i, p := range paragraphs {
		if i > 0 {
			w.w.Write([]byte{'\n', '\n'})
		}
		if _, err := w.w.Write([]byte(string(p))); err != nil {
			return err
		}
	}
	return nil
}

var sectionCounter = cascadia.MustCompile("body > section")

func (r Renderer) Render(w io.Writer, s *goquery.Selection) error {
	buf := &writer{
		w:       w,
		width:   r.Width,
		tabSize: r.TabSize,
	}
	if buf.width < 0 {
		buf.width, _, _ = terminal.GetSize(int(os.Stdout.Fd()))
	}
	var state walkState
	for _, node := range s.Nodes {
		if node.Type == html.TextNode {
			continue
		}
		// If the body contains more than one section, increase the depth to
		// force the section names to be rendered.
		isRoot := s.FindMatcher(sectionCounter).Length() > 1
		if isRoot {
			state.depth++
		}
		err := walk(node, func(node *html.Node, entering bool) error {
			switch node.Type {
			case html.ErrorNode:
			case html.TextNode:
				if entering {
					buf.WriteString(node.Data)
				}
			case html.DocumentNode:
			case html.ElementNode:
				h := handlers[elementMatcher{node.Data, entering}]
				if h != nil {
					return h(buf, node, &state)
				}
			case html.CommentNode:
			case html.DoctypeNode:
			case html.RawNode:
			}
			return nil
		})
		if err != nil && err != stop {
			return err
		}
		if isRoot {
			state.depth--
		}
	}
	return buf.Flush()
}

type elementMatcher struct {
	data     string
	entering bool
}

type walkState struct {
	depth int
}

type nodeHandlers map[elementMatcher]func(w *writer, node *html.Node, s *walkState) error

func isElement(node *html.Node, tag string) bool {
	return node.Type == html.ElementNode && node.Data == tag
}

func sectionName(node *html.Node) string {
	for _, attr := range node.Attr {
		if attr.Key == "data-name" {
			return attr.Val
		}
	}
	return ""
}

var handlers = nodeHandlers{
	{"code", true}: func(w *writer, node *html.Node, s *walkState) error {
		if isElement(node.Parent, "pre") { // May have syntax: `class="language-*"`
			// Block
			w.WriteString(node.FirstChild.Data)
			return skipChildren
		} else {
			// Inline
			return w.WriteByte('`')
		}
	},
	{"code", false}: func(w *writer, node *html.Node, s *walkState) error {
		if isElement(node.Parent, "pre") {
			// Block
		} else {
			// Inline
			return w.WriteByte('`')
		}
		return nil
	},
	{"section", true}: func(w *writer, node *html.Node, s *walkState) error {
		if s.depth > 0 {
			if name := sectionName(node); name != "" {
				w.WriteString(strings.Repeat("#", s.depth))
				w.WriteString(" ")
				w.WriteString(name)
				w.WriteString("\n\n")
			}
		}
		s.depth++
		return skipText
	},
	{"section", false}: func(w *writer, node *html.Node, s *walkState) error {
		s.depth--
		return nil
	},
	{"p", true}: func(w *writer, node *html.Node, s *walkState) error {
		_, err := w.WriteString("\n\n")
		return err
	},
	{"p", false}: func(w *writer, node *html.Node, s *walkState) error {
		_, err := w.WriteString("\n\n")
		return err
	},
}
<|endoftext|>