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

com.avsystem.commons.redis.commands.strings.scala Maven / Gradle / Ivy

package com.avsystem.commons
package redis.commands

import com.avsystem.commons.misc.{NamedEnum, NamedEnumCompanion}
import com.avsystem.commons.redis.CommandEncoder.CommandArg
import com.avsystem.commons.redis._
import com.avsystem.commons.redis.commands.ReplyDecoders._

trait StringsApi extends ApiSubset {
  /** Executes [[http://redis.io/commands/append APPEND]] */
  def append(key: Key, value: Value): Result[Int] =
    execute(new Append(key, value))

  /** Executes [[http://redis.io/commands/bitcount BITCOUNT]] */
  def bitcount(key: Key, range: OptArg[(Int, Int)] = OptArg.Empty): Result[Long] =
    execute(new Bitcount(key, range.toOpt))

  /** Executes [[http://redis.io/commands/bitfield BITFIELD]] */
  def bitfield(key: Key, op: BitFieldOp): Result[Opt[Long]] =
    execute(new Bitfield(key, op.single).map(_.head))

  /** Executes [[http://redis.io/commands/bitfield BITFIELD]] */
  def bitfield(key: Key, op: BitFieldOp, ops: BitFieldOp*): Result[Seq[Opt[Long]]] =
    execute(new Bitfield(key, op +:: ops))

  /** Executes [[http://redis.io/commands/bitfield BITFIELD]] */
  def bitfield(key: Key, ops: Iterable[BitFieldOp]): Result[Seq[Opt[Long]]] =
    execute(new Bitfield(key, ops))

  /** Executes [[http://redis.io/commands/bitop BITOP]] */
  def bitop(multiOperation: MultiBitOp, destkey: Key, keys: Key*): Result[Int] =
    execute(new Bitop(multiOperation, destkey, keys))

  /** Executes [[http://redis.io/commands/bitop BITOP]] */
  def bitopNot(destkey: Key, key: Key): Result[Int] =
    execute(new Bitop(BitOp.Not, destkey, List(key)))

  /** Executes [[http://redis.io/commands/bitpos BITPOS]] */
  def bitpos(key: Key, bit: Boolean): Result[Long] =
    execute(new Bitpos(key, bit, Opt.Empty))

  /** Executes [[http://redis.io/commands/bitpos BITPOS]] */
  def bitpos(key: Key, bit: Boolean, start: Int): Result[Long] =
    execute(new Bitpos(key, bit, SemiRange(start).opt))

  /** Executes [[http://redis.io/commands/bitpos BITPOS]] */
  def bitpos(key: Key, bit: Boolean, start: Int, end: Int): Result[Long] =
    execute(new Bitpos(key, bit, SemiRange(start, end.opt).opt))

  /** Executes [[http://redis.io/commands/decr DECR]] */
  def decr(key: Key): Result[Long] =
    execute(new Decr(key))

  /** Executes [[http://redis.io/commands/decrby DECRBY]] */
  def decrby(key: Key, decrement: Long): Result[Long] =
    execute(new Decrby(key, decrement))

  /** Executes [[http://redis.io/commands/get GET]] */
  def get(key: Key): Result[Opt[Value]] =
    execute(new Get(key))

  /** Executes [[http://redis.io/commands/getbit GETBIT]] */
  def getbit(key: Key, offset: Int): Result[Boolean] =
    execute(new Getbit(key, offset))

  /** Executes [[http://redis.io/commands/getdel GETDEL]] */
  def getdel(key: Key): Result[Opt[Value]] =
    execute(new Getdel(key))

  /** Executes [[http://redis.io/commands/getex GETEX]] */
  def getex(key: Key, expiration: GetExpiration): Result[Opt[Value]] =
    execute(new Getex(key, expiration))

  /** Executes [[http://redis.io/commands/getrange GETRANGE]] */
  def getrange(key: Key, start: Int = 0, end: Int = -1): Result[Value] =
    execute(new Getrange(key, start, end))

