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

io.udash.rest.RestDataCompanion.scala Maven / Gradle / Ivy

The newest version!
package io.udash
package rest

import com.avsystem.commons.meta.MacroInstances
import com.avsystem.commons.misc.ValueOf
import com.avsystem.commons.rpc.{AsRaw, AsReal}
import com.avsystem.commons.serialization.{GenCodec, TransparentWrapperCompanion}
import io.udash.rest.openapi.RestStructure.NameAndAdjusters
import io.udash.rest.openapi._
import io.udash.rest.raw.{HttpBody, JsonValue, PlainValue, RestResponse}

trait CodecWithStructure[T] {
  def codec: GenCodec[T]
  def structure: RestStructure[T]
}

/**
  * Base class for companion objects of ADTs (case classes, objects, sealed hierarchies) which are used as
  * parameter or result types in REST API traits. Automatically provides instances of
  * `GenCodec` and [[io.udash.rest.openapi.RestSchema RestSchema]].
  *
  * @example
  * {{{
  *   case class User(id: String, name: String, birthYear: Int)
  *   object User extends RestDataCompanion[User]
  * }}}
  */
abstract class RestDataCompanion[T](implicit
  instances: MacroInstances[DefaultRestImplicits, CodecWithStructure[T]]
) extends {
  implicit lazy val codec: GenCodec[T] = instances(DefaultRestImplicits, this).codec
  implicit lazy val restStructure: RestStructure[T] = instances(DefaultRestImplicits, this).structure
  implicit lazy val restSchema: RestSchema[T] = RestSchema.lazySchema(restStructure.standaloneSchema)
}

/**
  * A version of [[RestDataCompanion]] which injects additional implicits into macro materialization.
  * Implicits are imported from an object specified with type parameter `D`.
  * It must be a singleton object type, i.e. `SomeObject.type`.
  */
abstract class RestDataCompanionWithDeps[D, T](implicit
  deps: ValueOf[D], instances: MacroInstances[(DefaultRestImplicits, D), CodecWithStructure[T]]
) extends {
  implicit lazy val codec: GenCodec[T] = instances((DefaultRestImplicits, deps.value), this).codec
  implicit lazy val restStructure: RestStructure[T] = instances((DefaultRestImplicits, deps.value), this).structure
  implicit lazy val restSchema: RestSchema[T] = RestSchema.lazySchema(restStructure.standaloneSchema)
}

/**
  * Base class for companion objects of wrappers over other data types (i.e. case classes with single field).
  * This companion ensures instances of all the REST typeclasses (serialization, schema, etc.) for wrapping type
  * assuming that these instances are available for the wrapped type.
  *
  * Using this base companion class makes the wrapper class effectively "transparent", i.e. as if it was annotated with
  * [[com.avsystem.commons.serialization.transparent transparent]] annotation.
  *
  * @example
  * {{{
  *   case class UserId(id: String) extends AnyVal
  *   object UserId extends RestDataWrapperCompanion[String, UserId]
  * }}}
  */
abstract class RestDataWrapperCompanion[Wrapped, T](implicit
  instances: MacroInstances[DefaultRestImplicits, () => NameAndAdjusters[T]]
) extends TransparentWrapperCompanion[Wrapped, T] {
  private def nameAndAdjusters: NameAndAdjusters[T] = instances(DefaultRestImplicits, this).apply()

  // These implicits must be specialized for every raw type (PlainValue, JsonValue, etc.) because
  // it lifts their priority. Unfortunately, controlling implicit priority is not pretty.
  // Also, it's probably good that we explicitly enable derivation only for REST-related raw types
  // and not for all raw types - this avoids possible interference with other features using RPC.

  implicit def plainAsRaw(implicit wrappedAsRaw: AsRaw[PlainValue, Wrapped]): AsRaw[PlainValue, T] =
    AsRaw.fromTransparentWrapping

  implicit def plainAsReal(implicit wrappedAsRaw: AsReal[PlainValue, Wrapped]): AsReal[PlainValue, T] =
    AsReal.fromTransparentWrapping

  implicit def jsonAsRaw(implicit wrappedAsRaw: AsRaw[JsonValue, Wrapped]): AsRaw[JsonValue, T] =
    AsRaw.fromTransparentWrapping

  implicit def jsonAsReal(implicit wrappedAsRaw: AsReal[JsonValue, Wrapped]): AsReal[JsonValue, T] =
    AsReal.fromTransparentWrapping

  implicit def bodyAsRaw(implicit wrappedAsRaw: AsRaw[HttpBody, Wrapped]): AsRaw[HttpBody, T] =
    AsRaw.fromTransparentWrapping

  implicit def bodyAsReal(implicit wrappedAsRaw: AsReal[HttpBody, Wrapped]): AsReal[HttpBody, T] =
    AsReal.fromTransparentWrapping

  implicit def responseAsRaw(implicit wrappedAsRaw: AsRaw[RestResponse, Wrapped]): AsRaw[RestResponse, T] =
    AsRaw.fromTransparentWrapping

  implicit def responseAsReal(implicit wrappedAsRaw: AsReal[RestResponse, Wrapped]): AsReal[RestResponse, T] =
    AsReal.fromTransparentWrapping

  implicit def restSchema(implicit wrappedSchema: RestSchema[Wrapped]): RestSchema[T] =
    nameAndAdjusters.restSchema(wrappedSchema)

  implicit def restMediaTypes(implicit wrappedMediaTypes: RestMediaTypes[Wrapped]): RestMediaTypes[T] =
    (resolver: SchemaResolver, schemaTransform: RestSchema[_] => RestSchema[_]) =>
      wrappedMediaTypes.mediaTypes(resolver, ws => schemaTransform(nameAndAdjusters.restSchema(ws)))

  implicit def restRequestBody(implicit wrappedBody: RestRequestBody[Wrapped]): RestRequestBody[T] =
    (resolver: SchemaResolver, schemaTransform: RestSchema[_] => RestSchema[_]) =>
      wrappedBody.requestBody(resolver, ws => schemaTransform(nameAndAdjusters.restSchema(ws)))

  implicit def restResponses(implicit wrappedResponses: RestResponses[Wrapped]): RestResponses[T] =
    (resolver: SchemaResolver, schemaTransform: RestSchema[_] => RestSchema[_]) =>
      wrappedResponses.responses(resolver, ws => schemaTransform(nameAndAdjusters.restSchema(ws)))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy