-
-
Notifications
You must be signed in to change notification settings - Fork 280
Expand file tree
/
Copy patherror.go
More file actions
133 lines (121 loc) · 3.16 KB
/
error.go
File metadata and controls
133 lines (121 loc) · 3.16 KB
1
2
3
4
5
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package pongo2
import (
"bufio"
"fmt"
"io"
"os"
)
// The Error type is being used to address an error during lexing, parsing or
// execution. If you want to return an error object (for example in your own
// tag or filter) fill this object with as much information as you have.
// Make sure "Sender" is always given (if you're returning an error within
// a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag').
// It's okay if you only fill in ErrorMsg if you don't have any other details at hand.
type Error struct {
Template *Template
Filename string
Line int
Column int
Token *Token
Sender string
OrigError error
}
// updateFromTokenIfNeeded updates the error with template and token information
// if they haven't been set yet. This helps provide better error location context.
func (e *Error) updateFromTokenIfNeeded(template *Template, t *Token) *Error {
if e.Template == nil {
e.Template = template
}
if e.Token == nil {
e.Token = t
if e.Line <= 0 {
e.Line = t.Line
e.Column = t.Col
}
}
return e
}
// updateErrorToken is a helper that updates token info on a *Error if the error
// is of that type, otherwise returns the error as-is.
func updateErrorToken(err error, template *Template, t *Token) error {
if err == nil {
return nil
}
if e, ok := err.(*Error); ok {
return e.updateFromTokenIfNeeded(template, t)
}
return err
}
// Unwrap returns the underlying error for use with errors.Is and errors.As.
func (e *Error) Unwrap() error {
return e.OrigError
}
// Returns a nice formatted error string.
func (e *Error) Error() string {
s := "[Error"
if e.Sender != "" {
s += " (where: " + e.Sender + ")"
}
if e.Filename != "" {
s += " in " + e.Filename
}
if e.Line > 0 {
s += fmt.Sprintf(" | Line %d Col %d", e.Line, e.Column)
if e.Token != nil {
s += fmt.Sprintf(" near '%s'", e.Token.Val)
}
}
s += "] "
s += e.OrigError.Error()
return s
}
// RawLine returns the affected line from the original template, if available.
func (e *Error) RawLine() (line string, available bool, outErr error) {
if e.Line <= 0 || e.Filename == "<string>" {
return "", false, nil
}
filename := e.Filename
if e.Template != nil {
filename = e.Template.set.resolveFilename(e.Template, e.Filename)
}
// Try to get the file through the template's loader first (supports fs.FS),
// falling back to os.Open for backwards compatibility
var reader io.Reader
if e.Template != nil && e.Template.set != nil {
_, _, fd, err := e.Template.set.resolveTemplate(e.Template, e.Filename)
if err == nil {
reader = fd
// If reader implements io.Closer, ensure we close it
if closer, ok := reader.(io.Closer); ok {
defer func() {
err := closer.Close()
if err != nil && outErr == nil {
outErr = err
}
}()
}
}
}
if reader == nil {
file, err := os.Open(filename)
if err != nil {
return "", false, err
}
defer func() {
err := file.Close()
if err != nil && outErr == nil {
outErr = err
}
}()
reader = file
}
scanner := bufio.NewScanner(reader)
l := 0
for scanner.Scan() {
l++
if l == e.Line {
return scanner.Text(), true, nil
}
}
return "", false, nil
}