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

endpoints4s.algebra.circe.JsonEntitiesFromCodecs.scala Maven / Gradle / Ivy

The newest version!
package endpoints4s.algebra.circe

import cats.Show
import endpoints4s.{Codec, Decoder, Encoder, Invalid, Valid, Validated}
import io.circe.{
  DecodingFailure,
  Json,
  ParsingFailure,
  parser,
  Decoder => CirceDecoder,
  Encoder => CirceEncoder
}

/** Partial interpreter for [[endpoints4s.algebra.JsonEntitiesFromCodecs]] that only
  * fixes the `JsonCodec[A]` type to a [[CirceCodec]].
  *
  * The `jsonRequest` and `jsonResponse` operations have to be implemented by
  * a more specialized interpreter.
  *
  * Typical usage:
  *
  * {{{
  *   /* shared MyDto.scala */
  *
  *   case class MyDto(i: Int, s: String)
  *
  *   object MyDto {
  *     import io.circe.{Encoder, Decoder}
  *     import io.circe.generic.semiauto.{deriveEncoder, deriveDecoder}
  *
  *     implicit val encoder: Encoder[MyDto] = deriveEncoder
  *     implicit val decoder: Decoder[MyDto] = deriveDecoder
  *   }
  * }}}
  *
  * {{{
  *   /* shared endpoint definition */
  *
  *   trait MyEndpoints extends algebra.Endpoints with algebra.circe.JsonEntitiesFromCodec {
  *     val myEndpoint = endpoint(get(path), jsonResponse[MyDto])
  *   }
  * }}}
  *
  * {{{
  *   /* client MyEndpointsClient.scala */
  *
  *   object MyEndpointsClient extends MyEndpoints with xhr.JsonEntitiesFromCodec with xhr.faithful.Endpoints
  *
  *   MyEndpointsClient.myEndpoint().map(myDto => println(myDto.i))
  * }}}
  *
  * {{{
  *   /* server MyEndpointsServer.scala */
  *
  *   object MyEndpointsServer extends MyEndpoints with play.server.JsonEntitiesFromCodec {
  *
  *     val routes = routesFromEndpoints(
  *       myEndpoint.implementedBy(_ => MyDto(42, "foo"))
  *     )
  *
  *   }
  * }}}
  *
  * @group interpreters
  */
trait JsonEntitiesFromCodecs extends endpoints4s.algebra.JsonEntitiesFromCodecs {

//#type-carrier
  type JsonCodec[A] = CirceCodec[A]
//#type-carrier

  def stringCodec[A](implicit codec: CirceCodec[A]): Codec[String, A] =
    new Codec[String, A] {

      def decode(from: String): Validated[A] =
        parser
          .parse(from)
          .left
          .map(Show[ParsingFailure].show)
          .flatMap { json =>
            codec.decoder.decodeJson(json).left.map(Show[DecodingFailure].show)
          }
          .fold(Invalid(_), Valid(_))

      def encode(from: A): String =
        codec.encoder.apply(from).noSpaces

    }

  implicit def circeDecoderToDecoder[A](implicit
      decoder: CirceDecoder[A]
  ): Decoder[Json, A] =
    new Decoder[Json, A] {
      def decode(from: Json): Validated[A] =
        decoder
          .decodeJson(from)
          .fold(
            failure => Invalid(Show[DecodingFailure].show(failure)),
            Valid(_)
          )
    }

  implicit def circeEncoderToEncoder[A](implicit
      encoder: CirceEncoder[A]
  ): Encoder[A, Json] =
    new Encoder[A, Json] {
      def encode(from: A): Json = encoder(from)
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy