Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions core/src/main/scala/zio/tarantool/TarantoolError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
24 changes: 24 additions & 0 deletions core/src/main/scala/zio/tarantool/api/SelectQuery.scala
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package zio.tarantool.api

class TarantoolCallOperations {}
111 changes: 111 additions & 0 deletions core/src/main/scala/zio/tarantool/api/TarantoolSpaceOperations.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
200 changes: 200 additions & 0 deletions core/src/main/scala/zio/tarantool/api/UpdateOperation.scala
Original file line number Diff line number Diff line change
@@ -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)
)
)
}

}
Loading