func (ec ErrorContext) MethodArgs() []interface{} {
	return ec.methodArgs()
}
MethodArgs returns the input parameters of the method that caused the error.
package pinata
import (
	"bytes"
	"fmt"
	"strings"
)
type Stick interface {
	Error() error
	ClearError() error
	PathString(Pinata, ...string) string
	String(Pinata) string
	IndexString(Pinata, int) string
	PathFloat64(Pinata, ...string) float64
	Float64(Pinata) float64
	IndexFloat64(Pinata, int) float64
	PathBool(Pinata, ...string) bool
	Bool(Pinata) bool
	IndexBool(Pinata, int) bool
	PathNil(Pinata, ...string)
	Nil(Pinata)
	IndexNil(Pinata, int)
	Path(Pinata, ...string) Pinata
	Index(Pinata, int) Pinata
}
type stick struct {
	err error
}
func (s *stick) ClearError() error {
	err := s.err
	s.err = nil
	return err
}
func (s *stick) Error() error {
	return s.err
}
func (s *stick) unsupported(errCtx *ErrorContext, methodName string, input func() []interface{}, advice string) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: input,
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: advice,
	}
}
func (s *stick) indexUnsupported(errCtx *ErrorContext, methodName string, index int) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return []interface{}{index} },
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: "call this method on a slice pinata",
	}
}
func (s *stick) pathUnsupported(errCtx *ErrorContext, methodName string, path []string) {
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       errCtx,
		},
		reason: ErrorReasonIncompatibleType,
		advice: "call this method on a map pinata",
	}
}
func (s *stick) internalString(p Pinata, methodName string, input func() []interface{}) string {
	if _, ok := p.Map(); ok {
		s.unsupported(p.context, methodName, input, "this is a map")
		return ""
	}
	if _, ok := p.Slice(); ok {
		s.unsupported(p.context, methodName, input, "this is a slice")
		return ""
	}
	if v, ok := p.Value().(string); ok {
		return v
	}
	s.unsupported(p.context, methodName, input, "this is not a string")
	return ""
}
func (s *stick) internalFloat64(p Pinata, methodName string, input func() []interface{}) float64 {
	if _, ok := p.Map(); ok {
		s.unsupported(p.context, methodName, input, "this is a map")
		return 0
	}
	if _, ok := p.Slice(); ok {
		s.unsupported(p.context, methodName, input, "this is a slice")
		return 0
	}
	if v, ok := p.Value().(float64); ok {
		return v
	}
	s.unsupported(p.context, methodName, input, "this is not a float64")
	return 0
}
func (s *stick) internalBool(p Pinata, methodName string, input func() []interface{}) bool {
	if _, ok := p.Map(); ok {
		s.unsupported(p.context, methodName, input, "this is a map")
		return false
	}
	if _, ok := p.Slice(); ok {
		s.unsupported(p.context, methodName, input, "this is a slice")
		return false
	}
	if v, ok := p.Value().(bool); ok {
		return v
	}
	s.unsupported(p.context, methodName, input, "this is not a bool")
	return false
}
func (s *stick) internalNil(p Pinata, methodName string, input func() []interface{}) {
	if p.Value() == nil {
		return
	}
	if _, ok := p.Map(); ok {
		s.unsupported(p.context, methodName, input, "this is a map")
	}
	if _, ok := p.Slice(); ok {
		s.unsupported(p.context, methodName, input, "this is a slice")
	}
	s.unsupported(p.context, methodName, input, "this is not nil")
}
func (s *stick) String(p Pinata) string {
	if s.err != nil {
		return ""
	}
	return s.internalString(p, "String", func() []interface{} { return nil })
}
func (s *stick) Bool(p Pinata) bool {
	if s.err != nil {
		return false
	}
	return s.internalBool(p, "Bool", func() []interface{} { return nil })
}
func (s *stick) Float64(p Pinata) float64 {
	if s.err != nil {
		return 0
	}
	return s.internalFloat64(p, "Float64", func() []interface{} { return nil })
}
func (s *stick) Nil(p Pinata) {
	if s.err != nil {
		return
	}
	s.internalNil(p, "Nil", func() []interface{} { return nil })
}
func (s *stick) internalIndex(p Pinata, methodName string, index int) Pinata {
	if slice, ok := p.Slice(); ok {
		if index < 0 || index >= len(slice) {
			s.err = &Error{
				context: &ErrorContext{
					methodName: methodName,
					methodArgs: func() []interface{} { return []interface{}{index} },
					next:       p.context,
				},
				reason: ErrorReasonInvalidInput,
				advice: fmt.Sprintf("specify an index from 0 to %d", len(slice)-1),
			}
			return Pinata{}
		}
		return newPinataWithContext(slice[index], &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return []interface{}{index} },
			next:       p.context,
		})
	}
	s.indexUnsupported(p.context, methodName, index)
	return Pinata{}
}
func (s *stick) Index(p Pinata, index int) Pinata {
	if s.err != nil {
		return Pinata{}
	}
	return s.internalIndex(p, "Index", index)
}
func (s *stick) IndexString(p Pinata, index int) string {
	if s.err != nil {
		return ""
	}
	const methodName = "IndexString"
	pinata := s.internalIndex(p, methodName, index)
	if s.err != nil {
		return ""
	}
	pinata.context = p.context
	return s.internalString(pinata, methodName, func() []interface{} { return []interface{}{index} })
}
func (s *stick) IndexFloat64(p Pinata, index int) float64 {
	if s.err != nil {
		return 0
	}
	const methodName = "IndexFloat64"
	pinata := s.internalIndex(p, methodName, index)
	if s.err != nil {
		return 0
	}
	pinata.context = p.context
	return s.internalFloat64(pinata, methodName, func() []interface{} { return []interface{}{index} })
}
func (s *stick) IndexBool(p Pinata, index int) bool {
	if s.err != nil {
		return false
	}
	const methodName = "IndexBool"
	pinata := s.internalIndex(p, methodName, index)
	if s.err != nil {
		return false
	}
	pinata.context = p.context
	return s.internalBool(pinata, methodName, func() []interface{} { return []interface{}{index} })
}
func (s *stick) IndexNil(p Pinata, index int) {
	if s.err != nil {
		return
	}
	const methodName = "IndexNil"
	pinata := s.internalIndex(p, methodName, index)
	if s.err != nil {
		return
	}
	pinata.context = p.context
	s.internalNil(pinata, methodName, func() []interface{} { return []interface{}{index} })
}
func (s *stick) internalPath(p Pinata, methodName string, path ...string) Pinata {
	contents, ok := p.Map()
	if !ok {
		s.pathUnsupported(p.context, methodName, path)
		return Pinata{}
	}
	if len(path) == 0 {
		s.err = &Error{
			context: &ErrorContext{
				methodName: methodName,
				methodArgs: func() []interface{} { return toInterfaceSlice(path) },
				next:       p.context,
			},
			reason: ErrorReasonInvalidInput,
			advice: "specify a path",
		}
		return Pinata{}
	}
	for i := 0; i < len(path)-1; i++ {
		current := path[i]
		if v, ok := contents[current]; ok {
			if v, ok := v.(map[string]interface{}); ok {
				contents = v
			} else {
				s.err = &Error{
					context: &ErrorContext{
						methodName: methodName,
						methodArgs: func() []interface{} { return toInterfaceSlice(path) },
						next:       p.context,
					},
					reason: ErrorReasonIncompatibleType,
					advice: fmt.Sprintf(`"%s" does not hold a pinata`, strings.Join(path[:i+1], `", "`)),
				}
				return Pinata{}
			}
		} else {
			s.err = &Error{
				context: &ErrorContext{
					methodName: methodName,
					methodArgs: func() []interface{} { return toInterfaceSlice(path) },
					next:       p.context,
				},
				reason: ErrorReasonNotFound,
				advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path[:i+1], `", "`)),
			}
			return Pinata{}
		}
	}
	if v, ok := contents[path[len(path)-1]]; ok {
		return newPinataWithContext(v, &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       p.context,
		})
	}
	s.err = &Error{
		context: &ErrorContext{
			methodName: methodName,
			methodArgs: func() []interface{} { return toInterfaceSlice(path) },
			next:       p.context,
		},
		reason: ErrorReasonNotFound,
		advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path, `", "`)),
	}
	return Pinata{}
}
func (s *stick) Path(p Pinata, path ...string) Pinata {
	if s.err != nil {
		return Pinata{}
	}
	return s.internalPath(p, "Path", path...)
}
func (s *stick) PathString(p Pinata, path ...string) string {
	if s.err != nil {
		return ""
	}
	const methodName = "PathString"
	pinata := s.internalPath(p, methodName, path...)
	if s.err != nil {
		return ""
	}
	pinata.context = p.context
	return s.internalString(pinata, methodName, func() []interface{} { return toInterfaceSlice(path) })
}
func (s *stick) PathFloat64(p Pinata, path ...string) float64 {
	if s.err != nil {
		return 0
	}
	const methodName = "PathFloat64"
	pinata := s.internalPath(p, methodName, path...)
	if s.err != nil {
		return 0
	}
	pinata.context = p.context
	return s.internalFloat64(pinata, methodName, func() []interface{} { return toInterfaceSlice(path) })
}
func (s *stick) PathBool(p Pinata, path ...string) bool {
	if s.err != nil {
		return false
	}
	const methodName = "PathBool"
	pinata := s.internalPath(p, methodName, path...)
	if s.err != nil {
		return false
	}
	pinata.context = p.context
	return s.internalBool(pinata, methodName, func() []interface{} { return toInterfaceSlice(path) })
}
func (s *stick) PathNil(p Pinata, path ...string) {
	if s.err != nil {
		return
	}
	const methodName = "PathNil"
	pinata := s.internalPath(p, methodName, path...)
	if s.err != nil {
		return
	}
	pinata.context = p.context
	s.internalNil(pinata, methodName, func() []interface{} { return toInterfaceSlice(path) })
}
type Pinata struct {
	context   *ErrorContext
	value     interface{}
	mapFunc   func() (map[string]interface{}, bool)
	sliceFunc func() ([]interface{}, bool)
}
func (p Pinata) Value() interface{} {
	return p.value
}
func (p Pinata) Map() (map[string]interface{}, bool) {
	if p.mapFunc != nil {
		return p.mapFunc()
	}
	return noMap()
}
func (p Pinata) Slice() ([]interface{}, bool) {
	if p.sliceFunc != nil {
		return p.sliceFunc()
	}
	return noSlice()
}
func New(contents interface{}) (Stick, Pinata) {
	return NewStick(), NewPinata(contents)
}
func NewStick() Stick {
	return &stick{}
}
func NewPinata(contents interface{}) Pinata {
	return newPinataWithContext(contents, nil)
}
func noMap() (map[string]interface{}, bool) { return nil, false }
func noSlice() ([]interface{}, bool)        { return nil, false }
func newPinataWithContext(contents interface{}, context *ErrorContext) Pinata {
	switch t := contents.(type) {
	case map[string]interface{}:
		return Pinata{
			value:     t,
			sliceFunc: noSlice,
			mapFunc: func() (map[string]interface{}, bool) {
				return t, true
			},
			context: context,
		}
	case []interface{}:
		return Pinata{
			value: t,
			sliceFunc: func() ([]interface{}, bool) {
				return t, true
			},
			mapFunc: noMap,
			context: context,
		}
	default:
		return Pinata{
			value:     t,
			sliceFunc: noSlice,
			mapFunc:   noMap,
			context:   context,
		}
	}
}
type ErrorReason string
const (
	ErrorReasonIncompatibleType ErrorReason = "incompatible type"
	ErrorReasonNotFound = "not found"
	ErrorReasonInvalidInput = "invalid input"
)
type ErrorContext struct {
	methodName string
	methodArgs func() []interface{}
	next       *ErrorContext
}
func (ec ErrorContext) MethodName() string {
	return ec.methodName
}