podman

Форк
0
252 строки · 5.9 Кб
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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.