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

epus-client_native0.4_3.0.5.3.source-code.Transport.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.Functor
import cats.MonadError
import cats.effect.*
import cats.effect.std.Console
import com.comcast.ip4s.Host
import com.comcast.ip4s.SocketAddress
import fs2.Chunk
import fs2.Pipe
import fs2.Stream
import fs2.Stream.*
import fs2.interop.scodec.*
import fs2.io.net.Network
import fs2.io.net.Socket
import fs2.io.net.SocketOption
import lepus.protocol.*
import lepus.protocol.constants.ProtocolHeader
import lepus.wire.FrameCodec
import scodec.codecs.logFailuresToStdOut

import scala.{Console => SCon}

type Transport[F[_]] = Pipe[F, Frame, Frame]

object Transport {
  private[client] def decoderServer[F[_]](using
      F: MonadError[F, Throwable]
  ): Pipe[F, Byte, Frame] =
    StreamDecoder
      .once(logFailuresToStdOut(FrameCodec.protocol))
      .flatMap(pv => StreamDecoder.many(logFailuresToStdOut(FrameCodec.frame)))
      .toPipeByte

  private def decoderServer2[F[_]](using
      F: MonadError[F, Throwable]
  ): Pipe[F, Byte, Frame] =
    StreamDecoder.many(logFailuresToStdOut(FrameCodec.frame)).toPipeByte

  private[client] def decoder[F[_]](using
      F: MonadError[F, Throwable]
  ): Pipe[F, Byte, Frame] =
    StreamDecoder.many(logFailuresToStdOut(FrameCodec.frame)).toPipeByte

  private[client] def encoder[F[_]](using
      F: MonadError[F, Throwable]
  ): Pipe[F, Frame, Byte] =
    StreamEncoder.many(logFailuresToStdOut(FrameCodec.frame)).toPipeByte

  private val protocolHeader = chunk(Chunk.byteVector(ProtocolHeader))

  private[client] inline def debugSide[F[_]: Functor](
      send: Boolean
  )(using C: Console[F]): Pipe[F, Frame, Frame] =
    inline if send then
      _.evalTap(f => C.println(s"${SCon.CYAN}S> $f${SCon.RESET}"))
    else _.evalTap(f => C.println(s"${SCon.GREEN}C> $f${SCon.RESET}"))

  private[client] inline def debug[F[_]: Functor: Console](
      transport: Transport[F]
  ): Transport[F] =
    _.through(debugSide(false)).through(transport).through(debugSide(true))

  def build[F[_]: Concurrent](
      reads: Stream[F, Byte],
      writes: Pipe[F, Byte, Nothing]
  ): Transport[F] =
    toSend =>
      val recv = reads.through(decoderServer2)
      val send = (protocolHeader ++ toSend.through(encoder)).through(writes)
      recv mergeHaltBoth send

  def fromSocket[F[_]: Concurrent](socket: Socket[F]): Transport[F] =
    build(socket.reads, socket.writes)

  def fromSocket[F[_]: Concurrent](
      socket: Resource[F, Socket[F]]
  ): Transport[F] = in =>
    resource(socket).flatMap(s => build(s.reads, s.writes).apply(in))

  def connect[F[_]: Concurrent: Network](
      address: SocketAddress[Host],
      options: List[SocketOption] = List.empty
  ): Transport[F] = in =>
    resource(Network[F].client(address, options))
      .flatMap(fromSocket(_).apply(in))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy