diff --git a/src/main/scala/zio/nio/channels/SelectableChannel.scala b/src/main/scala/zio/nio/channels/SelectableChannel.scala index b1214a6a..e1027779 100644 --- a/src/main/scala/zio/nio/channels/SelectableChannel.scala +++ b/src/main/scala/zio/nio/channels/SelectableChannel.scala @@ -9,6 +9,7 @@ import java.nio.channels.{ SocketChannel => JSocketChannel } +import zio.nio.channels.SelectionKey.Operation import zio.nio.channels.spi.SelectorProvider import zio.nio.{ Buffer, SocketAddress, SocketOption } import zio.{ IO, UIO } @@ -20,8 +21,9 @@ trait SelectableChannel extends Channel { final val provider: UIO[SelectorProvider] = IO.effectTotal(new SelectorProvider(channel.provider())) - final val validOps: UIO[Int] = + final val validOps: UIO[Set[Operation]] = IO.effectTotal(channel.validOps()) + .map(Operation.fromInt(_)) final val isRegistered: UIO[Boolean] = IO.effectTotal(channel.isRegistered()) @@ -29,12 +31,21 @@ trait SelectableChannel extends Channel { final def keyFor(sel: Selector): UIO[Option[SelectionKey]] = IO.effectTotal(Option(channel.keyFor(sel.selector)).map(new SelectionKey(_))) - final def register(sel: Selector, ops: Int, att: Option[AnyRef]): IO[IOException, SelectionKey] = - IO.effect(new SelectionKey(channel.register(sel.selector, ops, att.orNull))) + final def register(sel: Selector, ops: Set[Operation], att: Option[AnyRef]): IO[IOException, SelectionKey] = + IO.effect(new SelectionKey(channel.register(sel.selector, Operation.toInt(ops), att.orNull))) .refineToOrDie[IOException] - final def register(sel: Selector, ops: Int): IO[IOException, SelectionKey] = - IO.effect(new SelectionKey(channel.register(sel.selector, ops))).refineToOrDie[IOException] + final def register(sel: Selector, ops: Set[Operation]): IO[IOException, SelectionKey] = + IO.effect(new SelectionKey(channel.register(sel.selector, Operation.toInt(ops)))) + .refineToOrDie[IOException] + + final def register(sel: Selector, op: Operation, att: Option[AnyRef]): IO[IOException, SelectionKey] = + IO.effect(new SelectionKey(channel.register(sel.selector, op.intVal, att.orNull))) + .refineToOrDie[IOException] + + final def register(sel: Selector, op: Operation): IO[IOException, SelectionKey] = + IO.effect(new SelectionKey(channel.register(sel.selector, op.intVal))) + .refineToOrDie[IOException] final def configureBlocking(block: Boolean): IO[IOException, Unit] = IO.effect(channel.configureBlocking(block)).unit.refineToOrDie[IOException] diff --git a/src/main/scala/zio/nio/channels/SelectionKey.scala b/src/main/scala/zio/nio/channels/SelectionKey.scala index 9a5f2165..ab7265ae 100644 --- a/src/main/scala/zio/nio/channels/SelectionKey.scala +++ b/src/main/scala/zio/nio/channels/SelectionKey.scala @@ -14,6 +14,23 @@ object SelectionKey { case e: CancelledKeyException => e } + sealed abstract class Operation(val intVal: Int) + + object Operation { + + final case object Read extends Operation(JSelectionKey.OP_READ) + final case object Write extends Operation(JSelectionKey.OP_WRITE) + final case object Connect extends Operation(JSelectionKey.OP_CONNECT) + final case object Accept extends Operation(JSelectionKey.OP_ACCEPT) + + final val fullSet: Set[Operation] = Set(Read, Write, Connect, Accept) + + final def fromInt(ops: Int): Set[Operation] = + fullSet.filter(op => (ops & op.intVal) != 0) + + final def toInt(set: Set[Operation]): Int = + set.foldLeft(0)((ops, op) => ops | op.intVal) + } } class SelectionKey(private[nio] val selectionKey: JSelectionKey) { @@ -32,16 +49,20 @@ class SelectionKey(private[nio] val selectionKey: JSelectionKey) { final val cancel: UIO[Unit] = IO.effectTotal(selectionKey.cancel()) - final val interestOps: IO[CancelledKeyException, Int] = - IO.effect(selectionKey.interestOps()).refineToOrDie[CancelledKeyException] + final val interestOps: IO[CancelledKeyException, Set[Operation]] = + IO.effectTotal(selectionKey.interestOps()) + .map(Operation.fromInt(_)) + .refineToOrDie[CancelledKeyException] - final def interestOps(ops: Int): IO[CancelledKeyException, SelectionKey] = - IO.effect(selectionKey.interestOps(ops)) - .map(new SelectionKey(_)) + final def interestOps(ops: Set[Operation]): IO[CancelledKeyException, Unit] = + IO.effect(selectionKey.interestOps(Operation.toInt(ops))) + .unit .refineToOrDie[CancelledKeyException] - final val readyOps: IO[CancelledKeyException, Int] = - IO.effect(selectionKey.readyOps()).refineToOrDie[CancelledKeyException] + final val readyOps: IO[CancelledKeyException, Set[Operation]] = + IO.effect(selectionKey.readyOps()) + .map(Operation.fromInt(_)) + .refineToOrDie[CancelledKeyException] final def isReadable: IO[CancelledKeyException, Boolean] = IO.effect(selectionKey.isReadable()).refineOrDie(JustCancelledKeyException) diff --git a/src/test/scala/zio/nio/channels/SelectorSuite.scala b/src/test/scala/zio/nio/channels/SelectorSuite.scala index 83f1ac82..f9262f8e 100644 --- a/src/test/scala/zio/nio/channels/SelectorSuite.scala +++ b/src/test/scala/zio/nio/channels/SelectorSuite.scala @@ -1,10 +1,11 @@ package zio.nio.channels -import java.nio.channels.{ CancelledKeyException, SelectionKey => JSelectionKey, SocketChannel => JSocketChannel } +import java.nio.channels.{ CancelledKeyException, SocketChannel => JSocketChannel } import testz.{ Harness, assert } import zio._ import zio.clock.Clock +import zio.nio.channels.SelectionKey.Operation import zio.nio.{ Buffer, SocketAddress } object SelectorSuite extends DefaultRuntime { @@ -36,7 +37,7 @@ object SelectorSuite extends DefaultRuntime { clientOpt <- channel.accept client = clientOpt.get _ <- client.configureBlocking(false) - _ <- client.register(selector, JSelectionKey.OP_READ) + _ <- client.register(selector, Operation.Read) } yield () } *> IO.whenM(safeStatusCheck(key.isReadable)) { @@ -62,7 +63,7 @@ object SelectorSuite extends DefaultRuntime { channel <- ServerSocketChannel.open _ <- channel.bind(address) _ <- channel.configureBlocking(false) - _ <- channel.register(selector, JSelectionKey.OP_ACCEPT) + _ <- channel.register(selector, Operation.Accept) buffer <- Buffer.byte(256) _ <- started.succeed(())