blob: 514bbc76391e6647d89d0f64d6d38d5047642056 [file] [log] [blame]
package errors
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"strings"
)
type Params map[string]interface{}
const (
ErrcodeNamespace = "ERRCODE"
DefaultErrorNamespace = "ERR"
)
const (
ErrcodeParseTmplError = 1
ErrcodeExecTmpleError = 2
)
var (
errorTemplate = make(map[string]*ErrCodeTemplate)
errCodeDefined = make(map[string]bool)
)
type ErrCode interface {
Id() string
Code() uint64
Namespace() string
Error() string
StackTrace() string
Context() ErrorContext
FullError() error
Append(err ...interface{}) ErrCode
WithContext(k string, v interface{}) ErrCode
Marshal() ([]byte, error)
}
var (
_ ErrCode = (*errorCode)(nil)
)
type Error struct {
ID string `json:"id"`
Namespace string `json:"namespace"`
Code uint64 `json:"code"`
Message string `json:"message"`
}
type errorCode struct {
err Error
stackTrace string
context map[string]interface{}
errors []string
}
func NewErrorCode(id string, code uint64, namespace string, message string, stackTrace string, context map[string]interface{}) ErrCode {
e := &errorCode{
err: Error{ID: id, Namespace: namespace, Code: code, Message: message},
stackTrace: stackTrace,
context: context,
}
if e.context == nil {
e.context = make(map[string]interface{})
}
e.err.ID = id
e.err.Code = code
e.err.Message = message
e.err.Namespace = namespace
return e
}
func (p *errorCode) Id() string {
return p.err.ID
}
func (p *errorCode) Code() uint64 {
return p.err.Code
}
func (p *errorCode) Namespace() string {
return p.err.Namespace
}
func (p *errorCode) Error() string {
msg := p.err.Message
if len(p.errors) > 0 {
if msg != "" {
msg += "; "
}
msg += strings.Join(p.errors, "; ") + "."
}
return msg
}
func (p *errorCode) FullError() error {
errLines := make([]string, 1)
errLines[0] = fmt.Sprintf("Id: %s#%d:%s", p.Namespace(), p.Code(), p.Id())
errLines = append(errLines, "Error:")
errLines = append(errLines, p.Error())
errLines = append(errLines, "Context:")
errLines = append(errLines, p.Context().String())
errLines = append(errLines, "StackTrace:")
errLines = append(errLines, p.stackTrace)
return New(strings.Join(errLines, "\n"))
}
func Unmarshal(data []byte) ErrCode {
decoder := json.NewDecoder(bytes.NewBuffer(data))
decoder.UseNumber()
v := errorCode{}
if decoder.Decode(&v.err) != nil {
return nil
}
return &v
}
func (p *errorCode) Context() ErrorContext {
return p.context
}
func (p *errorCode) StackTrace() string {
return p.stackTrace
}
func (p *errorCode) Append(err ...interface{}) ErrCode {
if err != nil {
for _, e := range err {
switch ev := e.(type) {
case ErrCode:
{
str := fmt.Sprintf("(%s#%d:%s) %s", ev.Namespace(), ev.Code(), ev.Id(), ev.Error())
p.errors = append(p.errors, str)
}
case error:
{
p.errors = append(p.errors, ev.Error())
}
default:
p.errors = append(p.errors, fmt.Sprintf("%v", e))
}
}
}
return p
}
func (p *errorCode) WithContext(key string, value interface{}) ErrCode {
p.context[key] = value
return p
}
func (p *errorCode) Marshal() (data []byte, err error) {
e := Error{
ID: p.err.ID,
Code: p.err.Code,
Message: p.Error(),
Namespace: p.err.Namespace,
}
return json.Marshal(e)
}
func IsErrCode(err error) bool {
_, ok := err.(ErrCode)
return ok
}
func (p errorCode) MarshalJSON() ([]byte, error) {
str := "\"" + fmt.Sprintf("(%s#%d:%s) %s", p.err.Namespace, p.err.Code, p.err.ID, p.err.Message) + "\""
return []byte(str), nil
}
func (p errorCode) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
str := fmt.Sprintf("(%s#%d:%s) %s", p.err.Namespace, p.err.Code, p.err.ID, p.err.Message)
e.EncodeElement(str, start)
return nil
}