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

com.avsystem.commons.rpc.rpcAnnotations.scala Maven / Gradle / Ivy

There is a newer version: 2.22.0
Show newest version
package com.avsystem.commons
package rpc

import com.avsystem.commons.meta._

/**
  * You can use this annotation on overloaded RPC methods to give them unique identifiers for RPC serialization.
  * You can also subclass this annotation provided that you always override the `name` parameter with another
  * constructor parameter.
  */
class rpcName(val name: String) extends RealSymAnnotation

/**
  * You can use this annotation on real RPC methods to instruct macro engine to prepend method name (or [[rpcName]] if
  * specified) with given prefix. This annotation is mostly useful when aggregated by another annotation e.g.
  *
  * {{{
  *   sealed trait RestMethod extends RpcTag
  *   final class GET extends RestMethod with AnnotationAggregate {
  *     @rpcNamePrefix("GET_")
  *     final def aggregated: List[StaticAnnotation] = reifyAggregated
  *   }
  * }}}
  */
class rpcNamePrefix(val prefix: String, val overloadedOnly: Boolean = false) extends RealSymAnnotation

/**
  * Enables name-mangling of overloaded RPC methods. Each overloaded variant (except for the first one) will get
  * a suffix `_` appended, where `` is an index of the overload, starting from 1 for the first actual
  * overload. The first method is not considered an "overload" and will get no suffix appended.
  *
  * This allows overloading methods in RPC traits without manual disambiguation using [[rpcName]]
  * or [[rpcNamePrefix]]. However, such approach is much more prone to breaking API changes,
  * e.g. by reordering methods.
  *
  * This annotation must be applied on a raw method or method metadata param.
  */
class mangleOverloads extends RawMethodAnnotation

/**
  * Base trait for RPC tag annotations. Tagging gives more direct control over how real methods
  * and their parameters are matched against raw methods and their parameters.
  * For more information about method tagging, see documentation of [[methodTag]].
  * For more information about parameter tagging, see documentation of [[paramTag]].
  */
trait RpcTag extends RealSymAnnotation

/**
  * May be applied on raw method parameter of type `String` to indicate that macro generated implementation of
  * `AsReal` should pass real method's RPC name as this parameter and that macro generated implementation of
  * `AsRaw` should expect real method's RPC name to be passed there.
  *
  * Macro generation of `AsRaw` implementations require that raw methods annotated as
  * [[com.avsystem.commons.meta.multi multi]] must take at least
  * one raw parameter annotated as [[methodName]] (it may also be aggregated into some
  * [[com.avsystem.commons.meta.composite composite]] parameter).
  * This is necessary to properly identify which real method should be called.
  */
final class methodName extends RawParamAnnotation

/**
  * Can be used on metadata parameters to instruct macro materialization that some particular metadata depends
  * on some implicit typeclass instances for method's type parameters.
  *
  * Type of a parameter annotated with this annotation must be a function which takes an `AnyIterable[TypeClass[_]]`
  * as an argument and returns actual metadata class as a result.
  * `AnyIterable` is any class that extends `Iterable`, e.g. `List`.
  * `TypeClass` is any type constructor of kind (* -> *)
  */
final class forTypeParams extends RawParamAnnotation

/**
  * `@rpcMethodMetadata` applied on metadata parameter of RPC trait metadata class indicates that this parameter holds
  * metadata for RPC method(s) (one, some or all, depending on [[com.avsystem.commons.meta.SymbolArity SymbolArity]],
  * tagging, etc.).
  * */
final class rpcMethodMetadata extends MetadataParamStrategy

/**
  * `@rpcParamMetadata` applied on metadata parameter of RPC method metadata class indicates that this parameter holds
  * metadata for RPC parameter(s) (one, some or all, depending on [[com.avsystem.commons.meta.SymbolArity SymbolArity]]],
  * tagging, etc.).
  * */
final class rpcParamMetadata extends MetadataParamStrategy

/**
  * `@rpcTypeParamMetadata` applied on metadata parameter of RPC method metadata class indicates that this parameter holds
  * metadata for RPC type parameter(s) (one, some or all, depending on [[com.avsystem.commons.meta.SymbolArity SymbolArity]]],
  * tagging, etc.).
  * */
final class rpcTypeParamMetadata extends MetadataParamStrategy

/**
  * When applied on a real parameter, this parameter becomes an implicit dependency for encoders
  * (`AsRaw` and `AsReal` instances) of other parameters of the same method and for encoder of that method's result.
  *
  * This is primarily useful for generic (type-parameterized) real methods where an ad-hoc (per each call)
  * encoding must be provided for type parameters.
  *
  * NOTE: `@encodingDependency` parameters are serialized before other parameters, regardless of parameter
  * declaration order.
  */
final class encodingDependency extends RealSymAnnotation

/**
  * Base trait for [[verbatim]] and [[encoded]]. These annotations can be applied either on a raw method or
  * raw parameter in order to specify how matching real method results or matching real parameter values are encoded
  * as raw values.
  * Currently there are two possible cases: [[verbatim]] (no encoding) and [[encoded]] (encoding using `AsRaw` and
  * `AsReal` typeclasses). By default, method return values and [[com.avsystem.commons.meta.multi multi]]
  * parameters are [[encoded]] while [[com.avsystem.commons.meta.single single]] and
  * [[com.avsystem.commons.meta.optional optional]] parameters are [[verbatim]].
  * See documentation of [[verbatim]] and [[encoded]] for more details.
  */
sealed trait RpcEncoding extends RawMethodAnnotation with RawParamAnnotation

/**
  * When a raw parameter is annotated as [[encoded]], macro generated code will translate
  * between real parameter values and raw parameter values using implicit instances of `AsRaw[Raw,Real]`
  * and `AsReal[Raw,Real]` typeclasses. This annotation may also be applied on a method, but this would be
  * redundant since method results are encoded by default.
  *
  * Here's an example of raw RPC definition supposed to handle asynchronous (`Future`-based) calls that uses
  * `GenCodec` in order to encode and decode arguments and results as JSON strings.
  * It introduces its own wrapper class for JSON strings that has appropriate implicit instances of
  * `AsRaw` and `AsReal` (or `AsRawReal` which serves as both `AsReal` and `AsRaw`).
  *
  * {{{
  * import com.avsystem.commons._
  * import com.avsystem.commons.rpc._
  * import com.avsystem.commons.serialization._
  * import com.avsystem.commons.serialization.json._
  *
  * case class Json(jsonStr: String)
  * object Json {
  *   private def readJson[T: GenCodec](json: Json): T =
  *     JsonStringInput.read[T](json.jsonStr)
  *
  *   private def writeJson[T: GenCodec](value: T): Json =
  *     Json(JsonStringOutput.write[T](value))
  *
  *   implicit def genCodecBasedJsonEncoding[T: GenCodec]: AsRawReal[T,Json] =
  *     AsRawReal.create[Json,T](writeJson[T], readJson[T])
  *
  *   // instead of using `mapNow`, this method can also take implicit ExecutionContext and just use `map`
  *   implicit def genCodecBasedFutureJsonEncoding[T: GenCodec]: AsRawReal[Future[Json],Future[T]] =
  *     AsRawReal.create[Future[Json],Future[T]](_.mapNow(writeJson[T]), _.mapNow(readJson[T]))
  * }
  *
  * trait AsyncRawRpc {
  *   def call(@methodName rpcName: String, @multi args: Map[String,Json]): Future[Json]
  * }
  * }}}
  *
  * If you don't want to introduce the wrapper `Json` class and use more raw type, e.g. plain `String` then you
  * can also do it by moving implicit instances of `AsReal` and `AsRaw` (or the joined `AsRawReal`) into the
  * `implicits` object of raw RPC companion:
  *
  * {{{
  * trait AsyncRawRpc {
  *   def call(@methodName rpcName: String, @multi args: Map[String,String]): Future[String]
  * }
  * object AsyncRawRpc extends RawRpcCompanion[AsyncRawRpc] {
  *   private def readJson[T: GenCodec](json: String): T =
  *     JsonStringInput.read[T](json)
  *   private def writeJson[T: GenCodec](value: T): String =
  *     JsonStringOutput.write[T](value)
  *
  *   override object implicits {
  *     implicit def genCodecBasedJsonEncoding[T: GenCodec]: AsRawReal[String,T] =
  *       AsRawReal.create[String,T](writeJson[T], readJson[T])
  *     implicit def genCodecBasedFutureJsonEncoding[T: GenCodec]: AsRawReal[Future[String],Future[T]] =
  *       AsRawReal.create[Future[String],Future[T]](_.mapNow(writeJson[T]), _.mapNow(readJson[T]))
  *   }
  * }
  * }}}
  */
