Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ lazy val effectsJS = effects.js
lazy val async = module("async", subFolder = Some("async"))
.dependsOn(core)
.jsSettings(sharedJsSettings: _*)
.crossDepSettings(commonDeps: _*)
.crossDepSettings(commonDeps ++ Seq(
%("cats-effect") % Test
): _*)

lazy val asyncJVM = async.jvm
lazy val asyncJS = async.js
Expand Down
29 changes: 28 additions & 1 deletion modules/async/async/shared/src/main/scala/async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package freestyle

import scala.concurrent._
import scala.util._

object async {

Expand All @@ -33,6 +34,19 @@ object async {
def async[A](fa: Proc[A]): FS[A]
}

class Future2AsyncM[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext)
extends FSHandler[Future, F] {
override def apply[A](future: Future[A]): F[A] =
AC.runAsync { cb =>
E.execute(new Runnable {
def run(): Unit = future.onComplete {
case Failure(e) => cb(Left(e))
case Success(r) => cb(Right(r))
}
})
}
}

trait Implicits {
implicit def futureAsyncContext(
implicit ec: ExecutionContext
Expand All @@ -57,5 +71,18 @@ object async {
}
}

object implicits extends Implicits
trait Syntax {

implicit def futureOps[A](f: Future[A]): FutureOps[A] = new FutureOps(f)

final class FutureOps[A](f: Future[A]) {

def to[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext): F[A] =
new Future2AsyncM[F].apply(f)

}

}

object implicits extends Implicits with Syntax
}
55 changes: 55 additions & 0 deletions modules/async/async/shared/src/test/scala/Future2AsyncMTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2017 47 Degrees, LLC. <http://www.47deg.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package freestyle

import freestyle.async.implicits._
import freestyle.async.{AsyncContext, Future2AsyncM, Proc}
import org.scalatest._
import cats.effect.{Effect, IO}

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

class Future2AsyncMTests extends WordSpec with Matchers {

implicit def catsEffectAsyncContext[F[_]](implicit F: Effect[F]): AsyncContext[F] =
new AsyncContext[F] {
def runAsync[A](fa: Proc[A]): F[A] = F.async(fa)
}

val exception: Throwable = new RuntimeException("Future2AsyncMTest exception")

def failedFuture[T]: Future[T] = Future.failed(exception)

def successfulFuture[T](value: T): Future[T] = Future.successful(value)

val F2F: Future2AsyncM[IO] = new Future2AsyncM[IO]

val foo = "bar"

"Future2Async Handler" should {

"transform scala.concurrent.Future to cats.effect.IO successfully" in {
F2F.apply(successfulFuture(foo)).map(_ shouldBe foo)
}

"recover from failed Futures and throw them accordingly" in {
F2F.apply(failedFuture[String]).attempt.map(_ shouldBe Left(exception))
}

}
}
2 changes: 1 addition & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version in ThisBuild := "0.4.4-SNAPSHOT"
version in ThisBuild := "0.4.4"