  /** Executes [[http://redis.io/commands/getset GETSET]] */
  def getset(key: Key, value: Value): Result[Opt[Value]] =
    execute(new Getset(key, value))

  /** Executes [[http://redis.io/commands/incr INCR]] */
  def incr(key: Key): Result[Long] =
    execute(new Incr(key))

  /** Executes [[http://redis.io/commands/incrby INCRBY]] */
  def incrby(key: Key, increment: Long): Result[Long] =
    execute(new Incrby(key, increment))

  /** Executes [[http://redis.io/commands/incrbyfloat INCRBYFLOAT]] */
  def incrbyfloat(key: Key, increment: Double): Result[Double] =
    execute(new Incrbyfloat(key, increment))

  /** Executes [[http://redis.io/commands/mget MGET]] */
  def mget(key: Key, keys: Key*): Result[Seq[Opt[Value]]] =
    execute(new Mget(key +:: keys))

  /** Executes [[http://redis.io/commands/mget MGET]]
    * or simply returns empty `Seq` when `keys` is empty, without sending the command to Redis */
  def mget(keys: Iterable[Key]): Result[Seq[Opt[Value]]] =
    execute(new Mget(keys))

  /** Executes [[http://redis.io/commands/mset MSET]] */
  def mset(keyValue: (Key, Value), keyValues: (Key, Value)*): Result[Unit] =
    execute(new Mset(keyValue +:: keyValues))

  /** Executes [[http://redis.io/commands/mset MSET]]
    * or does nothing when `keyValues` is empty, without sending the command to Redis */
  def mset(keyValues: Iterable[(Key, Value)]): Result[Unit] =
    execute(new Mset(keyValues))

  /** Executes [[http://redis.io/commands/msetnx MSETNX]] */
  def msetnx(keyValue: (Key, Value), keyValues: (Key, Value)*): Result[Boolean] =
    execute(new Msetnx(keyValue +:: keyValues))

  /** Executes [[http://redis.io/commands/msetnx MSETNX]]
    * or simply returns `true` when `keyValues` is empty, without sending the command to Redis */
  def msetnx(keyValues: Iterable[(Key, Value)]): Result[Boolean] =
    execute(new Msetnx(keyValues))

  /** Executes [[http://redis.io/commands/psetex PSETEX]] */
  def psetex(key: Key, milliseconds: Long, value: Value): Result[Unit] =
    execute(new Psetex(key, milliseconds, value))

  /** Executes [[http://redis.io/commands/set SET]] */
  def set(
    key: Key,
    value: Value,
    expiration: OptArg[SetExpiration] = OptArg.Empty,
    existence: OptArg[Existence] = OptArg.Empty
  ): Result[Boolean] =
    execute(new Set(key, value, expiration.toOpt, existence.toOpt))

  /** Executes [[http://redis.io/commands/set SET]] with `GET` option */
  def setGet(
    key: Key,
    value: Value,
    expiration: OptArg[SetExpiration] = OptArg.Empty,
    existence: OptArg[Existence] = OptArg.Empty
  ): Result[Opt[Value]] =
    execute(new SetGet(key, value, expiration.toOpt, existence.toOpt))

  /** Executes [[http://redis.io/commands/setbit SETBIT]] */
  def setbit(key: Key, offset: Long, value: Boolean): Result[Boolean] =
    execute(new Setbit(key, offset, value))

  /** Executes [[http://redis.io/commands/setex SETEX]] */
  def setex(key: Key, seconds: Long, value: Value): Result[Unit] =
    execute(new Setex(key, seconds, value))

  /** Executes [[http://redis.io/commands/setnx SETNX]] */
  def setnx(key: Key, value: Value): Result[Boolean] =
    execute(new Setnx(key, value))

  /** Executes [[http://redis.io/commands/setrange SETRANGE]] */
  def setrange(key: Key, offset: Int, value: Value): Result[Int] =
    execute(new Setrange(key, offset, value))

  /** Executes [[http://redis.io/commands/strlen STRLEN]] */
  def strlen(key: Key): Result[Int] =
    execute(new Strlen(key))

