gott.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package gott
  2. import (
  3. "fmt"
  4. "log"
  5. "reflect"
  6. "runtime"
  7. )
  8. type LogLevel int
  9. // LogLevel specifies what to log:
  10. // Quiet logs nothing,
  11. // Error logs only errors,
  12. // Debug logs functions that run,
  13. // Info logs run and skipped functions
  14. const (
  15. Quiet LogLevel = iota
  16. Error
  17. Debug
  18. Info
  19. )
  20. func fnName(fn interface{}) string {
  21. return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
  22. }
  23. func logErr(e error, fn interface{}) {
  24. log.Printf("Function %s returned error: %v\n", fnName(fn), e)
  25. }
  26. func logMsg(msg string, fn interface{}) {
  27. log.Printf("%s %s\n", msg, fnName(fn))
  28. }
  29. // Exception is a type encapsulating anything contained in panic.
  30. // It implements Error() and therefore can be used as error.
  31. type Exception struct {
  32. E interface{}
  33. }
  34. func (e Exception) Error() string {
  35. return fmt.Sprintf("function panicked with %v", e.E)
  36. }
  37. // R is a simplification of Either monad. It’s either succesful—when its error
  38. // is nil—or unsuccessful otherwise.
  39. type R[T any] struct {
  40. S T
  41. E error
  42. LogLevel LogLevel
  43. }
  44. // Bind performs f on the receiver’s success value and assigns the returned
  45. // value and error to the receiver if it’s is successful. In either case, Bind
  46. // returns the receiver.
  47. // Bind operates on functions that return value and error.
  48. func (r R[T]) Bind(f func(T) (T, error)) R[T] {
  49. if r.E == nil {
  50. if r.LogLevel >= Debug {
  51. logMsg("running:", f)
  52. }
  53. r.S, r.E = f(r.S)
  54. if r.E != nil {
  55. if r.LogLevel >= Error {
  56. logErr(r.E, f)
  57. }
  58. r.E = fmt.Errorf("while running %s: %w", fnName(f), r.E)
  59. }
  60. } else {
  61. if r.LogLevel >= Info {
  62. logMsg("skipping:", f)
  63. }
  64. }
  65. return r
  66. }
  67. // Map performs f on the receiver’s success value and assigns the returned
  68. // value to the receiver if it’s is successful. In either case, Map returns the
  69. // receiver.
  70. // Map operates on functions that are always successful and return only one
  71. // value.
  72. func (r R[T]) Map(f func(T) T) R[T] {
  73. if r.E == nil {
  74. if r.LogLevel >= Debug {
  75. logMsg("running:", f)
  76. }
  77. r.S = f(r.S)
  78. } else {
  79. if r.LogLevel >= Info {
  80. logMsg("skipping:", f)
  81. }
  82. }
  83. return r
  84. }
  85. // Tee performs f on the receiver’s success value and assigns the returned
  86. // error to the receiver if it’s is successful. In either case, Tee returns the
  87. // receiver.
  88. // Tee operates on functions that only perform side effects and might return an
  89. // error
  90. func (r R[T]) Tee(f func(T) error) R[T] {
  91. if r.E == nil {
  92. if r.LogLevel >= Debug {
  93. logMsg("running:", f)
  94. }
  95. r.E = f(r.S)
  96. if r.E != nil {
  97. if r.LogLevel >= Error {
  98. logErr(r.E, f)
  99. }
  100. r.E = fmt.Errorf("while running %s: %w", fnName(f), r.E)
  101. }
  102. } else {
  103. if r.LogLevel >= Info {
  104. logMsg("skipping:", f)
  105. }
  106. }
  107. return r
  108. }
  109. // SafeTee performs f on the receiver’s success value if the receiver is
  110. // successful. In either case, SafeTee returns the receiver.
  111. // SafeTee operates on functions that only perform side effects and are always
  112. // successful.
  113. func (r R[T]) SafeTee(f func(T)) R[T] {
  114. if r.E == nil {
  115. if r.LogLevel >= Debug {
  116. logMsg("running:", f)
  117. }
  118. f(r.S)
  119. } else {
  120. if r.LogLevel >= Info {
  121. logMsg("skipping:", f)
  122. }
  123. }
  124. return r
  125. }
  126. // Catch performs f on the receiver’s success value and assigns the returned
  127. // vale to the receiver if it’s successful. If f panics, Catch recovers and
  128. // stores the value passed to panic in receiver’s error as Exception. In either
  129. // case, Catch returns the receiver.
  130. func (r R[T]) Catch(f func(T) T) (r2 R[T]) {
  131. r2 = r
  132. if r2.E == nil {
  133. if r2.LogLevel >= Debug {
  134. logMsg("running:", f)
  135. }
  136. defer func() {
  137. if err := recover(); err != nil {
  138. if r2.LogLevel >= Error {
  139. logErr(Exception{err}, f)
  140. }
  141. r2.E = fmt.Errorf("while running %s: %w", fnName(f), Exception{err})
  142. }
  143. }()
  144. r2.S = f(r.S)
  145. } else {
  146. if r.LogLevel >= Info {
  147. logMsg("skipping:", f)
  148. }
  149. }
  150. return
  151. }
  152. // Revover tries to put processing back on the happy track.
  153. // If receiver is not successful, Recover calls the passed function and
  154. // assignes the returned value and error to the receiver. In either case,
  155. // Recover returns the receiver.
  156. func (r R[T]) Recover(f func(T, error) (T, error)) R[T] {
  157. if r.E != nil {
  158. if r.LogLevel >= Debug {
  159. logMsg("running:", f)
  160. }
  161. r.S, r.E = f(r.S, r.E)
  162. if r.E != nil {
  163. if r.LogLevel >= Error {
  164. logErr(r.E, f)
  165. }
  166. r.E = fmt.Errorf("while running %s: %w", fnName(f), r.E)
  167. }
  168. } else {
  169. if r.LogLevel >= Info {
  170. logMsg("skipping:", f)
  171. }
  172. }
  173. return r
  174. }
  175. // Handle performs onSuccess on the receiver’s success value if the receiver is
  176. // successful, or onError on the receiver’s error otherwise. In either case,
  177. // Handle returns the receiver.
  178. func (r R[T]) Handle(onSuccess func(T), onError func(error)) R[T] {
  179. if r.E == nil {
  180. if r.LogLevel >= Debug {
  181. logMsg("running:", onSuccess)
  182. }
  183. onSuccess(r.S)
  184. } else {
  185. if r.LogLevel >= Debug {
  186. logMsg("running:", onError)
  187. }
  188. onError(r.E)
  189. }
  190. return r
  191. }