forked from pSpaces/goSpace
-
Notifications
You must be signed in to change notification settings - Fork 0
/
space_error.go
128 lines (106 loc) · 3.08 KB
/
space_error.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
125
126
127
128
package space
import (
"fmt"
"reflect"
"runtime"
"strings"
)
// SpaceError represents an internal error type used when printing error messages.
type SpaceError struct {
msg string
pkg string
fun string
sid string
val string
sop bool
status interface{}
}
const (
SpaceInvalid = iota
SpaceNoErrorMethod
SpaceOperationFailed
)
// errMsg contains all the generic error messages when operating on spaces.
var errMsg = map[int]string{
SpaceInvalid: "operation performed on an invalid space",
SpaceNoErrorMethod: "no error method available, can't interpret error",
SpaceOperationFailed: "could not perform operation on this space",
}
// NewSpaceError creates a new error given space spc, a value used in an operation and the return state of the implemented operation.
// NewSpaceError returns a structure which fulfils the error interface and if an operation error has occured.
// NewSpaceError returns nil if no operation failure has occured.
func NewSpaceError(spc *Space, value interface{}, state interface{}) error {
var msg, pkg, fun, sid, val string
var err error
var sop bool
var status interface{}
pkg, fun = getCalleInfo(2)
if spc == nil {
sid = "nil"
msg = errMsg[SpaceInvalid]
status = nil
} else {
sid = (*spc).id
spct := reflect.ValueOf(spc)
method := spct.MethodByName("InterpretError")
if method.IsValid() {
vals := method.Call([]reflect.Value{reflect.ValueOf(state)})
if len(vals) == 1 {
msg = vals[0].String()
} else {
msg = errMsg[SpaceNoErrorMethod]
}
} else {
msg = errMsg[SpaceNoErrorMethod]
}
method = spct.MethodByName("InterpretOperation")
if method.IsValid() {
vals := method.Call([]reflect.Value{reflect.ValueOf(state)})
if len(vals) == 1 {
sop = vals[0].Interface().(bool)
} else {
sop = false
}
} else {
sop = false
}
method = spct.MethodByName("InterpretValue")
if method.IsValid() {
vals := method.Call([]reflect.Value{reflect.ValueOf(value)})
if len(vals) == 1 {
val = vals[0].Interface().(string)
} else {
val = ""
}
} else {
val = ""
}
status = state
}
if sop == true {
err = nil
} else {
err = SpaceError{msg, pkg, fun, sid, val, sop, status}
}
return err
}
// Operation returns a boolean value if an operation has succeeded.
func (e SpaceError) Operation() bool {
return e.sop
}
// Error prints the error message represented by SpaceError.
func (e SpaceError) Error() string {
sep := strings.Repeat(" ", 2)
return fmt.Sprintf("\n%s%s:\n%s%s%s(%s).%s%s: %s.", sep, e.pkg, sep, sep, "Space", e.sid, e.fun, e.val, e.msg)
}
// getCalleInfo determines the package and function names associated to a function call.
// getCalleInfo uses the runtime package, and no file or line information is provided,
// since this can not be guaranteed due to compiler optimizations.
func getCalleInfo(depth int) (pkg string, fun string) {
fpc, _, _, _ := runtime.Caller(depth)
fname := runtime.FuncForPC(fpc).Name()
fparts := strings.Split(fname, ".")
pkg = strings.Join(fparts[:len(fparts)-2], ".")
fun = fparts[len(fparts)-1]
return pkg, fun
}