  private final class Append(key: Key, value: Value) extends RedisIntCommand with NodeCommand {
    val encoded: Encoded = encoder("APPEND").key(key).data(value).result
  }

  private final class Bitcount(key: Key, range: Opt[(Int, Int)]) extends RedisLongCommand with NodeCommand {
    val encoded: Encoded = encoder("BITCOUNT").key(key).optAdd(range).result
  }

  private final class Bitfield(key: Key, ops: Iterable[BitFieldOp])
    extends RedisSeqCommand[Opt[Long]](nullBulkOr(integerAsLong)) with NodeCommand {

    val encoded: Encoded = {
      import BitFieldOp._
      val enc = encoder("BITFIELD").key(key)
      def loop(it: Iterator[BitFieldOp], curOverflow: Overflow): Unit = if (it.hasNext) {
        def ensureOverflow(overflow: Overflow) =
          if (overflow != curOverflow) {
            enc.add("OVERFLOW").add(overflow)
          }
        it.next() match {
          case Get(field) =>
            enc.add("GET").add(field)
            loop(it, curOverflow)
          case Set(field, overflow, value) =>
            ensureOverflow(overflow)
            enc.add("SET").add(field).add(value)
            loop(it, overflow)
          case Incrby(field, overflow, increment) =>
            ensureOverflow(overflow)
            enc.add("INCRBY").add(field).add(increment)
            loop(it, overflow)
        }
      }
      loop(ops.iterator, Overflow.Wrap)
      enc.result
    }
  }

  private final class Bitop(bitop: BitOp, destkey: Key, keys: Seq[Key]) extends RedisIntCommand with NodeCommand {
    require(keys.nonEmpty, "BITOP requires at least one source key")
    require(bitop != BitOp.Not || keys.size == 1, "BITOP NOT requires exactly one source key")
    val encoded: Encoded = encoder("BITOP").add(bitop).key(destkey).keys(keys).result
  }

  private final class Bitpos(key: Key, bit: Boolean, range: Opt[SemiRange]) extends RedisLongCommand with NodeCommand {
    val encoded: Encoded = encoder("BITPOS").key(key).add(bit).optAdd(range).result
  }

  private final class Decr(key: Key) extends RedisLongCommand with NodeCommand {
    val encoded: Encoded = encoder("DECR").key(key).result
  }

  private final class Decrby(key: Key, decrement: Long) extends RedisLongCommand with NodeCommand {
    val encoded: Encoded = encoder("DECRBY").key(key).add(decrement).result
  }

  private final class Get(key: Key) extends RedisOptDataCommand[Value] with NodeCommand {
    val encoded: Encoded = encoder("GET").key(key).result
  }

  private final class Getbit(key: Key, offset: Int) extends RedisBooleanCommand with NodeCommand {
    val encoded: Encoded = encoder("GETBIT").key(key).add(offset).result
  }

  private final class Getdel(key: Key) extends RedisOptDataCommand[Value] with NodeCommand {
    val encoded: Encoded = encoder("GETDEL").key(key).result
  }

  private final class Getex(key: Key, expiration: GetExpiration) extends RedisOptDataCommand[Value] with NodeCommand {
    val encoded: Encoded = encoder("GETEX").key(key).add(expiration).result
  }

  private final class Getrange(key: Key, start: Int, end: Int) extends RedisDataCommand[Value] with NodeCommand {
    val encoded: Encoded = encoder("GETRANGE").key(key).add(start).add(end).result
  }

  private final class Getset(key: Key, value: Value) extends RedisOptDataCommand[Value] with NodeCommand {
    val encoded: Encoded = encoder("GETSET").key(key).data(value).result
  }

  private final class Incr(key: Key) extends RedisLongCommand with NodeCommand {
    val encoded: Encoded = encoder("INCR").key(key).result
  }

  private final class Incrby(key: Key, increment: Long) extends RedisLongCommand with NodeCommand {
    val encoded: Encoded = encoder("INCRBY").key(key).add(increment).result
  }

