1//  Copyright (c) 2014 Couchbase, Inc.
2//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3//  except in compliance with the License. You may obtain a copy of the License at
4//    http://www.apache.org/licenses/LICENSE-2.0
5//  Unless required by applicable law or agreed to in writing, software distributed under the
6//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7//  either express or implied. See the License for the specific language governing permissions
8//  and limitations under the License.
9
10/*
11
12Package err provides user-visible errors and warnings. These errors
13include error codes and will eventually provide multi-language
14messages.
15
16*/
17package errors
18
19import (
20	"encoding/json"
21	"fmt"
22	"path"
23	"runtime"
24	"strings"
25)
26
27const (
28	EXCEPTION = iota
29	WARNING
30	NOTICE
31	INFO
32	LOG
33	DEBUG
34)
35
36type Errors []Error
37
38// Error will eventually include code, message key, and internal error
39// object (cause) and message
40type Error interface {
41	error
42	Code() int32
43	TranslationKey() string
44	Cause() error
45	Level() int
46	IsFatal() bool
47	IsWarning() bool
48	OnceOnly() bool
49}
50
51type ErrorChannel chan Error
52
53func NewError(e error, internalMsg string) Error {
54	switch e := e.(type) {
55	case Error: // if given error is already an Error, just return it:
56		return e
57	default:
58		return &err{level: EXCEPTION, ICode: 5000, IKey: "Internal Error", ICause: e,
59			InternalMsg: internalMsg, InternalCaller: CallerN(1)}
60	}
61}
62
63func NewWarning(internalMsg string) Error {
64	return &err{level: WARNING, InternalMsg: internalMsg, InternalCaller: CallerN(1)}
65}
66
67func NewNotice(internalMsg string) Error {
68	return &err{level: NOTICE, InternalMsg: internalMsg, InternalCaller: CallerN(1)}
69}
70
71func NewInfo(internalMsg string) Error {
72	return &err{level: INFO, InternalMsg: internalMsg, InternalCaller: CallerN(1)}
73}
74
75func NewLog(internalMsg string) Error {
76	return &err{level: LOG, InternalMsg: internalMsg, InternalCaller: CallerN(1)}
77}
78
79func NewDebug(internalMsg string) Error {
80	return &err{level: DEBUG, InternalMsg: internalMsg, InternalCaller: CallerN(1)}
81}
82
83type err struct {
84	ICode          int32
85	IKey           string
86	ICause         error
87	InternalMsg    string
88	InternalCaller string
89	level          int
90	onceOnly       bool
91}
92
93func (e *err) Error() string {
94	switch {
95	default:
96		return "Unspecified error."
97	case e.InternalMsg != "" && e.ICause != nil:
98		return e.InternalMsg + " - cause: " + e.ICause.Error()
99	case e.InternalMsg != "":
100		return e.InternalMsg
101	case e.ICause != nil:
102		return e.ICause.Error()
103	}
104}
105
106func (e *err) MarshalJSON() ([]byte, error) {
107	m := map[string]interface{}{
108		"code":    e.ICode,
109		"key":     e.IKey,
110		"message": e.InternalMsg,
111	}
112	if e.ICause != nil {
113		m["cause"] = e.ICause.Error()
114	}
115	if e.InternalCaller != "" &&
116		!strings.HasPrefix("e.InternalCaller", "unknown:") {
117		m["caller"] = e.InternalCaller
118	}
119	return json.Marshal(m)
120}
121
122func (e *err) UnmarshalJSON(body []byte) error {
123	var _unmarshalled struct {
124		Caller  string `json:"caller"`
125		Code    int32  `json:"code"`
126		Key     string `json:"key"`
127		Message string `json:"message"`
128	}
129
130	unmarshalErr := json.Unmarshal(body, &_unmarshalled)
131	if unmarshalErr != nil {
132		return unmarshalErr
133	}
134
135	e.ICode = _unmarshalled.Code
136	e.IKey = _unmarshalled.Key
137	e.InternalMsg = _unmarshalled.Message
138	e.InternalCaller = _unmarshalled.Caller
139	return nil
140}
141
142func (e *err) Level() int {
143	return e.level
144}
145
146func (e *err) IsFatal() bool {
147	return e.level == EXCEPTION
148}
149
150func (e *err) IsWarning() bool {
151	return e.level == WARNING
152}
153
154func (e *err) Code() int32 {
155	return e.ICode
156}
157
158func (e *err) TranslationKey() string {
159	return e.IKey
160}
161
162func (e *err) Cause() error {
163	return e.ICause
164}
165
166func (e *err) OnceOnly() bool {
167	return e.onceOnly
168}
169
170// only put errors in the reserved range here (7000-9999)
171func NewNotImplemented(feature string) Error {
172	return &err{level: EXCEPTION, ICode: 9999, IKey: "not_implemented", InternalMsg: fmt.Sprintf("Not yet implemented: %v", feature), InternalCaller: CallerN(1)}
173}
174
175// Returns "FileName:LineNum" of caller.
176func Caller() string {
177	return CallerN(1)
178}
179
180// Returns "FileName:LineNum" of the Nth caller on the call stack,
181// where level of 0 is the caller of CallerN.
182func CallerN(level int) string {
183	_, fname, lineno, ok := runtime.Caller(1 + level)
184	if !ok {
185		return "unknown:0"
186	}
187	return fmt.Sprintf("%s:%d",
188		strings.Split(path.Base(fname), ".")[0], lineno)
189}
190