qvm/vendor/github.com/samber/mo/future.go
Joshua Bell f501abe660 vendor
2026-01-26 21:41:26 -06:00

197 lines
3.5 KiB
Go

package mo
import (
"sync"
)
// NewFuture instanciate a new future.
func NewFuture[T any](cb func(resolve func(T), reject func(error))) *Future[T] {
future := Future[T]{
cb: cb,
cancelCb: func() {},
done: make(chan struct{}),
}
future.active()
return &future
}
// Future represents a value which may or may not currently be available, but will be
// available at some point, or an exception if that value could not be made available.
type Future[T any] struct {
mu sync.Mutex
cb func(func(T), func(error))
cancelCb func()
next *Future[T]
done chan struct{}
doneOnce sync.Once
result Result[T]
}
func (f *Future[T]) active() {
go f.cb(f.resolve, f.reject)
}
func (f *Future[T]) activeSync() {
f.cb(f.resolve, f.reject)
}
func (f *Future[T]) resolve(value T) {
f.doneOnce.Do(func() {
f.mu.Lock()
defer f.mu.Unlock()
f.result = Ok(value)
if f.next != nil {
f.next.activeSync()
}
close(f.done)
})
}
func (f *Future[T]) reject(err error) {
f.doneOnce.Do(func() {
f.mu.Lock()
defer f.mu.Unlock()
f.result = Err[T](err)
if f.next != nil {
f.next.activeSync()
}
close(f.done)
})
}
// Then is called when Future is resolved. It returns a new Future.
func (f *Future[T]) Then(cb func(T) (T, error)) *Future[T] {
f.mu.Lock()
next := &Future[T]{
cb: func(resolve func(T), reject func(error)) {
if f.result.IsError() {
reject(f.result.Error())
return
}
newValue, err := cb(f.result.MustGet())
if err != nil {
reject(err)
return
}
resolve(newValue)
},
cancelCb: func() {
f.Cancel()
},
done: make(chan struct{}),
}
f.next = next
select {
case <-f.done:
f.mu.Unlock()
next.active()
default:
f.mu.Unlock()
}
return next
}
// Catch is called when Future is rejected. It returns a new Future.
func (f *Future[T]) Catch(cb func(error) (T, error)) *Future[T] {
f.mu.Lock()
next := &Future[T]{
cb: func(resolve func(T), reject func(error)) {
if f.result.IsOk() {
resolve(f.result.MustGet())
return
}
newValue, err := cb(f.result.Error())
if err != nil {
reject(err)
return
}
resolve(newValue)
},
cancelCb: func() {
f.Cancel()
},
done: make(chan struct{}),
}
f.next = next
select {
case <-f.done:
f.mu.Unlock()
next.active()
default:
f.mu.Unlock()
}
return next
}
// Finally is called when Future is processed either resolved or rejected. It returns a new Future.
func (f *Future[T]) Finally(cb func(T, error) (T, error)) *Future[T] {
f.mu.Lock()
next := &Future[T]{
cb: func(resolve func(T), reject func(error)) {
newValue, err := cb(f.result.Get())
if err != nil {
reject(err)
return
}
resolve(newValue)
},
cancelCb: func() {
f.Cancel()
},
done: make(chan struct{}),
}
f.next = next
select {
case <-f.done:
f.mu.Unlock()
next.active()
default:
f.mu.Unlock()
}
return next
}
// Cancel cancels the Future chain.
func (f *Future[T]) Cancel() {
f.mu.Lock()
defer f.mu.Unlock()
f.next = nil
if f.cancelCb != nil {
f.cancelCb()
}
}
// Collect awaits and return result of the Future.
func (f *Future[T]) Collect() (T, error) {
<-f.done
return f.result.Get()
}
// Result wraps Collect and returns a Result.
func (f *Future[T]) Result() Result[T] {
return TupleToResult(f.Collect())
}
// Either wraps Collect and returns a Either.
func (f *Future[T]) Either() Either[error, T] {
v, err := f.Collect()
if err != nil {
return Left[error, T](err)
}
return Right[error](v)
}