All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avsystem.commons.redis.raw.scala Maven / Gradle / Ivy

package com.avsystem.commons
package redis

import akka.util.ByteString
import com.avsystem.commons.redis.RawCommand.Level
import com.avsystem.commons.redis.exception.ForbiddenCommandException
import com.avsystem.commons.redis.protocol.{ArrayMsg, BulkStringMsg, RedisMsg, RedisReply}

import scala.collection.mutable.ArrayBuffer

/**
  * One or more raw Redis commands. More lightweight than regular Scala collection
  * (avoids wrapping in case of single element).
  */
trait RawCommands {
  def emitCommands(consumer: RawCommand => Unit): Unit
}

trait RawCommand extends RawCommandPack with RawCommands with ReplyPreprocessor {
  def encoded: ArrayMsg[BulkStringMsg]
  def updateWatchState(message: RedisMsg, state: WatchState): Unit = ()
  def level: Level

  final def checkLevel(minAllowed: Level, clientType: String) =
    if (!minAllowed.allows(level)) {
      throw new ForbiddenCommandException(this, clientType)
    }

  final def rawCommands(inTransaction: Boolean) = this
  final def emitCommands(consumer: RawCommand => Unit) = consumer(this)
  final def createPreprocessor(replyCount: Int) = this
  final def preprocess(message: RedisMsg, state: WatchState) = {
    updateWatchState(message, state)
    Opt(message)
  }

  override final def isAsking = false

  protected final def encoder(command: String): CommandEncoder =
    new CommandEncoder(new ArrayBuffer).add(command)

  protected final def encoder(command: String, subcommand: String): CommandEncoder =
    new CommandEncoder(new ArrayBuffer).add(command).add(subcommand)
}

trait UnsafeCommand extends RawCommand {
  def level = Level.Unsafe
}
trait ConnectionCommand extends RawCommand {
  def level = Level.Connection
}
trait OperationCommand extends RawCommand {
  def level = Level.Operation
}
trait NodeCommand extends RawCommand {
  def level = Level.Node
}

object RawCommand {
  case class Level(raw: Int) extends AnyVal {
    def allows(other: Level) = raw <= other.raw
  }
  object Level {
    val Unsafe = Level(0)
    val Connection = Level(1)
    val Operation = Level(2)
    val Node = Level(3)
  }
}

/**
  * One or more [[RawCommandPack]]s. Conceptually pretty much the same as `Traversable[RawCommandPack]]`
  * but more lightweight.
  */
trait RawCommandPacks {
  def emitCommandPacks(consumer: RawCommandPack => Unit): Unit
  def computeSize(limit: Int): Int

  final def foreachKey(consumer: ByteString => Unit): Unit =
    emitCommandPacks(_.rawCommands(inTransaction = false)
      .emitCommands(_.encoded.elements.foreach(bs => if (bs.isCommandKey) consumer(bs.string))))

  final def encodedSize: Int = {
    var result = 0
    emitCommandPacks(_.rawCommands(inTransaction = false)
      .emitCommands(c => result += RedisMsg.encodedSize(c.encoded)))
    result
  }

  final def requireLevel(minAllowed: Level, clientType: String): this.type = {
    emitCommandPacks(_.checkLevel(minAllowed, clientType))
    this
  }
}

/**
  * Represents a sequence of commands that is always executed atomically, using a single network call
  * on a single Redis connection.
  */
trait RawCommandPack extends RawCommandPacks {
  def rawCommands(inTransaction: Boolean): RawCommands
  def createPreprocessor(replyCount: Int): ReplyPreprocessor
  def checkLevel(minAllowed: Level, clientType: String): Unit

  def isAsking: Boolean = false
  final def emitCommandPacks(consumer: RawCommandPack => Unit) = consumer(this)
  final def computeSize(limit: Int) = limit min 1
}

trait WatchState {
  var watching: Boolean = false
}

/**
  * Something that translates incoming [[protocol.RedisMsg RedisMsg]]
  * messages and emits a single [[protocol.RedisReply RedisReply]].
  * For example, it may handle transactions by extracting actual responses for every command from
  * the `EXEC` response and returning them in a [[protocol.TransactionReply TransactionReply]]
  * (see [[Transaction]]).
  */
trait ReplyPreprocessor {
  def preprocess(message: RedisMsg, watchState: WatchState): Opt[RedisReply]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy