Skip to content

Commit

Permalink
Merge pull request #7 from ChristopherDavenport/explicitErrors
Browse files Browse the repository at this point in the history
Use Explicit General Error Type
  • Loading branch information
ChristopherDavenport authored Aug 12, 2020
2 parents d7093d9 + 955d0c9 commit 12ba4e4
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object RedisConnection{
def getTillEqualSize(acc: List[List[Resp]], lastArr: Array[Byte]): F[List[Resp]] =
socket.read(maxBytes, timeout).flatMap{
case None =>
ApplicativeError[F, Throwable].raiseError[List[Resp]](new Throwable("Rediculous: Terminated Before reaching Equal size"))
ApplicativeError[F, Throwable].raiseError[List[Resp]](RedisError.Generic("Rediculous: Terminated Before reaching Equal size"))
case Some(bytes) =>
Resp.parseAll(lastArr.toArray ++ bytes.toArray.toIterable) match {
case e@Resp.ParseError(_, _) => ApplicativeError[F, Throwable].raiseError[List[Resp]](e)
Expand All @@ -60,7 +60,7 @@ object RedisConnection{
key: Option[String]
): F[F[NonEmptyList[Resp]]] = {
val chunk = Chunk.seq(inputs.toList.map(Resp.renderRequest))
def withSocket(socket: Socket[F]): F[NonEmptyList[Resp]] = explicitPipelineRequest[F](socket, chunk).flatMap(l => Sync[F].delay(l.toNel.getOrElse(throw new Throwable("Rediculous: Impossible Return List was Empty but we guarantee output matches input"))))
def withSocket(socket: Socket[F]): F[NonEmptyList[Resp]] = explicitPipelineRequest[F](socket, chunk).flatMap(l => Sync[F].delay(l.toNel.getOrElse(throw RedisError.Generic("Rediculous: Impossible Return List was Empty but we guarantee output matches input"))))
connection match {
case PooledConnection(pool) => pool.map(_._1).take(()).use{
m => withSocket(m.value).attempt.flatTap{
Expand All @@ -71,12 +71,12 @@ object RedisConnection{
case DirectConnection(socket) => withSocket(socket).map(_.pure[F])
case Queued(queue, _) => chunk.traverse(resp => Deferred[F, Either[Throwable, Resp]].map((_, resp))).flatMap{ c =>
queue.enqueue1(c).as {
c.traverse(_._1.get).flatMap(_.sequence.traverse(l => Sync[F].delay(l.toNel.getOrElse(throw new Throwable("Rediculous: Impossible Return List was Empty but we guarantee output matches input"))))).rethrow
c.traverse(_._1.get).flatMap(_.sequence.traverse(l => Sync[F].delay(l.toNel.getOrElse(throw RedisError.Generic("Rediculous: Impossible Return List was Empty but we guarantee output matches input"))))).rethrow
}
}
case Cluster(queue) => chunk.traverse(resp => Deferred[F, Either[Throwable, Resp]].map((_, key, None, 0, resp))).flatMap{ c =>
queue(c).as {
c.traverse(_._1.get).flatMap(_.sequence.traverse(l => Sync[F].delay(l.toNel.getOrElse(throw new Throwable("Rediculous: Impossible Return List was Empty but we guarantee output matches input"))))).rethrow
c.traverse(_._1.get).flatMap(_.sequence.traverse(l => Sync[F].delay(l.toNel.getOrElse(throw RedisError.Generic("Rediculous: Impossible Return List was Empty but we guarantee output matches input"))))).rethrow
}
}
}
Expand All @@ -91,7 +91,7 @@ object RedisConnection{
fE.flatMap{
case Right(a) => a.pure[F]
case Left(e@Resp.Error(_)) => ApplicativeError[F, Throwable].raiseError[A](e)
case Left(other) => ApplicativeError[F, Throwable].raiseError[A](new Throwable(s"Rediculous: Incompatible Return Type for Operation: ${input.head}, got: $other"))
case Left(other) => ApplicativeError[F, Throwable].raiseError[A](RedisError.Generic(s"Rediculous: Incompatible Return Type for Operation: ${input.head}, got: $other"))
}
}
})
Expand All @@ -100,7 +100,7 @@ object RedisConnection{
fE.flatMap{
case Right(a) => a.pure[F]
case Left(e@Resp.Error(_)) => ApplicativeError[F, Throwable].raiseError[A](e)
case Left(other) => ApplicativeError[F, Throwable].raiseError[A](new Throwable(s"Rediculous: Incompatible Return Type: Got $other"))
case Left(other) => ApplicativeError[F, Throwable].raiseError[A](RedisError.Generic(s"Rediculous: Incompatible Return Type: Got $other"))
}

def single[F[_]: Concurrent: ContextShift](
Expand Down
21 changes: 21 additions & 0 deletions core/src/main/scala/io/chrisdavenport/rediculous/RedisError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.chrisdavenport.rediculous

/** Indicates a Error while processing for Rediculous */
trait RedisError extends RuntimeException {

/** Provides a message appropriate for logging. */
def message: String

/* Overridden for sensible logging of the failure */
final override def getMessage: String = message

def cause: Option[Throwable]

final override def getCause: Throwable = cause.orNull
}

object RedisError {
final case class Generic(message: String) extends RedisError{
val cause: Option[Throwable] = None
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import cats.effect._
* ClusterMode: Multi Key Operations Will use for the first key
* provided.
*
* [[pipeline]] method converts the Pipeline state to the Redis Monad.
* [[pipeline]] method converts the Pipeline state to the Redis Monad. This will error
* if you pipeline and have not actually enterred any redis commands.
**/
final case class RedisPipeline[A](value: RedisTransaction.RedisTxState[RedisTransaction.Queued[A]]){
def pipeline[F[_]: Concurrent]: Redis[F, A] = RedisPipeline.pipeline[F](this)
Expand Down Expand Up @@ -60,7 +61,7 @@ object RedisPipeline {
commands.traverse(nelCommands => RedisConnection.runRequestInternal(c)(nelCommands, key) // We Have to Actually Send A Command
.map{fNel => RedisConnection.closeReturn(fNel.map(a => f(a.toList)))}
).flatMap{fOpt =>
fOpt.map(_.pure[F]).getOrElse(F.raiseError(new Throwable("Rediculous: Attempted to Pipeline Empty Command")))
fOpt.map(_.pure[F]).getOrElse(F.raiseError(RedisError.Generic("Rediculous: Attempted to Pipeline Empty Command")))
}
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ object RedisResult extends RedisResultLowPriority{
case "list" => RedisProtocol.RedisType.List
case "set" => RedisProtocol.RedisType.Set
case "zset" => RedisProtocol.RedisType.ZSet
case _ => throw new Throwable(s"Rediculous: Unhandled red type: $value")
case _ => throw RedisError.Generic(s"Rediculous: Unhandled red type: $value")
})
case r => Left(r)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ object RedisTransaction {
), key).map{_.flatMap{_.last match {
case Resp.Array(Some(a)) => f(a).fold[TxResult[A]](e => TxResult.Error(e.toString), TxResult.Success(_)).pure[F]
case Resp.Array(None) => (TxResult.Aborted: TxResult[A]).pure[F]
case other => ApplicativeError[F, Throwable].raiseError(new Throwable(s"EXEC returned $other"))
case other => ApplicativeError[F, Throwable].raiseError(RedisError.Generic(s"EXEC returned $other"))
}}}
})
}
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/scala/io/chrisdavenport/rediculous/Resp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object Resp {
sealed trait RespParserResult[+A]
final case class ParseComplete[A](value: A, rest: SArray[Byte]) extends RespParserResult[A]
final case class ParseIncomplete(arr: SArray[Byte]) extends RespParserResult[Nothing]
final case class ParseError(message: String, cause: Option[Throwable]) extends Throwable(message, cause.orNull) with RespParserResult[Nothing]
final case class ParseError(message: String, cause: Option[Throwable]) extends RedisError with RespParserResult[Nothing]
private[Resp] val CR = '\r'.toByte
private[Resp] val LF = '\n'.toByte
private[Resp] val Plus = '+'.toByte
Expand Down Expand Up @@ -104,7 +104,10 @@ object Resp {
}
}
// First Byte is -
case class Error(value: String) extends Throwable(s"Resp Error- $value") with Resp
case class Error(value: String) extends RedisError with Resp{
def message: String = s"Resp Error- $value"
val cause: Option[Throwable] = None
}
object Error {
def encode(error: Error): SArray[Byte] =
SArray(Minus) ++ error.value.getBytes(StandardCharsets.UTF_8) ++ CRLF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object ClusterCommands {
} else None
}.flatMap{
case Some(a) => Sync[F].pure(a)
case None => Sync[F].raiseError[(String, Int)](new Throwable("Rediculous: No Servers Available"))
case None => Sync[F].raiseError[(String, Int)](RedisError.Generic("Rediculous: No Servers Available"))
}
}
object ClusterSlots {
Expand Down

0 comments on commit 12ba4e4

Please sign in to comment.