123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- package gott
- 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 fnName(fn interface{}) string {
- return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
- }
- func logErr(e error, fn interface{}) {
- log.Printf("Function %s returned error: %v\n", fnName(fn), e)
- }
- func logMsg(msg string, fn interface{}) {
- log.Printf("%s %s\n", msg, fnName(fn))
- }
- // Exception is a type encapsulating anything contained in panic.
- // It implements Error() and therefore can be used as error.
- type Exception struct {
- E interface{}
- }
- func (e Exception) Error() string {
- return fmt.Sprintf("function panicked with %v", e.E)
- }
- // R is a simplification of Either monad. It’s either succesful—when its error
- // is nil—or unsuccessful otherwise.
- type R[T any] struct {
- S T
- E error
- LogLevel LogLevel
- }
- // Bind performs f on the receiver’s success value and assigns the returned
- // value and error to the receiver if it’s is successful. In either case, Bind
- // returns the receiver.
- // Bind operates on functions that return value and error.
- func (r R[T]) Bind(f func(T) (T, error)) R[T] {
- if r.E == nil {
- if r.LogLevel >= Debug {
- logMsg("running:", f)
- }
- r.S, r.E = f(r.S)
- if r.E != nil {
- if r.LogLevel >= Error {
- logErr(r.E, f)
- }
- r.E = fmt.Errorf("while running %s: %w", fnName(f), r.E)
- }
- } else {
- if r.LogLevel >= Info {
- logMsg("skipping:", f)
- }
- }
- return r
- }
- // Map performs f on the receiver’s success value and assigns the returned
- // value to the receiver if it’s is successful. In either case, Map returns the
- // receiver.
- // Map operates on functions that are always successful and return only one
- // value.
- func (r R[T]) Map(f func(T) T) R[T] {
- if r.E == nil {
- if r.LogLevel >= Debug {
- logMsg("running:", f)
- }
- r.S = f(r.S)
- } else {
- if r.LogLevel >= Info {
- logMsg("skipping:", f)
- }
- }
- return r
- }
- // Tee performs f on the receiver’s success value and assigns the returned
- // error to the receiver if it’s is successful. In either case, Tee returns the
- // receiver.
- // Tee operates on functions that only perform side effects and might return an
- // error
- func (r R[T]) Tee(f func(T) error) R[T] {
- if r.E == nil {
- if r.LogLevel >= Debug {
- logMsg("running:", f)
- }
- r.E = f(r.S)
- if r.E != nil {
- if r.LogLevel >= Error {
- logErr(r.E, f)
- }
- r.E = fmt.Errorf("while running %s: %w", fnName(f), r.E)
- }
- } else {
- if r.LogLevel >= Info {
- logMsg("skipping:", f)
- }
- }
- return r
- }
- // SafeTee performs f on the receiver’s success value if the receiver is
- // successful. In either case, SafeTee returns the receiver.
- // SafeTee operates on functions that only perform side effects and are always
- // successful.
- func (r R[T]) SafeTee(f func(T)) R[T] {
- if r.E == nil {
- if r.LogLevel >= Debug {
- logMsg("running:", f)
- }
- f(r.S)
- } else {
- if r.LogLevel >= Info {
- logMsg("skipping:", f)
- }
- }
- return r
- }
- // Catch performs f on the receiver’s success value and assigns the returned
- // vale to the receiver if it’s successful. If f panics, Catch recovers and
- // stores the value passed to panic in receiver’s error as Exception. In either
- // case, Catch returns the receiver.
- func (r R[T]) Catch(f func(T) T) (r2 R[T]) {
- r2 = r
- if r2.E == nil {
- if r2.LogLevel >= Debug {
- logMsg("running:", f)
- }
- defer func() {
- if err := recover(); err != nil {
- if r2.LogLevel >= Error {
- logErr(Exception{err}, f)
- }
- r2.E = fmt.Errorf("while running %s: %w", fnName(f), Exception{err})
- }
- }()
- r2.S = f(r.S)
- } else {
- if r.LogLevel >= Info {
- logMsg("skipping:", f)
- }
- }
- return
- }
- // Revover tries to put processing back on the happy track.
- // If receiver is not successful, Recover calls the passed function and
- // assignes the returned value and error to the receiver. In either case,
- // Recover returns the receiver.
- func (r R[T]) Recover(f func(T, error) (T, error)) R[T] {
- if r.E != nil {
- if r.LogLevel >= Debug {
- logMsg("running:", f)
- }
- r.S, r.E = f(r.S, r.E)
- if r.E != nil {
- if r.LogLevel >= Error {
- logErr(r.E, f)
- }
- r.E = fmt.Errorf("while running %s: %w", fnName(f), r.E)
- }
- } else {
- if r.LogLevel >= Info {
- logMsg("skipping:", f)
- }
- }
- return r
- }
- // Handle performs onSuccess on the receiver’s success value if the receiver is
- // successful, or onError on the receiver’s error otherwise. In either case,
- // Handle returns the receiver.
- func (r R[T]) Handle(onSuccess func(T), onError func(error)) R[T] {
- if r.E == nil {
- if r.LogLevel >= Debug {
- logMsg("running:", onSuccess)
- }
- onSuccess(r.S)
- } else {
- if r.LogLevel >= Debug {
- logMsg("running:", onError)
- }
- onError(r.E)
- }
- return r
- }
|