error.go raw
1 package errors
2
3 import (
4 "bytes"
5 "fmt"
6 "reflect"
7
8 "github.com/goccy/go-yaml/printer"
9 "github.com/goccy/go-yaml/token"
10 "golang.org/x/xerrors"
11 )
12
13 const (
14 defaultColorize = false
15 defaultIncludeSource = true
16 )
17
18 var (
19 ErrDecodeRequiredPointerType = xerrors.New("required pointer type value")
20 )
21
22 // Wrapf wrap error for stack trace
23 func Wrapf(err error, msg string, args ...interface{}) error {
24 return &wrapError{
25 baseError: &baseError{},
26 err: xerrors.Errorf(msg, args...),
27 nextErr: err,
28 frame: xerrors.Caller(1),
29 }
30 }
31
32 // ErrSyntax create syntax error instance with message and token
33 func ErrSyntax(msg string, tk *token.Token) *syntaxError {
34 return &syntaxError{
35 baseError: &baseError{},
36 msg: msg,
37 token: tk,
38 frame: xerrors.Caller(1),
39 }
40 }
41
42 type baseError struct {
43 state fmt.State
44 verb rune
45 }
46
47 func (e *baseError) Error() string {
48 return ""
49 }
50
51 func (e *baseError) chainStateAndVerb(err error) {
52 wrapErr, ok := err.(*wrapError)
53 if ok {
54 wrapErr.state = e.state
55 wrapErr.verb = e.verb
56 }
57 syntaxErr, ok := err.(*syntaxError)
58 if ok {
59 syntaxErr.state = e.state
60 syntaxErr.verb = e.verb
61 }
62 }
63
64 type wrapError struct {
65 *baseError
66 err error
67 nextErr error
68 frame xerrors.Frame
69 }
70
71 type FormatErrorPrinter struct {
72 xerrors.Printer
73 Colored bool
74 InclSource bool
75 }
76
77 func (e *wrapError) As(target interface{}) bool {
78 err := e.nextErr
79 for {
80 if wrapErr, ok := err.(*wrapError); ok {
81 err = wrapErr.nextErr
82 continue
83 }
84 break
85 }
86 return xerrors.As(err, target)
87 }
88
89 func (e *wrapError) Unwrap() error {
90 return e.nextErr
91 }
92
93 func (e *wrapError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
94 return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
95 }
96
97 func (e *wrapError) FormatError(p xerrors.Printer) error {
98 if _, ok := p.(*FormatErrorPrinter); !ok {
99 p = &FormatErrorPrinter{
100 Printer: p,
101 Colored: defaultColorize,
102 InclSource: defaultIncludeSource,
103 }
104 }
105 if e.verb == 'v' && e.state.Flag('+') {
106 // print stack trace for debugging
107 p.Print(e.err, "\n")
108 e.frame.Format(p)
109 e.chainStateAndVerb(e.nextErr)
110 return e.nextErr
111 }
112 err := e.nextErr
113 for {
114 if wrapErr, ok := err.(*wrapError); ok {
115 err = wrapErr.nextErr
116 continue
117 }
118 break
119 }
120 e.chainStateAndVerb(err)
121 if fmtErr, ok := err.(xerrors.Formatter); ok {
122 fmtErr.FormatError(p)
123 } else {
124 p.Print(err)
125 }
126 return nil
127 }
128
129 type wrapState struct {
130 org fmt.State
131 }
132
133 func (s *wrapState) Write(b []byte) (n int, err error) {
134 return s.org.Write(b)
135 }
136
137 func (s *wrapState) Width() (wid int, ok bool) {
138 return s.org.Width()
139 }
140
141 func (s *wrapState) Precision() (prec int, ok bool) {
142 return s.org.Precision()
143 }
144
145 func (s *wrapState) Flag(c int) bool {
146 // set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text
147 if c == '#' {
148 // ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail.
149 // ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 )
150 return false
151 }
152 return true
153 }
154
155 func (e *wrapError) Format(state fmt.State, verb rune) {
156 e.state = state
157 e.verb = verb
158 xerrors.FormatError(e, &wrapState{org: state}, verb)
159 }
160
161 func (e *wrapError) Error() string {
162 var buf bytes.Buffer
163 e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource)
164 return buf.String()
165 }
166
167 type syntaxError struct {
168 *baseError
169 msg string
170 token *token.Token
171 frame xerrors.Frame
172 }
173
174 func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
175 return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
176 }
177
178 func (e *syntaxError) FormatError(p xerrors.Printer) error {
179 var pp printer.Printer
180
181 var colored, inclSource bool
182 if fep, ok := p.(*FormatErrorPrinter); ok {
183 colored = fep.Colored
184 inclSource = fep.InclSource
185 }
186
187 pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column)
188 msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.msg), colored)
189 if inclSource {
190 msg += "\n" + pp.PrintErrorToken(e.token, colored)
191 }
192 p.Print(msg)
193
194 if e.verb == 'v' && e.state.Flag('+') {
195 // %+v
196 // print stack trace for debugging
197 e.frame.Format(p)
198 }
199 return nil
200 }
201
202 type PrettyPrinter interface {
203 PrettyPrint(xerrors.Printer, bool, bool) error
204 }
205 type Sink struct{ *bytes.Buffer }
206
207 func (es *Sink) Print(args ...interface{}) {
208 fmt.Fprint(es.Buffer, args...)
209 }
210
211 func (es *Sink) Printf(f string, args ...interface{}) {
212 fmt.Fprintf(es.Buffer, f, args...)
213 }
214
215 func (es *Sink) Detail() bool {
216 return false
217 }
218
219 func (e *syntaxError) Error() string {
220 var buf bytes.Buffer
221 e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource)
222 return buf.String()
223 }
224
225 type TypeError struct {
226 DstType reflect.Type
227 SrcType reflect.Type
228 StructFieldName *string
229 Token *token.Token
230 }
231
232 func (e *TypeError) Error() string {
233 if e.StructFieldName != nil {
234 return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType)
235 }
236 return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType)
237 }
238
239 func (e *TypeError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
240 return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
241 }
242
243 func (e *TypeError) FormatError(p xerrors.Printer) error {
244 var pp printer.Printer
245
246 var colored, inclSource bool
247 if fep, ok := p.(*FormatErrorPrinter); ok {
248 colored = fep.Colored
249 inclSource = fep.InclSource
250 }
251
252 pos := fmt.Sprintf("[%d:%d] ", e.Token.Position.Line, e.Token.Position.Column)
253 msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored)
254 if inclSource {
255 msg += "\n" + pp.PrintErrorToken(e.Token, colored)
256 }
257 p.Print(msg)
258
259 return nil
260 }
261