|
@@ -1,11 +1,40 @@
|
|
|
package gott
|
|
|
|
|
|
-import "fmt"
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "reflect"
|
|
|
+ "runtime"
|
|
|
+)
|
|
|
+
|
|
|
+type LogLevel int
|
|
|
+
|
|
|
+// LogLevel specifies what to log:
|
|
|
+// Quiet logs nothing,
|
|
|
+// Error logs only errors,
|
|
|
+// Debug logs functions that run,
|
|
|
+// Info logs run and skipped functions
|
|
|
+const (
|
|
|
+ Quiet LogLevel = iota
|
|
|
+ Error
|
|
|
+ Debug
|
|
|
+ Info
|
|
|
+)
|
|
|
+
|
|
|
+func logErr(e error, fn interface{}) {
|
|
|
+ fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
|
|
+ log.Printf("Function %s returned error: %v", fnName, e)
|
|
|
+}
|
|
|
+
|
|
|
+func logMsg(msg string, fn interface{}) {
|
|
|
+ fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
|
|
+ log.Printf("%s %s", msg, fnName)
|
|
|
+}
|
|
|
|
|
|
// Exception is a type encapsulating anything contained in panic.
|
|
|
// It implements Error() and therefore can be used as error.
|
|
|
type Exception struct {
|
|
|
- e interface{}
|
|
|
+ E interface{}
|
|
|
}
|
|
|
|
|
|
func (e Exception) Error() string {
|
|
@@ -18,13 +47,14 @@ type Tuple []interface{}
|
|
|
// Result is a simplification of Either monad. It’s either successful—and
|
|
|
// carries an interface{}—or unsuccessful—and carries an error.
|
|
|
type Result struct {
|
|
|
- s interface{}
|
|
|
- e error
|
|
|
+ s interface{}
|
|
|
+ e error
|
|
|
+ LogLevel LogLevel
|
|
|
}
|
|
|
|
|
|
// NewResult creates initial Result passed to functions.
|
|
|
func NewResult(s interface{}) *Result {
|
|
|
- return &Result{s, nil}
|
|
|
+ return &Result{s, nil, Quiet}
|
|
|
}
|
|
|
|
|
|
// Bind performs fn on the receiver’s success value and assigns the returned
|
|
@@ -33,11 +63,21 @@ func NewResult(s interface{}) *Result {
|
|
|
// Bind operates on functions that return value and error.
|
|
|
func (r *Result) Bind(fn func(...interface{}) (interface{}, error)) *Result {
|
|
|
if r.e == nil {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", fn)
|
|
|
+ }
|
|
|
if s, ok := r.s.(Tuple); ok {
|
|
|
r.s, r.e = fn(s...)
|
|
|
} else {
|
|
|
r.s, r.e = fn(r.s)
|
|
|
}
|
|
|
+ if r.e != nil && r.LogLevel >= Error {
|
|
|
+ logErr(r.e, fn)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if r.LogLevel >= Info {
|
|
|
+ logMsg("skipping:", fn)
|
|
|
+ }
|
|
|
}
|
|
|
return r
|
|
|
}
|
|
@@ -49,11 +89,18 @@ func (r *Result) Bind(fn func(...interface{}) (interface{}, error)) *Result {
|
|
|
// value
|
|
|
func (r *Result) Map(fn func(...interface{}) interface{}) *Result {
|
|
|
if r.e == nil {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", fn)
|
|
|
+ }
|
|
|
if s, ok := r.s.(Tuple); ok {
|
|
|
r.s = fn(s...)
|
|
|
} else {
|
|
|
r.s = fn(r.s)
|
|
|
}
|
|
|
+ } else {
|
|
|
+ if r.LogLevel >= Info {
|
|
|
+ logMsg("skipping:", fn)
|
|
|
+ }
|
|
|
}
|
|
|
return r
|
|
|
}
|
|
@@ -65,12 +112,21 @@ func (r *Result) Map(fn func(...interface{}) interface{}) *Result {
|
|
|
// error
|
|
|
func (r *Result) Tee(fn func(...interface{}) error) *Result {
|
|
|
if r.e == nil {
|
|
|
- r.s = nil
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", fn)
|
|
|
+ }
|
|
|
if s, ok := r.s.(Tuple); ok {
|
|
|
r.e = fn(s...)
|
|
|
} else {
|
|
|
r.e = fn(r.s)
|
|
|
}
|
|
|
+ if r.e != nil && r.LogLevel >= Error {
|
|
|
+ logErr(r.e, fn)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if r.LogLevel >= Info {
|
|
|
+ logMsg("skipping:", fn)
|
|
|
+ }
|
|
|
}
|
|
|
return r
|
|
|
}
|
|
@@ -81,11 +137,18 @@ func (r *Result) Tee(fn func(...interface{}) error) *Result {
|
|
|
// successful.
|
|
|
func (r *Result) SafeTee(fn func(...interface{})) *Result {
|
|
|
if r.e == nil {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", fn)
|
|
|
+ }
|
|
|
if s, ok := r.s.(Tuple); ok {
|
|
|
fn(s...)
|
|
|
} else {
|
|
|
fn(r.s)
|
|
|
}
|
|
|
+ } else {
|
|
|
+ if r.LogLevel >= Info {
|
|
|
+ logMsg("skipping:", fn)
|
|
|
+ }
|
|
|
}
|
|
|
return r
|
|
|
}
|
|
@@ -96,10 +159,16 @@ func (r *Result) SafeTee(fn func(...interface{})) *Result {
|
|
|
// case, Catch returns the receiver.
|
|
|
func (r *Result) Catch(fn func(...interface{}) interface{}) (result *Result) {
|
|
|
if r.e == nil {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", fn)
|
|
|
+ }
|
|
|
defer func() {
|
|
|
if err := recover(); err != nil {
|
|
|
r.e = Exception{err}
|
|
|
result = r
|
|
|
+ if r.e != nil && r.LogLevel >= Error {
|
|
|
+ logErr(r.e, fn)
|
|
|
+ }
|
|
|
}
|
|
|
}()
|
|
|
if s, ok := r.s.(Tuple); ok {
|
|
@@ -107,6 +176,35 @@ func (r *Result) Catch(fn func(...interface{}) interface{}) (result *Result) {
|
|
|
} else {
|
|
|
r.s = fn(r.s)
|
|
|
}
|
|
|
+ } else {
|
|
|
+ if r.LogLevel >= Info {
|
|
|
+ logMsg("skipping:", fn)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return r
|
|
|
+}
|
|
|
+
|
|
|
+// Revover tries to put processing back on the happy track.
|
|
|
+// If receiver is not successful, Recover calls the passed function and
|
|
|
+// assignes the returned values to the receiver. In either case, Recover
|
|
|
+// returns the receiver.
|
|
|
+func (r *Result) Recover(fn func(...interface{}) (interface{}, error)) *Result {
|
|
|
+ if r.e != nil {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", fn)
|
|
|
+ }
|
|
|
+ if s, ok := r.s.(Tuple); ok {
|
|
|
+ r.s, r.e = fn(s...)
|
|
|
+ } else {
|
|
|
+ r.s, r.e = fn(r.s)
|
|
|
+ }
|
|
|
+ if r.e != nil && r.LogLevel >= Error {
|
|
|
+ logErr(r.e, fn)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if r.LogLevel >= Info {
|
|
|
+ logMsg("skipping:", fn)
|
|
|
+ }
|
|
|
}
|
|
|
return r
|
|
|
}
|
|
@@ -116,12 +214,18 @@ func (r *Result) Catch(fn func(...interface{}) interface{}) (result *Result) {
|
|
|
// Handle returns the receiver.
|
|
|
func (r *Result) Handle(onSuccess func(...interface{}), onError func(error)) *Result {
|
|
|
if r.e == nil {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", onSuccess)
|
|
|
+ }
|
|
|
if s, ok := r.s.(Tuple); ok {
|
|
|
onSuccess(s...)
|
|
|
} else {
|
|
|
onSuccess(r.s)
|
|
|
}
|
|
|
} else {
|
|
|
+ if r.LogLevel >= Debug {
|
|
|
+ logMsg("running:", onError)
|
|
|
+ }
|
|
|
onError(r.e)
|
|
|
}
|
|
|
return r
|