Skip to content

Commit

Permalink
feat(88): Added tracing capabilities using a strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
rcardin committed Dec 30, 2024
1 parent 48bc7d9 commit 7a5dfbb
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 7 deletions.
14 changes: 13 additions & 1 deletion core/src/main/scala/in/rcard/raise4s/Raise.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package in.rcard.raise4s

import in.rcard.raise4s
import in.rcard.raise4s.Strategies.RecoverWith
import in.rcard.raise4s.Strategies.{RecoverWith, TraceWith, Traced, TracedRaise}

import scala.annotation.targetName
import scala.util.Try
Expand Down Expand Up @@ -1768,4 +1768,16 @@ object Raise {
*/
inline def withDefault[Error, A](default: A)(inline block: Raise[Error] ?=> A): A =
Raise.recover(block)(error => default)

inline def traced[Error, A](
inline block: Raise[Error] ?=> A
)(using inline tracing: TraceWith[Error]): Raise[Error] ?=> A = {
try {
given tracedRaise: Raise[Error] = new TracedRaise
block
} catch
case traced: Traced[Error] =>
tracing.trace(traced)
Raise.raise(traced.original)
}
}
20 changes: 16 additions & 4 deletions core/src/main/scala/in/rcard/raise4s/Strategies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,24 @@ object Strategies {
* actual should be(43)
* }}}
*
* @tparam Error The type of the error to recover from
* @tparam A The type of the value to return if the recovery is successful
*
* @see [[Raise.recoverable]]
* @tparam Error
* The type of the error to recover from
* @tparam A
* The type of the value to return if the recovery is successful
*
* @see
* [[Raise.recoverable]]
*/
trait RecoverWith[Error, A] {
def recover(error: Error): A
}

private[raise4s] class TracedRaise extends Raise[Any]:
def raise(e: Any): Nothing = throw Traced(e)

case class Traced[Error](original: Error) extends Exception

trait TraceWith[Error] {
def trace(traced: Traced[Error]): Unit
}
}
38 changes: 36 additions & 2 deletions core/src/test/scala/in/rcard/raise4s/StrategiesSpec.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package in.rcard.raise4s

import in.rcard.raise4s.Raise.raise
import in.rcard.raise4s.Strategies.{MapError, anyRaised}
import in.rcard.raise4s.Raise.{raise, traced}
import in.rcard.raise4s.Strategies.{MapError, TraceWith, anyRaised}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

Expand Down Expand Up @@ -39,4 +39,38 @@ class StrategiesSpec extends AnyFlatSpec with Matchers {
val result: Int | String = Raise.run(finalLambda)
result shouldBe "Hello"
}

"TraceWith" should "allow defining a strategy that trace the error and return it" in {
val queue = collection.mutable.ListBuffer.empty[String]
given TraceWith[String] = trace => {
queue += trace.original
trace.printStackTrace()
}

val lambda: Int raises String = traced {
raise("Oops!")
}

val actual: String | Int = Raise.run(lambda)

actual shouldBe "Oops!"
queue should contain("Oops!")
}

it should "return the happy path value if no error is raised" in {
val queue = collection.mutable.ListBuffer.empty[String]
given TraceWith[String] = trace => {
queue += trace.original
trace.printStackTrace()
}

val lambda: Int raises String = traced {
42
}

val actual: Int | String = Raise.run(lambda)

actual shouldBe 42
queue shouldBe empty
}
}

0 comments on commit 7a5dfbb

Please sign in to comment.