  private final class Incrbyfloat(key: Key, increment: Double) extends RedisDoubleCommand with NodeCommand {
    val encoded: Encoded = encoder("INCRBYFLOAT").key(key).add(increment).result
  }

  private final class Mget(keys: Iterable[Key]) extends RedisOptDataSeqCommand[Value] with NodeCommand {
    val encoded: Encoded = encoder("MGET").keys(keys).result
    override def immediateResult: Opt[Seq[Opt[Value]]] = whenEmpty(keys, Seq.empty)
  }

  private final class Mset(keyValues: Iterable[(Key, Value)]) extends RedisUnitCommand with NodeCommand {
    val encoded: Encoded = encoder("MSET").keyDatas(keyValues).result
    override def immediateResult: Opt[Unit] = whenEmpty(keyValues, ())
  }

  private final class Msetnx(keyValues: Iterable[(Key, Value)]) extends RedisBooleanCommand with NodeCommand {
    val encoded: Encoded = encoder("MSETNX").keyDatas(keyValues).result
    override def immediateResult: Opt[Boolean] = whenEmpty(keyValues, true)
  }

  private final class Psetex(key: Key, milliseconds: Long, value: Value) extends RedisUnitCommand with NodeCommand {
    val encoded: Encoded = encoder("PSETEX").key(key).add(milliseconds).data(value).result
  }

  private abstract class AbstractSet[T](decoder: ReplyDecoder[T])(
    key: Key, value: Value, expiration: Opt[SetExpiration], existence: Opt[Existence], get: Boolean
  ) extends AbstractRedisCommand[T](decoder) with NodeCommand {

    val encoded: Encoded = encoder("SET").key(key).data(value).optAdd(expiration)
      .optAdd(existence).addFlag("GET", get).result
  }

  private final class Set(key: Key, value: Value, expiration: Opt[SetExpiration], existence: Opt[Existence])
    extends AbstractSet[Boolean](nullBulkOrSimpleOkAsBoolean)(key, value, expiration, existence, get = false)

  private final class SetGet(key: Key, value: Value, expiration: Opt[SetExpiration], existence: Opt[Existence])
    extends AbstractSet[Opt[Value]](nullBulkOrAs[Value])(key, value, expiration, existence, get = true)

  private final class Setbit(key: Key, offset: Long, value: Boolean) extends RedisBooleanCommand with NodeCommand {
    val encoded: Encoded = encoder("SETBIT").key(key).add(offset).add(value).result
  }

  private final class Setex(key: Key, seconds: Long, value: Value) extends RedisUnitCommand with NodeCommand {
    val encoded: Encoded = encoder("SETEX").key(key).add(seconds).data(value).result
  }

  private final class Setnx(key: Key, value: Value) extends RedisBooleanCommand with NodeCommand {
    val encoded: Encoded = encoder("SETNX").key(key).data(value).result
  }

  private final class Setrange(key: Key, offset: Int, value: Value) extends RedisIntCommand with NodeCommand {
    val encoded: Encoded = encoder("SETRANGE").key(key).add(offset).data(value).result
  }

  private final class Strlen(key: Key) extends RedisIntCommand with NodeCommand {
    val encoded: Encoded = encoder("STRLEN").key(key).result
  }
}

case class BitFieldType(signed: Boolean, width: Int) {
  def at(offset: Long): BitField = BitField(this, offset, offsetInWidths = false)
  def atWidths(offset: Long): BitField = BitField(this, offset, offsetInWidths = true)
}
case class BitField(bfType: BitFieldType, offset: Long, offsetInWidths: Boolean) {

  import BitFieldOp._

  def get: BitFieldOp = Get(this)
  def set(value: Long): BitFieldMod = Set(this, Overflow.Wrap, value)
  def incrby(value: Long): BitFieldMod = Incrby(this, Overflow.Wrap, value)
}
object BitField {
  def signed(width: Int): BitFieldType = BitFieldType(signed = true, width)
  def unsigned(width: Int): BitFieldType = BitFieldType(signed = false, width)