final class encoded extends RpcEncoding

/**
  * Turns off raw value encoding as specified by [[encoded]]. By default, [[com.avsystem.commons.meta.single single]]
  * and [[com.avsystem.commons.meta.optional optional]] raw parameters are already [[verbatim]], so using [[verbatim]]
  * only makes sense on [[com.avsystem.commons.meta.multi multi]] raw parameters or raw methods themselves,
  * which means turning off encoding of method's result.
  *
  * When encoding is turned off, raw and real types must be exactly the same types. For example, the following raw RPC
  * definition will match only raw RPC traits whose methods take `Int`s as parameters and return `Double`s as values:
  *
  * {{{
  * trait VerbatimRawRpc {
  *   @verbatim def call(@methodName rpcName: String, @multi @verbatim args: Map[String,Int]): Double
  * }
  * }}}
  */
final class verbatim extends RpcEncoding

/**
  * Base trait for annotations which - when applied on a real parameter or method - change the way its value is
  * serialized. The `Raw` type parameter must match the original raw type into which the value is serialized.
  * The annotation may then specify a `NewRaw` type. Macro engine will then look for a different implicit for
  * serialization - instead of looking for `AsRaw[Raw,Real]` it will now look for `AsRaw[NewRaw,Real]`.
  * The annotation must therefore provide a conversion from its new raw type to the original raw type.
  *
  * `NewRaw` may also be the same type as `Raw` - this is useful if we only want to modify the raw value after
  * serialization without changing its type.
  */
trait EncodingInterceptor[NewRaw, Raw] extends RealSymAnnotation {
  def toOriginalRaw(newRaw: NewRaw): Raw
}

/**
  * Base trait for annotations which - when applied on a real parameter or method - change the way its value is
  * deserialized. The `Raw` type parameter must match the original raw type into which the value is serialized.
  * The annotation may then specify a `NewRaw` type. Macro engine will then look for a different implicit for
  * deserialization - instead of looking for `AsReal[Raw,Real]` it will now look for `AsReal[NewRaw,Real]`.
  * The annotation must therefore provide a conversion from the original raw type to the new raw type.
  *
  * `NewRaw` may also be the same type as `Raw` - this is useful if we only want to modify the raw value before
  * deserialization without changing its type.
  */
trait DecodingInterceptor[NewRaw, Raw] extends RealSymAnnotation {
  def toNewRaw(raw: Raw): NewRaw
}

class rawWhenAbsent[+Raw](val value: Raw) extends RealSymAnnotation

/**
  * When raw method is annotated as `@tried`, invocations of real methods matching that raw method will be
  * automatically wrapped into `Try`. Consequently, all real methods will be treated as if their result
  * type was `Try[Result]` instead of actual `Result`. For example, if raw method is [[encoded]] and its
  * (raw) result is `Raw` then macro engine will search for implicit `AsRaw/Real[Raw,Try[Result]]` instead of just
  * `AsRaw/Real[Raw,Result]`
  */
final class tried extends RawMethodAnnotation

/**
  * When applied on raw RPC method or method metadata parameter, customizes error message displayed
  * for unmatched real method.
  *
  * When applied on raw RPC parameter or param metadata parameter, customizes error message displayed
  * when no real parameter matched annotated raw parameter. This implies that the raw parameter must have
  * `single` arity (otherwise it's not required to be matched by any real parameter).
  */
final class unmatched(error: String) extends RawSymAnnotation

/**
  * Can be applied on raw RPC method or method metadata parameter to customize compilation error message for
  * unmatched real parameters tagged as `Tag`.
  */
final class unmatchedParam[Tag <: RpcTag](error: String) extends RawMethodAnnotation

