mirror of
https://github.com/goccy/go-yaml.git
synced 2025-10-29 12:24:27 +00:00
158 lines
3.3 KiB
Go
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
|
|
}
|