  implicit val commandArg: CommandArg[BitField] = CommandArg {
    case (enc, BitField(BitFieldType(signed, width), offset, offsetInWidths)) =>
      enc.add((if (signed) "i" else "u") + width.toString).add((if (offsetInWidths) "#" else "") + offset.toString)
  }
}

sealed trait BitFieldOp {
  def field: BitField
}

sealed trait BitFieldMod extends BitFieldOp {

  import BitFieldOp.Overflow

  def overflow: Overflow
  def overflow(of: Overflow): BitFieldMod

  def overflowWrap: BitFieldMod = overflow(Overflow.Wrap)
  def overflowSat: BitFieldMod = overflow(Overflow.Sat)
  def overflowFail: BitFieldMod = overflow(Overflow.Fail)
}

object BitFieldOp {
  sealed abstract class Overflow(val name: String) extends NamedEnum
  object Overflow extends NamedEnumCompanion[Overflow] {
    case object Wrap extends Overflow("WRAP")
    case object Sat extends Overflow("SAT")
    case object Fail extends Overflow("FAIL")
    val values: List[Overflow] = caseObjects
  }

  case class Get(field: BitField) extends BitFieldOp
  case class Set(field: BitField, overflow: Overflow, value: Long) extends BitFieldMod {
    def overflow(of: Overflow): BitFieldMod = copy(overflow = of)
  }
  case class Incrby(field: BitField, overflow: Overflow, increment: Long) extends BitFieldMod {
    def overflow(of: Overflow): BitFieldMod = copy(overflow = of)
  }
}

sealed abstract class BitOp(val name: String) extends NamedEnum
sealed abstract class MultiBitOp(name: String) extends BitOp(name)
object BitOp extends NamedEnumCompanion[BitOp] {
  case object And extends MultiBitOp("AND")
  case object Or extends MultiBitOp("OR")
  case object Xor extends MultiBitOp("XOR")
  case object Not extends BitOp("NOT")

  val values: List[BitOp] = caseObjects
}

case class SemiRange(start: Int, end: Opt[Int] = Opt.Empty)
object SemiRange {
  implicit val SemiRangeArg: CommandArg[SemiRange] =
    CommandArg((enc, sa) => enc.add(sa.start).optAdd(sa.end))
}

sealed trait Existence extends Product with Serializable
object Existence {
  case object XX extends Existence
  case object NX extends Existence

  implicit val ExistenceArg: CommandArg[Existence] =
    CommandArg((ce, ex) => ce.add(ex.productPrefix))
}

sealed trait Comparison extends Product with Serializable
object Comparison {
  case object GT extends Comparison
  case object LT extends Comparison

  implicit val ComparisonArg: CommandArg[Comparison] =
    CommandArg((ce, ex) => ce.add(ex.productPrefix))
}

sealed trait Expiration extends Product with Serializable
sealed trait SetExpiration extends Expiration
sealed trait GetExpiration extends Expiration
object Expiration {
  case class Ex(seconds: Long) extends SetExpiration with GetExpiration
  case class Px(milliseconds: Long) extends SetExpiration with GetExpiration
  case class ExAt(secondsTimestamp: Long) extends SetExpiration with GetExpiration
  case class PxAt(millisecondsTimestamp: Long) extends SetExpiration with GetExpiration
  case object KeepTtl extends SetExpiration
  case object Persist extends GetExpiration

  implicit val ExpirationArg: CommandArg[Expiration] = CommandArg {
    case (ce, Ex(seconds)) => ce.add("EX").add(seconds)
    case (ce, Px(milliseconds)) => ce.add("PX").add(milliseconds)
    case (ce, ExAt(timestmap)) => ce.add("EXAT").add(timestmap)
    case (ce, PxAt(timestmap)) => ce.add("PXAT").add(timestmap)
    case (ce, KeepTtl) => ce.add("KEEPTTL")
    case (ce, Persist) => ce.add("PERSIST")
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy