diff --git a/core/src/main/scala/zio/tarantool/TarantoolError.scala b/core/src/main/scala/zio/tarantool/TarantoolError.scala index 86585bf..8895339 100644 --- a/core/src/main/scala/zio/tarantool/TarantoolError.scala +++ b/core/src/main/scala/zio/tarantool/TarantoolError.scala @@ -4,30 +4,32 @@ import java.io.IOException import zio.tarantool.protocol.{MessagePackPacket, RequestCode, ResponseCode} -sealed abstract class TarantoolError(message: String, cause: Throwable) extends Exception(message, cause) +sealed abstract class TarantoolError(message: String, cause: Option[Throwable]) extends Exception(message, cause.orNull) object TarantoolError { - final case class IOError(exception: IOException) extends TarantoolError(exception.getLocalizedMessage, exception) - final case class InternalError(cause: Throwable) extends TarantoolError(cause.getLocalizedMessage, cause) + final case class IOError(exception: IOException) + extends TarantoolError(exception.getLocalizedMessage, Some(exception)) + final case class InternalError(cause: Throwable) extends TarantoolError(cause.getLocalizedMessage, Some(cause)) - final case class NotSupportedUpdateOperation(msg: String) extends TarantoolError(msg, null) + final case class UpdateOperationError(msg: String) extends TarantoolError(msg, None) - final case class AuthError(message: String, code: ResponseCode) extends TarantoolError(s"$message. Code: $code", null) + final case class AuthError(message: String, code: ResponseCode) extends TarantoolError(s"$message. Code: $code", None) - final case class SpaceNotFound(space: String) extends TarantoolError(space, null) - final case class IndexNotFound(space: String, index: String) extends TarantoolError(s"$space:$index", null) + final case class SpaceNotFound(space: String) extends TarantoolError(space, None) + final case class IndexNotFound(space: String, index: String) extends TarantoolError(s"$space:$index", None) - final case class ProtocolError(message: String) extends TarantoolError(message, null) - final case class CodecError(exception: Throwable) extends TarantoolError(exception.getLocalizedMessage, exception) - case object EmptyResultSet extends TarantoolError("Empty result set", null) - final case class Timeout(message: String) extends TarantoolError(message, null) - final case class UnknownResponseCode(mp: MessagePackPacket) extends TarantoolError("Unknown response code", null) + final case class ProtocolError(message: String) extends TarantoolError(message, None) + final case class CodecError(exception: Throwable) + extends TarantoolError(exception.getLocalizedMessage, Some(exception)) + case object EmptyResultSet extends TarantoolError("Empty result set", None) + final case class Timeout(message: String) extends TarantoolError(message, None) + final case class UnknownResponseCode(mp: MessagePackPacket) extends TarantoolError("Unknown response code", None) final case class OperationException(reason: String, errorCode: Int) - extends TarantoolError(s"[$errorCode] $reason", null) - final case class NotFoundOperation(syncId: Long) extends TarantoolError(syncId.toString, null) - final case class DuplicateOperation(syncId: Long) extends TarantoolError(syncId.toString, null) - final case class DeclinedOperation(syncId: Long, code: RequestCode) extends TarantoolError(s"$code -- $syncId", null) + extends TarantoolError(s"[$errorCode] $reason", None) + final case class NotFoundOperation(syncId: Long) extends TarantoolError(syncId.toString, None) + final case class DuplicateOperation(syncId: Long) extends TarantoolError(syncId.toString, None) + final case class DeclinedOperation(syncId: Long, code: RequestCode) extends TarantoolError(s"$code -- $syncId", None) private[tarantool] val toIOError: PartialFunction[Throwable, TarantoolError.IOError] = { case e: IOException => TarantoolError.IOError(e) diff --git a/core/src/main/scala/zio/tarantool/api/SelectQuery.scala b/core/src/main/scala/zio/tarantool/api/SelectQuery.scala new file mode 100644 index 0000000..eec9e77 --- /dev/null +++ b/core/src/main/scala/zio/tarantool/api/SelectQuery.scala @@ -0,0 +1,24 @@ +package zio.tarantool.api + +import org.msgpack.value.Value +import zio.tarantool.internal.schema.SpaceMeta +import zio.tarantool.protocol.{IteratorCode, TarantoolRequestBody} + +final case class SelectQuery(indexId: Long, iterator: IteratorCode, limit: Long, offset: Long) { + def encode(spaceMeta: SpaceMeta, key: Value): Map[Long, Value] = + TarantoolRequestBody.selectBody(spaceMeta.spaceId, indexId, limit, offset, iterator, key) +} + +object SelectQuery { + val default: SelectQuery = SelectQuery(0, IteratorCode.Eq, 0, 0) + + def builder(): SelectQueryBuilder = new SelectQueryBuilder() + + final class SelectQueryBuilder { + private var indexId: Long = 0 + private var iterator: IteratorCode = IteratorCode.Eq + private var limit: Long = 0 + private var offset: Long = 0 + } + +} diff --git a/core/src/main/scala/zio/tarantool/api/TarantoolCallOperations.scala b/core/src/main/scala/zio/tarantool/api/TarantoolCallOperations.scala new file mode 100644 index 0000000..78ea0c3 --- /dev/null +++ b/core/src/main/scala/zio/tarantool/api/TarantoolCallOperations.scala @@ -0,0 +1,3 @@ +package zio.tarantool.api + +class TarantoolCallOperations {} diff --git a/core/src/main/scala/zio/tarantool/api/TarantoolSpaceOperations.scala b/core/src/main/scala/zio/tarantool/api/TarantoolSpaceOperations.scala new file mode 100644 index 0000000..0ab751e --- /dev/null +++ b/core/src/main/scala/zio/tarantool/api/TarantoolSpaceOperations.scala @@ -0,0 +1,111 @@ +package zio.tarantool.api + +import org.msgpack.value.Value +import zio._ +import zio.tarantool.TarantoolError +import zio.tarantool.codec.TupleEncoder +import zio.tarantool.internal.schema.SpaceMeta +import zio.tarantool.internal.{SyncIdProvider, TarantoolConnection} +import zio.tarantool.protocol._ + +object TarantoolSpaceOperations { + type TarantoolSpaceOperations = Has[Service] + + trait Service { + def select(key: Value, query: SelectQuery): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def select[A: TupleEncoder]( + key: A, + query: SelectQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def insert(tuple: Value): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def insert[A: TupleEncoder](tuple: A): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def update(key: Value, updateQuery: UpdateQuery): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def update[A: TupleEncoder]( + key: A, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def delete(key: Value): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def delete[A: TupleEncoder](key: A): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def upsert( + tuple: Value, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def upsert[A: TupleEncoder]( + tuple: A, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def replace(tuple: Value): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + + def replace[A: TupleEncoder](tuple: A): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] + } + + private case class Live( + spaceMeta: SpaceMeta, + connection: TarantoolConnection.Service, + syncIdProvider: SyncIdProvider.Service + ) extends Service { + override def select( + key: Value, + query: SelectQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = + ??? + + override def select[A: TupleEncoder]( + key: A, + query: SelectQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def insert(tuple: Value): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def insert[A: TupleEncoder](tuple: A): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def update( + key: Value, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def update[A: TupleEncoder]( + key: A, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def delete(key: Value): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def delete[A: TupleEncoder](key: A): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def upsert( + tuple: Value, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def upsert[A: TupleEncoder]( + tuple: A, + updateQuery: UpdateQuery + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def replace(tuple: Value): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = ??? + + override def replace[A: TupleEncoder](tuple: A): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = + ??? + + private def send( + op: RequestCode, + body: Map[Long, Value] + ): IO[TarantoolError, Promise[TarantoolError, TarantoolResponse]] = + for { + syncId <- syncIdProvider.syncId() + request = TarantoolRequest(op, syncId, body) + response <- connection.sendRequest(request).map(_.response) + } yield response + } +} diff --git a/core/src/main/scala/zio/tarantool/api/UpdateOperation.scala b/core/src/main/scala/zio/tarantool/api/UpdateOperation.scala new file mode 100644 index 0000000..ed02bf7 --- /dev/null +++ b/core/src/main/scala/zio/tarantool/api/UpdateOperation.scala @@ -0,0 +1,200 @@ +package zio.tarantool.api + +import org.msgpack.value.Value +import zio.tarantool.codec.Encoder +import zio.tarantool.internal.schema._ +import zio.tarantool.protocol.OperatorCode + +sealed trait UpdateOperation { + def operator: OperatorCode + + // todo: Check field types + def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult + + def toQuery(): Value +} + +object UpdateOperation { + + final case class Add(fieldPosition: Int, value: Long) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Addition + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[Long].encode(value) + ) + ) + } + + final case class Subtract(fieldPosition: Int, value: Long) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Subtraction + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[Long].encode(value) + ) + ) + } + + final case class Or(fieldPosition: Int, value: Long) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Or + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[Long].encode(value) + ) + ) + } + + final case class And(fieldPosition: Int, value: Long) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.And + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[Long].encode(value) + ) + ) + } + + final case class Xor(fieldPosition: Int, value: Long) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Xor + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[Long].encode(value) + ) + ) + } + + final case class Splice(fieldPosition: Int, start: Int, length: Int, value: String) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Splice + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[Int].encode(start), + Encoder[Int].encode(length), + Encoder[String].encode(value) + ) + ) + } + + final case class Insert[A: Encoder](fieldPosition: Int, value: A) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Insertion + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[A].encode(value) + ) + ) + } + + final case class Delete(fieldPosition: Int) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Deletion + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition) + ) + ) + } + + final case class Set[A: Encoder](fieldPosition: Int, value: A) extends UpdateOperation { + override val operator: OperatorCode = OperatorCode.Assigment + + override def validate(spaceMeta: SpaceMeta): UpdateOperationValidationResult = + if (fieldPosition >= spaceMeta.fieldFormat.length) { + UpdateOperationValidationResult.Error(s"Space ${spaceMeta.spaceName} has no field at position $fieldPosition") + } else { + UpdateOperationValidationResult.Success + } + + override def toQuery(): Value = + Encoder[Vector[Value]].encode( + Vector( + Encoder[OperatorCode].encode(operator), + Encoder[Int].encode(fieldPosition), + Encoder[A].encode(value) + ) + ) + } + +} diff --git a/core/src/main/scala/zio/tarantool/api/UpdateOperationValidationResult.scala b/core/src/main/scala/zio/tarantool/api/UpdateOperationValidationResult.scala new file mode 100644 index 0000000..3050ecd --- /dev/null +++ b/core/src/main/scala/zio/tarantool/api/UpdateOperationValidationResult.scala @@ -0,0 +1,9 @@ +package zio.tarantool.api + +sealed trait UpdateOperationValidationResult + +object UpdateOperationValidationResult { + case object Success extends UpdateOperationValidationResult + + final case class Error(reason: String) extends UpdateOperationValidationResult +} diff --git a/core/src/main/scala/zio/tarantool/api/UpdateQuery.scala b/core/src/main/scala/zio/tarantool/api/UpdateQuery.scala new file mode 100644 index 0000000..86cf0e3 --- /dev/null +++ b/core/src/main/scala/zio/tarantool/api/UpdateQuery.scala @@ -0,0 +1,86 @@ +package zio.tarantool.api + +import org.msgpack.value.Value +import zio.tarantool.TarantoolError.UpdateOperationError +import zio.tarantool.codec.Encoder +import zio.tarantool.internal.schema.SpaceMeta + +import scala.collection.mutable + +final case class UpdateQuery(operations: Vector[UpdateOperation]) { + def encode(spaceMeta: SpaceMeta): Either[Vector[UpdateOperationError], Value] = { + val (values, errors) = operations.foldLeft((Vector.empty[Value], Vector.empty[UpdateOperationError])) { + case ((values, errors), operation) => + operation.validate(spaceMeta) match { + case UpdateOperationValidationResult.Success => + (values.appended(operation.toQuery()), errors) + case UpdateOperationValidationResult.Error(reason) => + (values, errors.appended(UpdateOperationError(reason))) + } + } + + if (errors.nonEmpty) { + Left(errors) + } else { + Right(Encoder[Vector[Value]].encode(values)) + } + } +} + +object UpdateQuery { + val default: UpdateQuery = UpdateQuery(Vector.empty) + + def builder(): UpdateQueryBuilder = new UpdateQueryBuilder() + + final class UpdateQueryBuilder() { + private val operations = mutable.ArrayBuffer.empty[UpdateOperation] + + def add(fieldPosition: Int, value: Long): this.type = { + operations.append(UpdateOperation.Add(fieldPosition, value)) + this + } + + def subtract(fieldPosition: Int, value: Long): this.type = { + operations.append(UpdateOperation.Subtract(fieldPosition, value)) + this + } + + def or(fieldPosition: Int, value: Long): this.type = { + operations.append(UpdateOperation.Or(fieldPosition, value)) + this + } + + def and(fieldPosition: Int, value: Long): this.type = { + operations.append(UpdateOperation.And(fieldPosition, value)) + this + } + + def xor(fieldPosition: Int, value: Long): this.type = { + operations.append(UpdateOperation.Xor(fieldPosition, value)) + this + } + + def splice(fieldPosition: Int, start: Int, length: Int, value: String): this.type = { + operations.append(UpdateOperation.Splice(fieldPosition, start, length, value)) + this + } + + def insert[A: Encoder](fieldPosition: Int, value: A): this.type = { + operations.append(UpdateOperation.Insert(fieldPosition, value)) + this + } + + def delete(fieldPosition: Int): this.type = { + operations.append(UpdateOperation.Delete(fieldPosition)) + this + } + + def set[A: Encoder](fieldPosition: Int, value: A): this.type = { + operations.append(UpdateOperation.Set(fieldPosition, value)) + this + } + + def build(): UpdateQuery = UpdateQuery(operations.toVector) + + } +} diff --git a/core/src/main/scala/zio/tarantool/codec/TupleOps.scala b/core/src/main/scala/zio/tarantool/codec/TupleOps.scala deleted file mode 100644 index 5667399..0000000 --- a/core/src/main/scala/zio/tarantool/codec/TupleOps.scala +++ /dev/null @@ -1,204 +0,0 @@ -package zio.tarantool.codec - -import org.msgpack.value.Value -import zio.tarantool.TarantoolError -import zio.tarantool.TarantoolError.NotSupportedUpdateOperation -import zio.tarantool.protocol.FieldUpdate.{SimpleFieldUpdate, SpliceFieldUpdate} -import zio.tarantool.protocol.{FieldUpdate, OperatorCode} - -sealed trait TupleOps[A] { - def encoder: Encoder[A] - - // todo: Try ... Catch ? - def plus(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def minus(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def or(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def and(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def xor(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def splice( - position: Int, - start: Int, - length: Int, - value: A - ): Either[TarantoolError, FieldUpdate] = - Right(SpliceFieldUpdate(position, start, length, OperatorCode.Splice, encoder.encode(value))) - - def insert(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def delete(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) - - def assign(position: Int, value: A): Either[TarantoolError, FieldUpdate] = - Right(SimpleFieldUpdate(position, OperatorCode.Assigment, encoder.encode(value))) -} - -object TupleOps { - def apply[A](implicit ops: TupleOps[A]): TupleOps[A] = ops - - implicit val messagePackTupleOps: TupleOps[Value] = new TupleOps[Value] { - override val encoder: Encoder[Value] = Encoder[Value] - } - implicit val byteTupleOps: TupleOps[Byte] = new TupleOps[Byte] { - override val encoder: Encoder[Byte] = Encoder[Byte] - override def splice( - position: Int, - start: Int, - length: Int, - value: Byte - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val shortTupleOps: TupleOps[Short] = new TupleOps[Short] { - override val encoder: Encoder[Short] = Encoder[Short] - override def splice( - position: Int, - start: Int, - length: Int, - value: Short - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val intTupleOps: TupleOps[Int] = new TupleOps[Int] { - override val encoder: Encoder[Int] = Encoder[Int] - override def splice( - position: Int, - start: Int, - length: Int, - value: Int - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val longTupleOps: TupleOps[Long] = new TupleOps[Long] { - override val encoder: Encoder[Long] = Encoder[Long] - override def splice( - position: Int, - start: Int, - length: Int, - value: Long - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val floatTupleOps: TupleOps[Float] = new TupleOps[Float] { - override val encoder: Encoder[Float] = Encoder[Float] - override def splice( - position: Int, - start: Int, - length: Int, - value: Float - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val doubleTupleOps: TupleOps[Double] = new TupleOps[Double] { - override val encoder: Encoder[Double] = Encoder[Double] - override def splice( - position: Int, - start: Int, - length: Int, - value: Double - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val booleanTupleOps: TupleOps[Boolean] = new TupleOps[Boolean] { - override val encoder: Encoder[Boolean] = Encoder[Boolean] - override def splice( - position: Int, - start: Int, - length: Int, - value: Boolean - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - - implicit val characterTupleOps: TupleOps[Char] = new TupleOps[Char] { - override val encoder: Encoder[Char] = Encoder[Char] - override def splice( - position: Int, - start: Int, - length: Int, - value: Char - ): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - implicit val stringTupleOps: TupleOps[String] = new TupleOps[String] { - - override val encoder: Encoder[String] = Encoder[String] - - override def plus(position: Int, value: String): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def minus(position: Int, value: String): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def or(position: Int, value: String): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def and(position: Int, value: String): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def xor(position: Int, value: String): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - } - - implicit def vectorTupleOps[A: Encoder]: TupleOps[Vector[A]] = new TupleOps[Vector[A]] { - override val encoder: Encoder[Vector[A]] = Encoder[Vector[A]] - - override def plus(position: Int, value: Vector[A]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def minus(position: Int, value: Vector[A]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def or(position: Int, value: Vector[A]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def and(position: Int, value: Vector[A]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def xor(position: Int, value: Vector[A]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def splice( - position: Int, - start: Int, - length: Int, - value: Vector[A] - ): Either[TarantoolError, FieldUpdate] = Left(NotSupportedUpdateOperation("Not supported")) - } - - implicit def mapTupleOps[A: Encoder, B: Encoder]: TupleOps[Map[A, B]] = new TupleOps[Map[A, B]] { - override val encoder: Encoder[Map[A, B]] = Encoder[Map[A, B]] - - override def plus(position: Int, value: Map[A, B]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def minus(position: Int, value: Map[A, B]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def or(position: Int, value: Map[A, B]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def and(position: Int, value: Map[A, B]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def xor(position: Int, value: Map[A, B]): Either[TarantoolError, FieldUpdate] = - Left(NotSupportedUpdateOperation("Not supported")) - - override def splice( - position: Int, - start: Int, - length: Int, - value: Map[A, B] - ): Either[TarantoolError, FieldUpdate] = Left(NotSupportedUpdateOperation("Not supported")) - } -} diff --git a/core/src/main/scala/zio/tarantool/codec/TupleOpsBuilder.scala b/core/src/main/scala/zio/tarantool/codec/TupleOpsBuilder.scala deleted file mode 100644 index ab476c3..0000000 --- a/core/src/main/scala/zio/tarantool/codec/TupleOpsBuilder.scala +++ /dev/null @@ -1,93 +0,0 @@ -package zio.tarantool.codec - -import shapeless._ -import shapeless.ops.hlist.ToTraversable -import shapeless.ops.record.Keys -import zio.IO -import zio.tarantool.TarantoolError -import zio.tarantool.TarantoolError.CodecError -import zio.tarantool.protocol.{FieldUpdate, UpdateOperations} -import zio.tarantool.codec.TupleOpsBuilder.FieldMeta - -import scala.collection.mutable - -final class TupleOpsBuilder[C] private (fields: Map[String, FieldMeta]) { - private val buffer = mutable.ListBuffer[Either[TarantoolError, FieldUpdate]]() - - def plus[A](field: String, value: A)(implicit ops: TupleOps[A]): this.type = - applyOperation(field, value, (meta, a) => ops.plus(meta.position, a)) - - def minus[A](field: String, value: A)(implicit ops: TupleOps[A]): this.type = - applyOperation(field, value, (meta, a) => ops.minus(meta.position, a)) - - def or[A](field: String, value: A)(implicit ops: TupleOps[A]): this.type = - applyOperation(field, value, (meta, a) => ops.or(meta.position, a)) - - def and[A](field: String, value: A)(implicit ops: TupleOps[A]): this.type = - applyOperation(field, value, (meta, a) => ops.and(meta.position, a)) - - def xor[A](field: String, value: A)(implicit ops: TupleOps[A]): this.type = - applyOperation(field, value, (meta, a) => ops.xor(meta.position, a)) - - def splice[A](field: String, start: Int, length: Int, replacement: A)(implicit - ops: TupleOps[A] - ): this.type = - applyOperation(field, replacement, (meta, a) => ops.splice(meta.position, start, length, a)) - - def assign[A](field: String, value: A)(implicit ops: TupleOps[A]): this.type = - applyOperation(field, value, (meta, a) => ops.assign(meta.position, a)) - - def build(): Either[TarantoolError, UpdateOperations] = { - val attempt: Either[TarantoolError, Vector[FieldUpdate]] = { - val empty: Either[TarantoolError, Vector[FieldUpdate]] = - Right[TarantoolError, Vector[FieldUpdate]](Vector.empty[FieldUpdate]) - - buffer.foldLeft(empty) { case (acc, el) => - acc.flatMap(a => el.map(a :+ _)) - } - } - - attempt.map(ops => UpdateOperations(ops)) - } - - def buildM(): IO[CodecError, UpdateOperations] = - IO.fromEither(build()).mapError(err => CodecError(err)) - - def reset(): Unit = buffer.clear() - - private def applyOperation[A]( - field: String, - value: A, - f: (FieldMeta, A) => Either[TarantoolError, FieldUpdate] - ): this.type = { - val result: Either[TarantoolError, FieldUpdate] = fields.get(field) match { - case Some(meta) => - f(meta, value).left.map(err => - TarantoolError.NotSupportedUpdateOperation(s"$field: ${err.getLocalizedMessage}") - ) - case None => Left(TarantoolError.NotSupportedUpdateOperation(s"Field $field does not exist")) - } - - buffer += result - this - } -} - -object TupleOpsBuilder { - private[tarantool] case class FieldMeta(position: Int) - - def apply[A](implicit builder: TupleOpsBuilder[A]): TupleOpsBuilder[A] = builder - - implicit def newBuilder[A, ARepr <: HList, KeysRepr <: HList](implicit - gen: LabelledGeneric.Aux[A, ARepr], - keys: Keys.Aux[ARepr, KeysRepr], - keysToTraversable: ToTraversable.Aux[KeysRepr, List, Symbol] - ): TupleOpsBuilder[A] = { - val fieldMetas: Map[String, FieldMeta] = - keys().toList.zipWithIndex.map { case (symbol, i) => - symbol.name -> FieldMeta(i) - }.toMap - - new TupleOpsBuilder[A](fieldMetas) - } -} diff --git a/core/src/main/scala/zio/tarantool/internal/schema/SchemaEncoder.scala b/core/src/main/scala/zio/tarantool/internal/schema/SchemaEncoder.scala index 372e101..b5d8783 100644 --- a/core/src/main/scala/zio/tarantool/internal/schema/SchemaEncoder.scala +++ b/core/src/main/scala/zio/tarantool/internal/schema/SchemaEncoder.scala @@ -61,7 +61,7 @@ private[tarantool] object SchemaEncoder { } } - private def decodeFieldMeta(vector: Vector[Map[String, Value]]): List[FieldMeta] = + private def decodeFieldMeta(vector: Vector[Map[String, Value]]): Vector[FieldMeta] = vector.map { map => val fieldNameMp = Encoder[String].decode(map("name")) val fieldTypeMp = Encoder[String].decode(map("type")) @@ -73,7 +73,7 @@ private[tarantool] object SchemaEncoder { fieldTypeMp, isNullableMp ) - }.foldLeft(Vector.empty[FieldMeta])((acc, value) => acc :+ value).toList + }.foldLeft(Vector.empty[FieldMeta])((acc, value) => acc :+ value) private def decodeIndexOptions(map: Map[String, Value]): IndexOptions = if (map.isEmpty) IndexOptions(isUnique = false) diff --git a/core/src/main/scala/zio/tarantool/internal/schema/SpaceMeta.scala b/core/src/main/scala/zio/tarantool/internal/schema/SpaceMeta.scala index 107ad01..148bd4e 100644 --- a/core/src/main/scala/zio/tarantool/internal/schema/SpaceMeta.scala +++ b/core/src/main/scala/zio/tarantool/internal/schema/SpaceMeta.scala @@ -5,7 +5,7 @@ private[tarantool] final case class SpaceMeta( spaceName: String, engine: String, spaceOptions: SpaceOptions, - fieldFormat: List[FieldMeta], + fieldFormat: Vector[FieldMeta], indexes: Map[String, IndexMeta] ) { def withIndexes(indexes: Map[String, IndexMeta]): SpaceMeta = this.copy(indexes = indexes) @@ -17,6 +17,6 @@ private[tarantool] object SpaceMeta { spaceName: String, engine: String, spaceOptions: SpaceOptions, - fieldFormat: List[FieldMeta] + fieldFormat: Vector[FieldMeta] ): SpaceMeta = new SpaceMeta(spaceId, spaceName, engine, spaceOptions, fieldFormat, Map.empty) } diff --git a/core/src/main/scala/zio/tarantool/protocol/FieldUpdate.scala b/core/src/main/scala/zio/tarantool/protocol/FieldUpdate.scala deleted file mode 100644 index eaba84d..0000000 --- a/core/src/main/scala/zio/tarantool/protocol/FieldUpdate.scala +++ /dev/null @@ -1,18 +0,0 @@ -package zio.tarantool.protocol - -import org.msgpack.value.Value - -sealed trait FieldUpdate - -object FieldUpdate { - final case class SimpleFieldUpdate(position: Int, operatorCode: OperatorCode, value: Value) extends FieldUpdate - - final case class SpliceFieldUpdate( - position: Int, - start: Int, - length: Int, - operatorCode: OperatorCode, - replacement: Value - ) extends FieldUpdate - -} diff --git a/core/src/main/scala/zio/tarantool/protocol/TarantoolRequestBody.scala b/core/src/main/scala/zio/tarantool/protocol/TarantoolRequestBody.scala index 376d0fc..1ed8435 100644 --- a/core/src/main/scala/zio/tarantool/protocol/TarantoolRequestBody.scala +++ b/core/src/main/scala/zio/tarantool/protocol/TarantoolRequestBody.scala @@ -5,17 +5,17 @@ import zio.tarantool.codec.Encoder object TarantoolRequestBody { def selectBody( - spaceId: Int, - indexId: Int, - limit: Int, - offset: Int, + spaceId: Long, + indexId: Long, + limit: Long, + offset: Long, iterator: IteratorCode, key: Value ): Map[Long, Value] = Map( - RequestBodyKey.Space.value -> Encoder[Int].encode(spaceId), - RequestBodyKey.Index.value -> Encoder[Int].encode(indexId), - RequestBodyKey.Limit.value -> Encoder[Int].encode(limit), - RequestBodyKey.Offset.value -> Encoder[Int].encode(offset), + RequestBodyKey.Space.value -> Encoder[Long].encode(spaceId), + RequestBodyKey.Index.value -> Encoder[Long].encode(indexId), + RequestBodyKey.Limit.value -> Encoder[Long].encode(limit), + RequestBodyKey.Offset.value -> Encoder[Long].encode(offset), RequestBodyKey.Iterator.value -> Encoder[Int].encode(iterator.value), RequestBodyKey.Key.value -> key ) @@ -26,13 +26,13 @@ object TarantoolRequestBody { ) def updateBody( - spaceId: Int, - indexId: Int, + spaceId: Long, + indexId: Long, key: Value, tuple: Value ): Map[Long, Value] = Map( - RequestBodyKey.Space.value -> Encoder[Int].encode(spaceId), - RequestBodyKey.Index.value -> Encoder[Int].encode(indexId), + RequestBodyKey.Space.value -> Encoder[Long].encode(spaceId), + RequestBodyKey.Index.value -> Encoder[Long].encode(indexId), RequestBodyKey.Key.value -> key, RequestBodyKey.Tuple.value -> tuple ) @@ -56,11 +56,11 @@ object TarantoolRequestBody { ) def replaceBody( - spaceId: Int, + spaceId: Long, tuple: Value ): Map[Long, Value] = Map( - RequestBodyKey.Space.value -> Encoder[Int].encode(spaceId), + RequestBodyKey.Space.value -> Encoder[Long].encode(spaceId), RequestBodyKey.Tuple.value -> tuple ) diff --git a/core/src/main/scala/zio/tarantool/protocol/UpdateOperations.scala b/core/src/main/scala/zio/tarantool/protocol/UpdateOperations.scala deleted file mode 100644 index 9fa3827..0000000 --- a/core/src/main/scala/zio/tarantool/protocol/UpdateOperations.scala +++ /dev/null @@ -1,52 +0,0 @@ -package zio.tarantool.protocol - -import org.msgpack.value.{ArrayValue, Value} -import zio.tarantool.codec.{Encoder, TupleEncoder} - -final case class UpdateOperations(ops: Vector[FieldUpdate]) - -object UpdateOperations { - implicit val updateOperationsTupleEncoder: TupleEncoder[UpdateOperations] = - new TupleEncoder[UpdateOperations] { - override def encode(v: UpdateOperations): Vector[Value] = { - val encodedOps: Vector[Value] = - v.ops.foldLeft(Vector.empty[Value]) { (state, ops) => - val opsEncoded: Value = ops match { - case FieldUpdate.SimpleFieldUpdate(position, operatorCode, value) => - val positionEncoded = Encoder[Int].encode(position) - val operationEncoded = Encoder[OperatorCode].encode(operatorCode) - - Encoder[Vector[Value]].encode(Vector(operationEncoded, positionEncoded, value)) - case FieldUpdate.SpliceFieldUpdate( - position, - start, - length, - operatorCode, - replacement - ) => - val positionEncoded = Encoder[Int].encode(position) - val startEncoded = Encoder[Int].encode(start) - val lengthEncoded = Encoder[Int].encode(length) - val operationEncoded = Encoder[OperatorCode].encode(operatorCode) - - Encoder[Vector[Value]].encode( - Vector( - operationEncoded, - positionEncoded, - startEncoded, - lengthEncoded, - replacement - ) - ) - } - - state :+ opsEncoded - } - - encodedOps - } - - override def decode(v: ArrayValue, idx: Int): UpdateOperations = - throw new UnsupportedOperationException("UpdateOperations decoding is not supported") - } -}