-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathe.go
124 lines (111 loc) · 2.97 KB
/
e.go
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
// Copyright 2016 aletheia7. All rights reserved. Use of this source code is
// governed by a BSD-2-Clause license that can be found in the LICENSE file.
// Package errors augments an error with the file and line number of where it
// occurred.
package errors
import (
"fmt"
"runtime"
"strings"
)
type e_stack struct {
err error
file string
line int
}
func (o *e_stack) Error() string {
return fmt.Sprintf("%v %v:%v", o.err.Error(), o.file, o.line)
}
type causer interface {
Cause() error
}
// Cause returns the original error without the file and line number
//
func Cause(err error) error {
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
return cause.Cause()
}
return err
}
// Implements causer
//
func (o *e_stack) Cause() error {
return o.err
}
// Adds file/line number to the error
//
func Wrap(err error) error {
if err == nil {
return nil
}
r := &e_stack{err: err}
r.file, r.line = file_line()
return r
}
// New returns a new error
//
func New(msg string) error {
r := &e_stack{err: fmt.Errorf(msg)}
r.file, r.line = file_line()
return r
}
// Errorf returns a new error with format
//
func Errorf(format string, args ...interface{}) error {
r := &e_stack{err: fmt.Errorf(format, args...)}
r.file, r.line = file_line()
return r
}
func file_line() (file string, line int) {
pc := make([]uintptr, 1)
n := runtime.Callers(3, pc)
if n == 0 {
return ``, 0
}
frames := runtime.CallersFrames(pc[:n])
frame, _ := frames.Next()
return trim_go_path(frame.Function, frame.File), frame.Line
}
func trim_go_path(name, file string) string {
// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(name, sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]
return file
}