Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/pelletier/go-toml/v2/errors.go
2880 views
1
package toml
2
3
import (
4
"fmt"
5
"strconv"
6
"strings"
7
8
"github.com/pelletier/go-toml/v2/internal/danger"
9
"github.com/pelletier/go-toml/v2/unstable"
10
)
11
12
// DecodeError represents an error encountered during the parsing or decoding
13
// of a TOML document.
14
//
15
// In addition to the error message, it contains the position in the document
16
// where it happened, as well as a human-readable representation that shows
17
// where the error occurred in the document.
18
type DecodeError struct {
19
message string
20
line int
21
column int
22
key Key
23
24
human string
25
}
26
27
// StrictMissingError occurs in a TOML document that does not have a
28
// corresponding field in the target value. It contains all the missing fields
29
// in Errors.
30
//
31
// Emitted by Decoder when DisallowUnknownFields() was called.
32
type StrictMissingError struct {
33
// One error per field that could not be found.
34
Errors []DecodeError
35
}
36
37
// Error returns the canonical string for this error.
38
func (s *StrictMissingError) Error() string {
39
return "strict mode: fields in the document are missing in the target struct"
40
}
41
42
// String returns a human readable description of all errors.
43
func (s *StrictMissingError) String() string {
44
var buf strings.Builder
45
46
for i, e := range s.Errors {
47
if i > 0 {
48
buf.WriteString("\n---\n")
49
}
50
51
buf.WriteString(e.String())
52
}
53
54
return buf.String()
55
}
56
57
type Key []string
58
59
// Error returns the error message contained in the DecodeError.
60
func (e *DecodeError) Error() string {
61
return "toml: " + e.message
62
}
63
64
// String returns the human-readable contextualized error. This string is multi-line.
65
func (e *DecodeError) String() string {
66
return e.human
67
}
68
69
// Position returns the (line, column) pair indicating where the error
70
// occurred in the document. Positions are 1-indexed.
71
func (e *DecodeError) Position() (row int, column int) {
72
return e.line, e.column
73
}
74
75
// Key that was being processed when the error occurred. The key is present only
76
// if this DecodeError is part of a StrictMissingError.
77
func (e *DecodeError) Key() Key {
78
return e.key
79
}
80
81
// decodeErrorFromHighlight creates a DecodeError referencing a highlighted
82
// range of bytes from document.
83
//
84
// highlight needs to be a sub-slice of document, or this function panics.
85
//
86
// The function copies all bytes used in DecodeError, so that document and
87
// highlight can be freely deallocated.
88
//
89
//nolint:funlen
90
func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError {
91
offset := danger.SubsliceOffset(document, de.Highlight)
92
93
errMessage := de.Error()
94
errLine, errColumn := positionAtEnd(document[:offset])
95
before, after := linesOfContext(document, de.Highlight, offset, 3)
96
97
var buf strings.Builder
98
99
maxLine := errLine + len(after) - 1
100
lineColumnWidth := len(strconv.Itoa(maxLine))
101
102
// Write the lines of context strictly before the error.
103
for i := len(before) - 1; i > 0; i-- {
104
line := errLine - i
105
buf.WriteString(formatLineNumber(line, lineColumnWidth))
106
buf.WriteString("|")
107
108
if len(before[i]) > 0 {
109
buf.WriteString(" ")
110
buf.Write(before[i])
111
}
112
113
buf.WriteRune('\n')
114
}
115
116
// Write the document line that contains the error.
117
118
buf.WriteString(formatLineNumber(errLine, lineColumnWidth))
119
buf.WriteString("| ")
120
121
if len(before) > 0 {
122
buf.Write(before[0])
123
}
124
125
buf.Write(de.Highlight)
126
127
if len(after) > 0 {
128
buf.Write(after[0])
129
}
130
131
buf.WriteRune('\n')
132
133
// Write the line with the error message itself (so it does not have a line
134
// number).
135
136
buf.WriteString(strings.Repeat(" ", lineColumnWidth))
137
buf.WriteString("| ")
138
139
if len(before) > 0 {
140
buf.WriteString(strings.Repeat(" ", len(before[0])))
141
}
142
143
buf.WriteString(strings.Repeat("~", len(de.Highlight)))
144
145
if len(errMessage) > 0 {
146
buf.WriteString(" ")
147
buf.WriteString(errMessage)
148
}
149
150
// Write the lines of context strictly after the error.
151
152
for i := 1; i < len(after); i++ {
153
buf.WriteRune('\n')
154
line := errLine + i
155
buf.WriteString(formatLineNumber(line, lineColumnWidth))
156
buf.WriteString("|")
157
158
if len(after[i]) > 0 {
159
buf.WriteString(" ")
160
buf.Write(after[i])
161
}
162
}
163
164
return &DecodeError{
165
message: errMessage,
166
line: errLine,
167
column: errColumn,
168
key: de.Key,
169
human: buf.String(),
170
}
171
}
172
173
func formatLineNumber(line int, width int) string {
174
format := "%" + strconv.Itoa(width) + "d"
175
176
return fmt.Sprintf(format, line)
177
}
178
179
func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) {
180
return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround)
181
}
182
183
func beforeLines(document []byte, offset int, linesAround int) [][]byte {
184
var beforeLines [][]byte
185
186
// Walk the document backward from the highlight to find previous lines
187
// of context.
188
rest := document[:offset]
189
backward:
190
for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; {
191
switch {
192
case rest[o] == '\n':
193
// handle individual lines
194
beforeLines = append(beforeLines, rest[o+1:])
195
rest = rest[:o]
196
o = len(rest) - 1
197
case o == 0:
198
// add the first line only if it's non-empty
199
beforeLines = append(beforeLines, rest)
200
201
break backward
202
default:
203
o--
204
}
205
}
206
207
return beforeLines
208
}
209
210
func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte {
211
var afterLines [][]byte
212
213
// Walk the document forward from the highlight to find the following
214
// lines of context.
215
rest := document[offset+len(highlight):]
216
forward:
217
for o := 0; o < len(rest) && len(afterLines) <= linesAround; {
218
switch {
219
case rest[o] == '\n':
220
// handle individual lines
221
afterLines = append(afterLines, rest[:o])
222
rest = rest[o+1:]
223
o = 0
224
225
case o == len(rest)-1:
226
// add last line only if it's non-empty
227
afterLines = append(afterLines, rest)
228
229
break forward
230
default:
231
o++
232
}
233
}
234
235
return afterLines
236
}
237
238
func positionAtEnd(b []byte) (row int, column int) {
239
row = 1
240
column = 1
241
242
for _, c := range b {
243
if c == '\n' {
244
row++
245
column = 1
246
} else {
247
column++
248
}
249
}
250
251
return
252
}
253
254