Skip to content

Commit

Permalink
feat: Expose syntax and type errors (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
nieomylnieja authored Jul 5, 2024
1 parent 5cf06f1 commit 99bcbfd
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 7 deletions.
8 changes: 8 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,10 @@ func (e *unknownFieldError) Error() string {
return e.err.Error()
}

func (e *unknownFieldError) Unwrap() error {
return e.err
}

func errUnknownField(msg string, tk *token.Token) *unknownFieldError {
return &unknownFieldError{err: errors.ErrSyntax(msg, tk)}
}
Expand All @@ -550,6 +554,10 @@ func (e *duplicateKeyError) Error() string {
return e.err.Error()
}

func (e *duplicateKeyError) Unwrap() error {
return e.err
}

func errDuplicateKey(msg string, tk *token.Token) *duplicateKeyError {
return &duplicateKeyError{err: errors.ErrSyntax(msg, tk)}
}
Expand Down
41 changes: 41 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package yaml

import (
"github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
)

Expand Down Expand Up @@ -60,3 +62,42 @@ func IsInvalidAnchorNameError(err error) bool {
func IsInvalidAliasNameError(err error) bool {
return xerrors.Is(err, ast.ErrInvalidAliasName)
}

// TokenScopedError represents an error associated with a specific [token.Token].
type TokenScopedError struct {
// Msg is the underlying error message.
Msg string
// Token is the [token.Token] associated with this error.
Token *token.Token
// err is the underlying, unwraped error.
err error
}

// Error implements the error interface.
// It returns the unwraped error returned by go-yaml.
func (s TokenScopedError) Error() string {
return s.err.Error()
}

// AsTokenScopedError checks if the error is associated with a specific token.
// If so, it returns
// Otherwise, it returns nil.
func AsTokenScopedError(err error) *TokenScopedError {
var syntaxError *errors.SyntaxError
if xerrors.As(err, &syntaxError) {
return &TokenScopedError{
Msg: syntaxError.GetMessage(),
Token: syntaxError.GetToken(),
err: err,
}
}
var typeError *errors.TypeError
if xerrors.As(err, &typeError) {
return &TokenScopedError{
Msg: typeError.Error(),
Token: typeError.Token,
err: err,
}
}
return nil
}
78 changes: 78 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package yaml

import (
"reflect"
"testing"

"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
)

func TestAsSyntaxError(t *testing.T) {
tests := []struct {
input error
expected *TokenScopedError
}{
{
input: nil,
expected: nil,
},
{
input: xerrors.New("dummy test"),
expected: nil,
},
{
input: errors.ErrSyntax("some error", &token.Token{Value: "123"}),
expected: &TokenScopedError{
Msg: "some error",
Token: &token.Token{Value: "123"},
},
},
{
input: xerrors.Errorf(
"something went wrong: %w",
errors.ErrSyntax("some error", &token.Token{Value: "123"})),
expected: &TokenScopedError{
Msg: "some error",
Token: &token.Token{Value: "123"},
},
},
{
input: errUnknownField("unknown field", &token.Token{Value: "123"}),
expected: &TokenScopedError{
Msg: "unknown field",
Token: &token.Token{Value: "123"},
},
},
{
input: errDuplicateKey("duplicate key", &token.Token{Value: "123"}),
expected: &TokenScopedError{
Msg: "duplicate key",
Token: &token.Token{Value: "123"},
},
},
{
input: errTypeMismatch(reflect.TypeOf("string"), reflect.TypeOf(0), &token.Token{Value: "123"}),
expected: &TokenScopedError{
Msg: "cannot unmarshal int into Go value of type string",
Token: &token.Token{Value: "123"},
},
},
}
for _, test := range tests {
syntaxErr := AsTokenScopedError(test.input)
if test.expected == nil {
if syntaxErr != nil {
t.Fatalf("wanted nil, but go %v", syntaxErr)
}
continue
}
if syntaxErr == nil {
t.Fatalf("must not be nil")
}
if *test.expected.Token != *syntaxErr.Token || test.expected.Msg != syntaxErr.Msg {
t.Fatalf("unexpected output.\nexpect:\n[%v]\nactual:\n[%v]", test.expected, syntaxErr)
}
}
}
22 changes: 15 additions & 7 deletions internal/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ func Wrapf(err error, msg string, args ...interface{}) error {
}

// ErrSyntax create syntax error instance with message and token
func ErrSyntax(msg string, tk *token.Token) *syntaxError {
return &syntaxError{
func ErrSyntax(msg string, tk *token.Token) *SyntaxError {
return &SyntaxError{
baseError: &baseError{},
msg: msg,
token: tk,
Expand All @@ -54,7 +54,7 @@ func (e *baseError) chainStateAndVerb(err error) {
wrapErr.state = e.state
wrapErr.verb = e.verb
}
syntaxErr, ok := err.(*syntaxError)
syntaxErr, ok := err.(*SyntaxError)
if ok {
syntaxErr.state = e.state
syntaxErr.verb = e.verb
Expand Down Expand Up @@ -164,18 +164,18 @@ func (e *wrapError) Error() string {
return buf.String()
}

type syntaxError struct {
type SyntaxError struct {
*baseError
msg string
token *token.Token
frame xerrors.Frame
}

func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
func (e *SyntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
}

func (e *syntaxError) FormatError(p xerrors.Printer) error {
func (e *SyntaxError) FormatError(p xerrors.Printer) error {
var pp printer.Printer

var colored, inclSource bool
Expand All @@ -199,6 +199,14 @@ func (e *syntaxError) FormatError(p xerrors.Printer) error {
return nil
}

func (e *SyntaxError) GetToken() *token.Token {
return e.token
}

func (e *SyntaxError) GetMessage() string {
return e.msg
}

type PrettyPrinter interface {
PrettyPrint(xerrors.Printer, bool, bool) error
}
Expand All @@ -216,7 +224,7 @@ func (es *Sink) Detail() bool {
return false
}

func (e *syntaxError) Error() string {
func (e *SyntaxError) Error() string {
var buf bytes.Buffer
e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource)
return buf.String()
Expand Down

0 comments on commit 99bcbfd

Please sign in to comment.