-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #49 from scoquelin/jl-scripting-poc
support eval scripting commands
- Loading branch information
Showing
6 changed files
with
588 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 193 additions & 0 deletions
193
...pi/src/main/scala/com/github/scoquelin/arugula/commands/RedisScriptingAsyncCommands.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
package com.github.scoquelin.arugula.commands | ||
|
||
import scala.concurrent.Future | ||
import scala.jdk.CollectionConverters.CollectionHasAsScala | ||
|
||
import com.github.scoquelin.arugula.commands.RedisScriptingAsyncCommands.ScriptOutputType | ||
|
||
trait RedisScriptingAsyncCommands[K, V] { | ||
/** | ||
* Evaluate a script | ||
* @param script The script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @return The result of the script | ||
*/ | ||
def eval(script: String, outputType: ScriptOutputType, keys:K*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script | ||
* @param script The script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @return The result of the script | ||
*/ | ||
def eval(script: Array[Byte], outputType: ScriptOutputType, keys:K*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script | ||
* @param script The script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @param values The values to use in the script | ||
* @return The result of the script | ||
*/ | ||
def eval(script: String, outputType: ScriptOutputType, keys: List[K], values: V*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script | ||
* @param script The script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @param values The values to use in the script | ||
* @return The result of the script | ||
*/ | ||
def eval(script: Array[Byte], outputType: ScriptOutputType, keys: List[K], values: V*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script by its SHA | ||
* @param sha The SHA of the script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @return The result of the script | ||
*/ | ||
def evalSha(sha: String, outputType: ScriptOutputType, keys:K*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script by its SHA | ||
* @param sha The SHA of the script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @return The result of the script | ||
*/ | ||
def evalSha(sha: String, outputType: ScriptOutputType, keys: List[K], values: V*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script in read-only mode | ||
* @param script The script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @param values The values to use in the script | ||
* @return The result of the script | ||
*/ | ||
def evalReadOnly(script: String, outputType: ScriptOutputType, keys:List[K], values: V*): Future[outputType.R] | ||
|
||
/** | ||
* Evaluate a script in read-only mode | ||
* @param script The script to evaluate | ||
* @param outputType The expected output type | ||
* @param keys The keys to use in the script | ||
* @param values The values to use in the script | ||
* @return The result of the script | ||
*/ | ||
def evalReadOnly(script: Array[Byte], outputType: ScriptOutputType, keys:List[K], values: V*): Future[outputType.R] | ||
|
||
/** | ||
* check if a script exists based on its SHA | ||
* @param digest The SHA of the script to check | ||
* @return True if the script exists, false otherwise | ||
*/ | ||
def scriptExists(digest: String): Future[Boolean] | ||
|
||
/** | ||
* check if a script exists based on its SHA | ||
* @param digests The SHA of the scripts to check | ||
* @return A list of booleans indicating if the scripts exist | ||
*/ | ||
def scriptExists(digests: List[String]): Future[List[Boolean]] | ||
|
||
/** | ||
* Flush all scripts from the script cache | ||
* @return Unit | ||
*/ | ||
def scriptFlush: Future[Unit] | ||
|
||
/** | ||
* Flush all scripts from the script cache | ||
* @param mode The mode to use for flushing | ||
* @return Unit | ||
*/ | ||
def scriptFlush(mode: RedisScriptingAsyncCommands.FlushMode): Future[Unit] | ||
|
||
/** | ||
* Kill the currently executing script | ||
* @return Unit | ||
*/ | ||
def scriptKill: Future[Unit] | ||
|
||
/** | ||
* Load a script into the script cache | ||
* @param script The script to load | ||
* @return The SHA of the script | ||
*/ | ||
def scriptLoad(script: String): Future[String] | ||
|
||
/** | ||
* Load a script into the script cache | ||
* @param script The script to load | ||
* @return The SHA of the script | ||
*/ | ||
def scriptLoad(script: Array[Byte]): Future[String] | ||
} | ||
|
||
object RedisScriptingAsyncCommands{ | ||
|
||
sealed trait ScriptOutputType { | ||
type R | ||
|
||
def convert(in: Any): R | ||
} | ||
object ScriptOutputType { | ||
case object Boolean extends ScriptOutputType{ | ||
type R = Boolean | ||
|
||
def convert(in: Any): R = in match { | ||
case 0L => false | ||
case 1L => true | ||
case b: Boolean => b | ||
case s: String => s.toBoolean | ||
case _ => throw new IllegalArgumentException(s"Cannot convert $in to Boolean") | ||
} | ||
} | ||
case object Integer extends ScriptOutputType{ | ||
type R = Long | ||
|
||
def convert(in: Any): R = in match { | ||
case l: Long => l | ||
case i: Int => i.toLong | ||
case s: String => s.toLong | ||
case _ => throw new IllegalArgumentException(s"Cannot convert $in to Int") | ||
} | ||
|
||
} | ||
case object Multi extends ScriptOutputType{ | ||
type R = List[Any] | ||
|
||
def convert(in: Any): R = in match { | ||
case l: List[_] => l | ||
case l: java.util.List[_] => l.asScala.toList | ||
case _ => throw new IllegalArgumentException(s"Cannot convert $in to List") | ||
} | ||
} | ||
case object Status extends ScriptOutputType{ | ||
type R = String | ||
|
||
def convert(in: Any): R = in match { | ||
case s: String => s | ||
case _ => throw new IllegalArgumentException(s"Cannot convert $in to String") | ||
} | ||
} | ||
case object Value extends ScriptOutputType{ | ||
type R = Any | ||
|
||
def convert(in: Any): R = in | ||
} | ||
} | ||
|
||
sealed trait FlushMode | ||
|
||
object FlushMode{ | ||
case object Async extends FlushMode | ||
case object Sync extends FlushMode | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
...main/scala/com/github/scoquelin/arugula/commands/LettuceRedisScriptingAsyncCommands.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package com.github.scoquelin.arugula.commands | ||
|
||
import scala.concurrent.Future | ||
import scala.jdk.CollectionConverters._ | ||
|
||
import com.github.scoquelin.arugula.RedisCommandsClient | ||
import com.github.scoquelin.arugula.commands.RedisScriptingAsyncCommands.ScriptOutputType | ||
import com.github.scoquelin.arugula.internal.LettuceRedisCommandDelegation | ||
|
||
|
||
trait LettuceRedisScriptingAsyncCommands[K, V] extends RedisScriptingAsyncCommands[K, V] with LettuceRedisCommandDelegation[K, V] { this: RedisCommandsClient[K, V] => | ||
|
||
|
||
override def eval(script: String, outputType: ScriptOutputType, keys:K*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.eval( | ||
script, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys:_*)).map(outputType.convert) | ||
} | ||
|
||
override def eval(script: Array[Byte], outputType: ScriptOutputType, keys: K*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.eval( | ||
script, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys:_*)).map(outputType.convert) | ||
} | ||
|
||
override def eval(script: String, outputType: ScriptOutputType, keys: List[K], values: V*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.eval( | ||
script, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys.toArray[Any].asInstanceOf[Array[K with AnyRef]], | ||
values:_*)).map(outputType.convert) | ||
} | ||
|
||
override def eval(script: Array[Byte], outputType: ScriptOutputType, keys: List[K], values: V*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.eval( | ||
script, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys.toArray[Any].asInstanceOf[Array[K with AnyRef]], | ||
values: _*)).map(outputType.convert) | ||
} | ||
|
||
override def evalSha(sha: String, outputType: ScriptOutputType, keys:K*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.evalsha( | ||
sha, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys:_*)).map(outputType.convert) | ||
} | ||
|
||
override def evalSha(sha: String, outputType: ScriptOutputType, keys: List[K], values: V*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.evalsha( | ||
sha, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys.toArray[Any].asInstanceOf[Array[K with AnyRef]], | ||
values:_*)).map(outputType.convert) | ||
} | ||
|
||
override def evalReadOnly(script: String, outputType: ScriptOutputType, keys:List[K], values: V*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.evalReadOnly( | ||
script.getBytes, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys.toArray[Any].asInstanceOf[Array[K with AnyRef]], | ||
values:_* | ||
)).map(outputType.convert) | ||
} | ||
|
||
override def evalReadOnly(script: Array[Byte], outputType: ScriptOutputType, keys:List[K], values: V*): Future[outputType.R] = { | ||
delegateRedisClusterCommandAndLift(_.evalReadOnly( | ||
script, | ||
LettuceRedisScriptingAsyncCommands.outputTypeToJava(outputType), | ||
keys.toArray[Any].asInstanceOf[Array[K with AnyRef]], | ||
values:_* | ||
)).map(outputType.convert) | ||
} | ||
|
||
override def scriptExists(digest: String): Future[Boolean] = | ||
delegateRedisClusterCommandAndLift(_.scriptExists(digest)).map { | ||
_.asScala.headOption.exists(Boolean2boolean) | ||
} | ||
|
||
override def scriptExists(digests: List[String]): Future[List[Boolean]] = { | ||
delegateRedisClusterCommandAndLift(_.scriptExists(digests:_*)).map(_.asScala.map(Boolean2boolean).toList) | ||
} | ||
|
||
override def scriptFlush: Future[Unit] = { | ||
delegateRedisClusterCommandAndLift(_.scriptFlush()).map(_ => ()) | ||
} | ||
|
||
override def scriptFlush(mode: RedisScriptingAsyncCommands.FlushMode): Future[Unit] = { | ||
delegateRedisClusterCommandAndLift(_.scriptFlush( | ||
mode match { | ||
case RedisScriptingAsyncCommands.FlushMode.Async => io.lettuce.core.FlushMode.ASYNC | ||
case RedisScriptingAsyncCommands.FlushMode.Sync => io.lettuce.core.FlushMode.SYNC | ||
} | ||
)).map(_ => ()) | ||
} | ||
|
||
override def scriptKill: Future[Unit] = | ||
delegateRedisClusterCommandAndLift(_.scriptKill()).map(_ => ()) | ||
|
||
override def scriptLoad(script: String): Future[String] = { | ||
delegateRedisClusterCommandAndLift(_.scriptLoad(script)) | ||
} | ||
|
||
override def scriptLoad(script: Array[Byte]): Future[String] = | ||
delegateRedisClusterCommandAndLift(_.scriptLoad(script)) | ||
|
||
} | ||
|
||
object LettuceRedisScriptingAsyncCommands{ | ||
private[commands] def outputTypeToJava(outputType: ScriptOutputType): io.lettuce.core.ScriptOutputType = outputType match { | ||
case ScriptOutputType.Boolean => io.lettuce.core.ScriptOutputType.BOOLEAN | ||
case ScriptOutputType.Integer => io.lettuce.core.ScriptOutputType.INTEGER | ||
case ScriptOutputType.Multi => io.lettuce.core.ScriptOutputType.MULTI | ||
case ScriptOutputType.Status => io.lettuce.core.ScriptOutputType.STATUS | ||
case ScriptOutputType.Value => io.lettuce.core.ScriptOutputType.VALUE | ||
} | ||
} |
Oops, something went wrong.