|
| 1 | +package com.avsystem.commons |
| 2 | +package concurrent |
| 3 | + |
| 4 | +import com.avsystem.commons.concurrent.TaskExtensions.{TaskCompanionOps, TaskOps} |
| 5 | +import com.avsystem.commons.misc.Timestamp |
| 6 | +import monix.eval.Task |
| 7 | +import monix.reactive.Observable |
| 8 | + |
| 9 | +import java.util.concurrent.TimeUnit |
| 10 | +import scala.concurrent.TimeoutException |
| 11 | +import scala.concurrent.duration.FiniteDuration |
| 12 | + |
| 13 | +trait TaskExtensions { |
| 14 | + implicit def taskOps[T](task: Task[T]): TaskOps[T] = new TaskOps(task) |
| 15 | + |
| 16 | + implicit def taskCompanionOps(task: Task.type): TaskCompanionOps.type = TaskCompanionOps |
| 17 | +} |
| 18 | + |
| 19 | +object TaskExtensions extends TaskExtensions { |
| 20 | + final class TaskOps[T](private val task: Task[T]) extends AnyVal { |
| 21 | + /** |
| 22 | + * Similar to [[Task.timeoutWith]] but exception instance is created lazily (for performance) |
| 23 | + */ |
| 24 | + def lazyTimeout(after: FiniteDuration, msg: => String): Task[T] = |
| 25 | + task.timeoutTo(after, Task.defer(Task.raiseError(new TimeoutException(msg)))) |
| 26 | + |
| 27 | + /** |
| 28 | + * Similar to [[Task.tapEval]], accepts simple consumer function as an argument |
| 29 | + */ |
| 30 | + def tapL(f: T => Unit): Task[T] = |
| 31 | + task.map(_.setup(f)) |
| 32 | + |
| 33 | + /** |
| 34 | + * Similar to [[Task.tapError]], accepts [[PartialFunction]] as an argument |
| 35 | + */ |
| 36 | + def tapErrorL[B](f: PartialFunction[Throwable, B]): Task[T] = |
| 37 | + task.tapError(t => Task(f.applyOpt(t))) |
| 38 | + } |
| 39 | + |
| 40 | + object TaskCompanionOps { |
| 41 | + import com.avsystem.commons.concurrent.ObservableExtensions.observableOps |
| 42 | + |
| 43 | + /** A [[Task]] of [[Opt.Empty]] */ |
| 44 | + def optEmpty[A]: Task[Opt[A]] = Task.pure(Opt.Empty) |
| 45 | + |
| 46 | + def traverseOpt[A, B](opt: Opt[A])(f: A => Task[B]): Task[Opt[B]] = |
| 47 | + opt.fold(Task.optEmpty[B])(a => f(a).map(_.opt)) |
| 48 | + |
| 49 | + def fromOpt[A](maybeTask: Opt[Task[A]]): Task[Opt[A]] = maybeTask match { |
| 50 | + case Opt(task) => task.map(_.opt) |
| 51 | + case Opt.Empty => Task.optEmpty |
| 52 | + } |
| 53 | + |
| 54 | + def traverseMap[K, V, A, B](map: Map[K, V])(f: (K, V) => Task[(A, B)]): Task[Map[A, B]] = |
| 55 | + Observable.fromIterable(map).mapEval({ case (key, value) => f(key, value) }).toL(Map) |
| 56 | + |
| 57 | + def traverseMapValues[K, A, B](map: Map[K, A])(f: (K, A) => Task[B]): Task[Map[K, B]] = |
| 58 | + traverseMap(map)({ case (key, value) => f(key, value).map(key -> _) }) |
| 59 | + |
| 60 | + def currentTimestamp: Task[Timestamp] = |
| 61 | + Task.clock.realTime(TimeUnit.MILLISECONDS).map(Timestamp(_)) |
| 62 | + |
| 63 | + def usingNow[T](useNow: Timestamp => Task[T]): Task[T] = |
| 64 | + currentTimestamp.flatMap(useNow) |
| 65 | + } |
| 66 | +} |
0 commit comments