Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macros to create Trys #187

Merged
merged 1 commit into from
Dec 12, 2014
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,26 @@ package com.twitter.bijection.macros
import scala.language.experimental.macros

import com.twitter.bijection._
import com.twitter.bijection.macros.impl.{ CaseClassToTuple, CaseClassToMap }
import com.twitter.bijection.macros.impl.{ CaseClassToTuple, CaseClassToMap, TryMacros }

import scala.util.Try

object Macros {
def caseClassToTuple[T: IsCaseClass, Tup](recursivelyApply: Boolean): Bijection[T, Tup] = macro CaseClassToTuple.caseClassToTupleImplWithOption[T, Tup]
def caseClassToMap[T: IsCaseClass](recursivelyApply: Boolean): Injection[T, Map[String, Any]] = macro CaseClassToMap.caseClassToMapImplWithOption[T]

/**
* This can be used like Inversion.attempt. For example:
* fastAttempt(intString)(intString.toInt)
* The reason we don't take a B => A like attempt does is
* that would still require calling apply on that function.
* In contrast, here, we can inline it directly.
*/
def fastAttempt[A, B](b: B)(inv: A): Try[A] = macro TryMacros.fastAttempt[A, B]

/**
* This macro expands out to a try block so it is only slower
* than a try block in that it allocates Success/Failure wrappers
*/
def fastTry[T](toEval: T): Try[T] = macro TryMacros.fastTry[T]
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ private[bijection] object CaseClassToMap {
}
override def invert(m: _root_.scala.collection.immutable.Map[String, Any]): _root_.scala.util.Try[ $T ] = {
..$converters
try { _root_.scala.util.Success($companion(..$getters)) } catch { case _root_.scala.util.control.NonFatal(e) => _root_.scala.util.Failure(e) }
try { _root_.scala.util.Success($companion(..$getters)) }
catch { case _root_.scala.util.control.NonFatal(e) =>
_root_.scala.util.Failure(new _root_.com.twitter.bijection.InversionFailure(m, e)) }
}
}
""")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.twitter.bijection.macros.impl

import scala.language.experimental.macros
import scala.reflect.macros.Context
import scala.reflect.runtime.universe._
import scala.util.Try

import com.twitter.bijection._

private[bijection] object TryMacros {
def fastAttempt[A, B](c: Context)(b: c.Expr[B])(inv: c.Expr[A])(implicit A: c.WeakTypeTag[A]): c.Expr[Try[A]] = {
import c.universe._
c.Expr[scala.util.Try[A]](
q"""(try { _root_.scala.util.Success($inv) }
catch { case _root_.scala.util.control.NonFatal(e) =>
_root_.scala.util.Failure(new _root_.com.twitter.bijection.InversionFailure($b, e)) }): _root_.scala.util.Try[$A]""")
}

def fastTry[T](c: Context)(toEval: c.Expr[T])(implicit T: c.WeakTypeTag[T]): c.Expr[Try[T]] = {
import c.universe._
c.Expr[scala.util.Try[T]](
q"""(try { _root_.scala.util.Success($toEval) }
catch { case _root_.scala.util.control.NonFatal(e) => _root_.scala.util.Failure(e) }): _root_.scala.util.Try[$T]""")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.twitter.bijection._
import com.twitter.bijection.macros._
import com.twitter.chill.Externalizer

import scala.util.{ Failure, Success }

class MacroUnitTests extends WordSpec with Matchers with MacroTestHelper {
import MacroImplicits._
import MacroCaseClasses._
Expand All @@ -19,6 +21,50 @@ class MacroUnitTests extends WordSpec with Matchers with MacroTestHelper {
canExternalize(bij)
}

def alwaysFail(): Unit = {
true should be (false)
}

"Fast Try" when {
"creating Try" should {
"give a Failure with expection" in {
Macros.fastTry(sys.error("oh no")) match {
case Failure(e) => e.getMessage.contains("oh no") should be (true)
case _ => alwaysFail()
}
Macros.fastTry {
val l = List(1, 2, 3)
val l2 = l.filter(_ < 0)
l2.head
}.isFailure should be (true)
}
"not fail when there is no exception" in {
Macros.fastTry(1) should be (Success(1))
Macros.fastTry(List(1, 2)) should be (Success(List(1, 2)))
Macros.fastTry {
val l = List(1, 2, 3)
val l2 = l.filter(_ > 0)
l2.last
} should be (Success(3))
}
}
"attempt Inversion" should {
"give a InversionFailure on error" in {
Macros.fastAttempt("12m")("12m".toInt) match {
case Failure(InversionFailure(d, e)) => d should be ("12m")
case _ => alwaysFail()
}
Macros.fastAttempt("12m")(sys.error("This is lazy")) match {
case Failure(InversionFailure(d, e)) => d should be ("12m")
case _ => alwaysFail()
}
}
"give Success when correct" in {
Macros.fastAttempt("12")("12".toInt) should be (Success(12))
}
}
}

"Recursively applied" when {

"MacroGenerated Bijection to tuple" should {
Expand Down