/**
  * Method tagging lets you have more explicit control over which raw methods can match which real methods.
  * Example:
  *
  * {{{
  * sealed trait MethodType extends RpcTag
  * class GET extends RestMethod
  * class POST extends RestMethod
  *
  * @methodTag[MethodType](new GET)
  * trait ExampleRawRpc {
  *   @tagged[GET] def get(@methodName name: String, @multi args: Map[String,Json]): Future[Json]
  *   @tagged[POST] def post(@methodName name: String, @multi args: Map[String,Json]): Future[Json]
  * }
  * }}}
  *
  * In the example above, we created a hierarchy of annotations rooted at `MethodType` which can be used
  * on real methods in order to explicitly tell the RPC macro which raw methods can match it.
  * We also specify `new GET` as the default tag that will be assumed for real methods without any tag annotation.
  * Then, using [[tagged]] we specify that the raw `get` method may only match real methods annotated as `GET`
  * while `post` raw method may only match real methods annotated as `POST`.
  * Raw methods not annotated with [[tagged]] have no limitations and may still match any real methods.
  *
  * Also, instead of specifying `defaultTag` in `@methodTag` annotation, you may provide the `whenUntagged`
  * parameter to [[tagged]] annotation. Raw method annotated as `@tagged[MethodType](whenUntagged = new GET)`
  * will match real methods either explicitly tagged with `GET` or untagged. If untagged, `new GET` will be assumed
  * as the tag. This is useful when you want to have multiple raw methods with different `whenUntagged` setting.
  *
  * NOTE: The example above assumes there is a Json` type defined with appropriate encodings -
  * see [[encoded]] for more details on parameter and method result encoding.
  *
  * An example of real RPC for `ExampleRawRpc`:
  *
  * {{{
  * trait ExampleApi {
  *   def getUser(id: UserId): Future[User]
  *   @POST def saveUser(user: User): Future[Unit]
  * }
  * object ExampleApi {
  *   implicit val AsRawReal: AsRawReal[ExampleRawRpc,ExampleApi] = AsRawReal.materialize
  * }
  * }}}
  *
  * @tparam BaseTag base type for tags that can be used on real RPC methods
  * @param defaultTag default tag value assumed for untagged methods
  * */
final class methodTag[BaseTag <: RpcTag](val defaultTag: BaseTag = null) extends RawRpcAnnotation

/**
  * Parameter tagging lets you have more explicit control over which raw parameters can match which real
  * parameters. This way you can have some of the parameters annotated in order to treat them differently, e.g.
  * they may be [[verbatim]], encoded in a different way or collected to a different raw container (e.g.
  * `Map[String,Raw]` vs `List[Raw]` - see [[com.avsystem.commons.meta.multi multi]] for more details).
  *
  * Example:
  * {{{
  * sealed trait RestParam extends RpcTag
  * class Body extends RestParam
  * class Url extends RestParam
  * class Path extends RestParam
  *
  * @paramTag[RestParam](new Body)
  * trait RestRawRpc {
  *   def get(
  *     @methodName name: String,
  *     @multi @verbatim @tagged[Path] pathParams: List[String],
  *     @multi @verbatim @tagged[Url] urlParams: Map[String,String],
  *     @multi @tagged[Body] bodyParams: Map[String,Json]
  *   ): Future[Json]
  * }
  * }}}
  *
  * NOTE: The example above assumes there is a `Json` type defined with appropriate encodings -
  * see [[encoded]] for more details on parameter and method result encoding.
  *
  * The example above configures parameter tag type for the entire trait, but you can also do it for
  * each raw method, e.g.
  *
  * {{{
  * trait RestRawRpc {
  *   @paramTag[RestParam](new Body)
  *   def get(...)
  * }
  * }}}
  *
  * @tparam BaseTag base type for tags that can be used on real RPC parameters
  * @param defaultTag default tag value assumed for untagged real parameters
  */
final class paramTag[BaseTag <: RpcTag](val defaultTag: BaseTag = null) extends RawSymAnnotation

/**
  * Like [[paramTag]] or [[methodTag]] but used for tagging case classes in sealed hierarchies when
  * materializing ADT metadata for them.
  */
final class caseTag[BaseTag <: RpcTag](val defaultTag: BaseTag = null) extends RawSymAnnotation

/**
  * Annotation applied on raw methods or raw parameters that limits matching real methods or real parameters to
  * only these annotated as `Tag`. See [[methodTag]] and [[paramTag]] for more explanation. NOTE: `Tag` may
  * also be some common supertype of multiple tags which are accepted by this raw method or param.
  *
  * @tparam Tag annotation type required to be present on real method or parameter
  * @param whenUntagged default tag value assumed for untagged methods/parameters - if specified, this effectively
  *                     means that raw method/parameter will also match untagged real methods/parameters and assume
  *                     the default tag value for them
  */
final class tagged[Tag <: RpcTag](val whenUntagged: Tag = null)
  extends RawMethodAnnotation with RawParamAnnotation




© 2015 - 2025 Weber Informatics LLC | Privacy Policy