go-yaml/errors/error.go
2019-10-23 16:40:26 +09:00

158 lines
3.3 KiB
Go

package errors
import (
"fmt"
"github.com/goccy/go-yaml/printer"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
)
var (
// ColoredErr error with syntax highlight
ColoredErr = true
// WithSourceCode error with source code
WithSourceCode = true
// ErrDecodeRequiredPointerType error instance for decoding
ErrDecodeRequiredPointerType = xerrors.New("required pointer type value")
)
// Wrapf wrap error for stack trace
func Wrapf(err error, msg string, args ...interface{}) error {
return &wrapError{
baseError: &baseError{},
err: xerrors.Errorf(msg, args...),
nextErr: err,
frame: xerrors.Caller(1),
}
}
// ErrSyntax create syntax error instance with message and token
func ErrSyntax(msg string, tk *token.Token) *syntaxError {
return &syntaxError{
baseError: &baseError{},
msg: msg,
token: tk,
frame: xerrors.Caller(1),
}
}
type baseError struct {
state fmt.State
verb rune
}
func (e *baseError) Error() string {
return ""
}
func (e *baseError) chainStateAndVerb(err error) {
wrapErr, ok := err.(*wrapError)
if ok {
wrapErr.state = e.state
wrapErr.verb = e.verb
}
syntaxErr, ok := err.(*syntaxError)
if ok {
syntaxErr.state = e.state
syntaxErr.verb = e.verb
}
}
type wrapError struct {
*baseError
err error
nextErr error
frame xerrors.Frame
}
func (e *wrapError) FormatError(p xerrors.Printer) error {
if e.verb == 'v' && e.state.Flag('+') {
// print stack trace for debugging
p.Print(e.err, "\n")
e.frame.Format(p)
e.chainStateAndVerb(e.nextErr)
return e.nextErr
}
err := e.nextErr
for {
if wrapErr, ok := err.(*wrapError); ok {
err = wrapErr.nextErr
continue
}
break
}
e.chainStateAndVerb(err)
if fmtErr, ok := err.(xerrors.Formatter); ok {
fmtErr.FormatError(p)
} else {
p.Print(err)
}
return nil
}
type wrapState struct {
org fmt.State
}
func (s *wrapState) Write(b []byte) (n int, err error) {
return s.org.Write(b)
}
func (s *wrapState) Width() (wid int, ok bool) {
return s.org.Width()
}
func (s *wrapState) Precision() (prec int, ok bool) {
return s.org.Precision()
}
func (s *wrapState) Flag(c int) bool {
// set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text
if c == '#' {
// ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail.
// ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 )
return false
}
return true
}
func (e *wrapError) Format(state fmt.State, verb rune) {
e.state = state
e.verb = verb
xerrors.FormatError(e, &wrapState{org: state}, verb)
}
func (e *wrapError) Error() string {
return e.err.Error()
}
type syntaxError struct {
*baseError
msg string
token *token.Token
frame xerrors.Frame
}
func (e *syntaxError) FormatError(p xerrors.Printer) error {
if e.verb == 'v' && e.state.Flag('+') {
// %+v
// print stack trace for debugging
p.Print(e.Error())
e.frame.Format(p)
} else {
p.Print(e.Error())
}
return nil
}
func (e *syntaxError) Error() string {
var p printer.Printer
pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column)
msg := p.PrintErrorMessage(fmt.Sprintf("syntax error: %s%s", pos, e.msg), ColoredErr)
if WithSourceCode {
err := p.PrintErrorToken(e.token, ColoredErr)
return fmt.Sprintf("%s\n%s", msg, err)
}
return msg
}