1 package toml
2
3 import (
4 "fmt"
5 "strings"
6 )
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 type ParseError struct {
53 Message string
54 Usage string
55 Position Position
56 LastKey string
57
58
59
60
61 Line int
62
63 err error
64 input string
65 }
66
67
68 type Position struct {
69 Line int
70 Start int
71 Len int
72 }
73
74 func (pe ParseError) Error() string {
75 msg := pe.Message
76 if msg == "" {
77 msg = pe.err.Error()
78 }
79
80 if pe.LastKey == "" {
81 return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg)
82 }
83 return fmt.Sprintf("toml: line %d (last key %q): %s",
84 pe.Position.Line, pe.LastKey, msg)
85 }
86
87
88
89
90 func (pe ParseError) ErrorWithPosition() string {
91 if pe.input == "" {
92 return pe.Error()
93 }
94
95 var (
96 lines = strings.Split(pe.input, "\n")
97 col = pe.column(lines)
98 b = new(strings.Builder)
99 )
100
101 msg := pe.Message
102 if msg == "" {
103 msg = pe.err.Error()
104 }
105
106
107
108
109 if pe.Position.Len == 1 {
110 fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n",
111 msg, pe.Position.Line, col+1)
112 } else {
113 fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n",
114 msg, pe.Position.Line, col, col+pe.Position.Len)
115 }
116 if pe.Position.Line > 2 {
117 fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, lines[pe.Position.Line-3])
118 }
119 if pe.Position.Line > 1 {
120 fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, lines[pe.Position.Line-2])
121 }
122 fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, lines[pe.Position.Line-1])
123 fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col), strings.Repeat("^", pe.Position.Len))
124 return b.String()
125 }
126
127
128
129
130
131 func (pe ParseError) ErrorWithUsage() string {
132 m := pe.ErrorWithPosition()
133 if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" {
134 lines := strings.Split(strings.TrimSpace(u.Usage()), "\n")
135 for i := range lines {
136 if lines[i] != "" {
137 lines[i] = " " + lines[i]
138 }
139 }
140 return m + "Error help:\n\n" + strings.Join(lines, "\n") + "\n"
141 }
142 return m
143 }
144
145 func (pe ParseError) column(lines []string) int {
146 var pos, col int
147 for i := range lines {
148 ll := len(lines[i]) + 1
149 if pos+ll >= pe.Position.Start {
150 col = pe.Position.Start - pos
151 if col < 0 {
152 col = 0
153 }
154 break
155 }
156 pos += ll
157 }
158
159 return col
160 }
161
162 type (
163 errLexControl struct{ r rune }
164 errLexEscape struct{ r rune }
165 errLexUTF8 struct{ b byte }
166 errLexInvalidNum struct{ v string }
167 errLexInvalidDate struct{ v string }
168 errLexInlineTableNL struct{}
169 errLexStringNL struct{}
170 errParseRange struct {
171 i interface{}
172 size string
173 }
174 errParseDuration struct{ d string }
175 )
176
177 func (e errLexControl) Error() string {
178 return fmt.Sprintf("TOML files cannot contain control characters: '0x%02x'", e.r)
179 }
180 func (e errLexControl) Usage() string { return "" }
181
182 func (e errLexEscape) Error() string { return fmt.Sprintf(`invalid escape in string '\%c'`, e.r) }
183 func (e errLexEscape) Usage() string { return usageEscape }
184 func (e errLexUTF8) Error() string { return fmt.Sprintf("invalid UTF-8 byte: 0x%02x", e.b) }
185 func (e errLexUTF8) Usage() string { return "" }
186 func (e errLexInvalidNum) Error() string { return fmt.Sprintf("invalid number: %q", e.v) }
187 func (e errLexInvalidNum) Usage() string { return "" }
188 func (e errLexInvalidDate) Error() string { return fmt.Sprintf("invalid date: %q", e.v) }
189 func (e errLexInvalidDate) Usage() string { return "" }
190 func (e errLexInlineTableNL) Error() string { return "newlines not allowed within inline tables" }
191 func (e errLexInlineTableNL) Usage() string { return usageInlineNewline }
192 func (e errLexStringNL) Error() string { return "strings cannot contain newlines" }
193 func (e errLexStringNL) Usage() string { return usageStringNewline }
194 func (e errParseRange) Error() string { return fmt.Sprintf("%v is out of range for %s", e.i, e.size) }
195 func (e errParseRange) Usage() string { return usageIntOverflow }
196 func (e errParseDuration) Error() string { return fmt.Sprintf("invalid duration: %q", e.d) }
197 func (e errParseDuration) Usage() string { return usageDuration }
198
199 const usageEscape = `
200 A '\' inside a "-delimited string is interpreted as an escape character.
201
202 The following escape sequences are supported:
203 \b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX
204
205 To prevent a '\' from being recognized as an escape character, use either:
206
207 - a ' or '''-delimited string; escape characters aren't processed in them; or
208 - write two backslashes to get a single backslash: '\\'.
209
210 If you're trying to add a Windows path (e.g. "C:\Users\martin") then using '/'
211 instead of '\' will usually also work: "C:/Users/martin".
212 `
213
214 const usageInlineNewline = `
215 Inline tables must always be on a single line:
216
217 table = {key = 42, second = 43}
218
219 It is invalid to split them over multiple lines like so:
220
221 # INVALID
222 table = {
223 key = 42,
224 second = 43
225 }
226
227 Use regular for this:
228
229 [table]
230 key = 42
231 second = 43
232 `
233
234 const usageStringNewline = `
235 Strings must always be on a single line, and cannot span more than one line:
236
237 # INVALID
238 string = "Hello,
239 world!"
240
241 Instead use """ or ''' to split strings over multiple lines:
242
243 string = """Hello,
244 world!"""
245 `
246
247 const usageIntOverflow = `
248 This number is too large; this may be an error in the TOML, but it can also be a
249 bug in the program that uses too small of an integer.
250
251 The maximum and minimum values are:
252
253 size │ lowest │ highest
254 ───────┼────────────────┼──────────
255 int8 │ -128 │ 127
256 int16 │ -32,768 │ 32,767
257 int32 │ -2,147,483,648 │ 2,147,483,647
258 int64 │ -9.2 × 10¹⁷ │ 9.2 × 10¹⁷
259 uint8 │ 0 │ 255
260 uint16 │ 0 │ 65535
261 uint32 │ 0 │ 4294967295
262 uint64 │ 0 │ 1.8 × 10¹⁸
263
264 int refers to int32 on 32-bit systems and int64 on 64-bit systems.
265 `
266
267 const usageDuration = `
268 A duration must be as "number<unit>", without any spaces. Valid units are:
269
270 ns nanoseconds (billionth of a second)
271 us, µs microseconds (millionth of a second)
272 ms milliseconds (thousands of a second)
273 s seconds
274 m minutes
275 h hours
276
277 You can combine multiple units; for example "5m10s" for 5 minutes and 10
278 seconds.
279 `
280
View as plain text