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

epus-client_native0.4_3.0.5.3.source-code.Channel.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021 Hossein Naderi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package lepus.client

import cats.MonadError
import cats.effect.Concurrent
import cats.effect.kernel.Resource
import cats.effect.kernel.Resource.ExitCase.Canceled
import cats.effect.kernel.Resource.ExitCase.Errored
import cats.effect.kernel.Resource.ExitCase.Succeeded
import cats.implicits.*
import fs2.Stream
import fs2.concurrent.Signal
import lepus.client.Channel.Status
import lepus.client.apis.*
import lepus.client.internal.*
import lepus.protocol.*
import lepus.protocol.domains.*

trait Channel[F[_], M <: MessagingChannel] {
  def exchange: ExchangeAPI[F]
  def queue: QueueAPI[F]
  def messaging: M

  def status: Signal[F, Status]
}

object Channel {
  enum Status {
    case Active, InActive, Closed
  }
  private final class ChannelImpl[F[_], M <: MessagingChannel](
      transmitter: ChannelTransmitter[F],
      val messaging: M
  )(using MonadError[F, Throwable])
      extends Channel[F, M] {

    override def status: Signal[F, Status] = transmitter.status

    final def exchange: ExchangeAPI[F] = ExchangeAPIImpl(transmitter)
    final def queue: QueueAPI[F] = QueueAPIImpl(transmitter)
  }

  extension [F[_]](rpc: ChannelTransmitter[F]) {
    private[client] def call[M <: Method, O](m: M)(using
        d: RPCCallDef[F, M, O]
    ): F[O] =
      d.call(rpc)(m)
  }

  private abstract class ConsumingImpl[F[_]: Concurrent](
      channel: ChannelTransmitter[F]
  ) extends Consuming[F] {

    def qos(
        prefetchSize: Int,
        prefetchCount: Short,
        global: Boolean
    ): F[BasicClass.QosOk.type] = channel.call(
      BasicClass.Qos(
        prefetchSize = prefetchSize,
        prefetchCount = prefetchCount,
        global = global
      )
    )

    def consumeRaw(
        queue: QueueName,
        noLocal: NoLocal = false,
        noAck: NoAck = true,
        exclusive: Boolean = false,
        arguments: FieldTable = FieldTable.empty,
        ctag: Option[ConsumerTag]
    ): Stream[F, DeliveredMessageRaw] =
      import Stream.*
      resource(channel.delivered(ctag)).flatMap { case (ctag, data) =>
        val recv = eval(
          channel.call(
            BasicClass.Consume(
              queue,
              ctag,
              noLocal = noLocal,
              noAck = noAck,
              exclusive = exclusive,
              noWait = true,
              arguments
            )
          )
        ) >> data

        recv.onFinalize(
          channel.call(BasicClass.Cancel(ctag, true)).void
        )
      }

    def get(
        queue: QueueName,
        noAck: NoAck
    ): F[Option[SynchronousGetRaw]] =
      channel.get(BasicClass.Get(queue, noAck))

    def ack(deliveryTag: DeliveryTag, multiple: Boolean = false): F[Unit] =
      channel.call(BasicClass.Ack(deliveryTag, multiple))

    def reject(deliveryTag: DeliveryTag, requeue: Boolean = true): F[Unit] =
      channel.call(BasicClass.Reject(deliveryTag, requeue))

    def recoverAsync(requeue: Boolean): F[Unit] =
      channel.call(BasicClass.RecoverAsync(requeue))

    def recover(requeue: Boolean): F[Unit] =
      channel.call(BasicClass.Recover(requeue))

    def nack(
        deliveryTag: DeliveryTag,
        multiple: Boolean = false,
        requeue: Boolean = true
    ): F[Unit] =
      channel.call(
        BasicClass.Nack(deliveryTag, multiple = multiple, requeue = requeue)
      )

  }

  private class NormalPublishingImpl[F[_]: Concurrent](
      channel: ChannelTransmitter[F]
  ) extends ConsumingImpl[F](channel),
        NormalMessagingChannel[F] {

    override def returned: Stream[F, ReturnedMessageRaw] = channel.returned

    def publishRaw(env: EnvelopeRaw): F[Unit] =
      channel.publish(
        BasicClass
          .Publish(
            env.exchange,
            env.routingKey,
            mandatory = env.mandatory,
            immediate = false
          ),
        env.message
      )

  }

  private final class ReliablePublishingImpl[F[_]: Concurrent](
      channel: ChannelTransmitter[F],
      tagger: SequentialTagger[F]
  ) extends ConsumingImpl(channel),
        ReliablePublishingMessagingChannel[F] {
    def publishRaw(env: EnvelopeRaw): F[DeliveryTag] = tagger.next(
      channel
        .publish(
          BasicClass.Publish(
            env.exchange,
            env.routingKey,
            mandatory = env.mandatory,
            immediate = false
          ),
          env.message
        )
    )

    def confirmations: Stream[F, Confirmation] = channel.confirmed
    def returned: Stream[F, ReturnedMessageRaw] = channel.returned
  }

  private final class TransactionalMessagingImpl[F[_]: Concurrent](
      channel: ChannelTransmitter[F]
  ) extends NormalPublishingImpl(channel),
        TransactionalMessagingChannel[F] {
    private def trx = new Transaction[F] {
      def commit: F[Unit] = channel.call(TxClass.Commit).void
      def rollback: F[Unit] = channel.call(TxClass.Rollback).void
    }

    def transaction: Resource[F, Transaction[F]] = Resource
      .onFinalizeCase {
        case Succeeded  => trx.commit
        case Errored(e) => trx.rollback
        case Canceled   => trx.rollback
      }
      .as(trx)
  }

  private[client] def normal[F[_]: Concurrent](
      channel: ChannelTransmitter[F]
  ): Channel[F, NormalMessagingChannel[F]] =
    ChannelImpl(channel, NormalPublishingImpl(channel))

  private[client] def reliable[F[_]: Concurrent](
      channel: ChannelTransmitter[F]
  ): F[Channel[F, ReliablePublishingMessagingChannel[F]]] = for {
    _ <- channel.call(ConfirmClass.Select(true))
    tagger <- SequentialTagger[F]
  } yield ChannelImpl(channel, ReliablePublishingImpl(channel, tagger))

  private[client] def transactional[F[_]: Concurrent](
      channel: ChannelTransmitter[F]
  ): F[Channel[F, TransactionalMessagingChannel[F]]] =
    channel
      .call(TxClass.Select)
      .as(ChannelImpl(channel, TransactionalMessagingImpl(channel)))

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy