From 0824718eaf7db7b76fb45e472c5795d2c67b3eb8 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 01:28:43 +0200 Subject: [PATCH 01/14] router macro generates direct string comparison instead of hashmap --- README.md | 8 ++-- .../src/main/scala/HttpRpcTransport.scala | 6 +-- .../src/main/scala/HttpRpcRoutes.scala | 6 +-- .../src/main/scala/HttpRpcTransport.scala | 2 +- sloth/src/main/scala-2/internal/Macros.scala | 28 ++++++++++---- sloth/src/main/scala-3/internal/Macros.scala | 37 ++++++++++++------- sloth/src/main/scala/Failures.scala | 3 +- sloth/src/main/scala/LogHandler.scala | 4 +- sloth/src/main/scala/Request.scala | 3 +- sloth/src/main/scala/Router.scala | 34 +++++++---------- sloth/src/main/scala/internal/Impls.scala | 8 ++-- sloth/src/test/scala/ImplsSpec.scala | 16 ++++---- 12 files changed, 86 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 415e4d2a..229e978b 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ val router = Router[ByteBuffer, Future].route[Api](ApiImpl) Use it to route requests to your Api implementation: ```scala -val result = router(Request[ByteBuffer]("Api" :: "fun" :: Nil, bytes)) +val result = router(Request[ByteBuffer](RequestPath(apiName = "Api", methodName = "fun"), bytes)) // Now result contains the serialized Int result returned by the method ApiImpl.fun ``` @@ -187,7 +187,7 @@ For logging, you can define a `LogHandler`, which can log each request including Define it when creating the `Client`: ```scala object MyLogHandler extends LogHandler[ClientResult[_]] { - def logRequest[T](path: List[String], argumentObject: Any, result: ClientResult[T]): ClientResult[T] = ??? + def logRequest[T](path: RequestPath, argumentObject: Any, result: ClientResult[T]): ClientResult[T] = ??? } val client = Client[PickleType, ClientResult](Transport, MyLogHandler) @@ -196,7 +196,7 @@ val client = Client[PickleType, ClientResult](Transport, MyLogHandler) Define it when creating the `Router`: ```scala object MyLogHandler extends LogHandler[ServerResult[_]] { - def logRequest[T](path: List[String], argumentObject: Any, result: ServerResult[T]): ServerResult[T] = ??? + def logRequest[T](path: RequestPath, argumentObject: Any, result: ServerResult[T]): ServerResult[T] = ??? } val router = Router[PickleType, ServerResult](MyLogHandler) @@ -231,7 +231,7 @@ trait Api { ``` For each declared method in this trait (in this case `fun`): -* Calculate method path: `List("Api", "fun")` (`PathName` annotations on the trait or method are taken into account). +* Calculate method path: `RequestPath("Api", "fun")` (`PathName` annotations on the trait or method are taken into account). * Serialize the method parameters as a tuple: `(a, b, c)`. ### Server diff --git a/http4sClient/src/main/scala/HttpRpcTransport.scala b/http4sClient/src/main/scala/HttpRpcTransport.scala index f5e939f3..2bbf8015 100644 --- a/http4sClient/src/main/scala/HttpRpcTransport.scala +++ b/http4sClient/src/main/scala/HttpRpcTransport.scala @@ -5,16 +5,16 @@ import cats.implicits._ import org.http4s.client.Client import org.http4s.{EntityBody, EntityDecoder, EntityEncoder, Headers, HttpVersion, Method, Request, ServerSentEvent, Uri} import fs2.Stream -import sloth.RequestTransport +import sloth.{RequestPath, RequestTransport} case class HttpRequestConfig( baseUri: Uri = Uri(path = Uri.Path.Root), headers: Headers = Headers.empty, httpVersion: HttpVersion = HttpVersion.`HTTP/1.1`, ) { - def toRequest[F[_]](requestPath: List[String], entityBody: EntityBody[F]): Request[F] = Request[F]( + def toRequest[F[_]](requestPath: RequestPath, entityBody: EntityBody[F]): Request[F] = Request[F]( method = Method.POST, - uri = requestPath.foldLeft(baseUri)(_ / _), + uri = baseUri / requestPath.apiName / requestPath.methodName, httpVersion = httpVersion, headers = headers, body = entityBody, diff --git a/http4sServer/src/main/scala/HttpRpcRoutes.scala b/http4sServer/src/main/scala/HttpRpcRoutes.scala index 405bf657..a995fb69 100644 --- a/http4sServer/src/main/scala/HttpRpcRoutes.scala +++ b/http4sServer/src/main/scala/HttpRpcRoutes.scala @@ -6,7 +6,7 @@ import cats.effect.Concurrent import org.http4s._ import org.http4s.dsl.Http4sDsl import fs2.Stream -import sloth.{Router, ServerFailure} +import sloth.{RequestPath, Router, ServerFailure} object HttpRpcRoutes { @@ -25,7 +25,7 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { case Vector(apiName, methodName) => - val path = List(apiName.decoded(), methodName.decoded()) + val path = RequestPath(apiName.decoded(), methodName.decoded()) val result = router(request).getFunction(path).traverse { f => request.as[PickleType].flatMap { payload => f(payload) match { @@ -56,7 +56,7 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { case Vector(apiName, methodName) => - val path = List(apiName.decoded(), methodName.decoded()) + val path = RequestPath(apiName.decoded(), methodName.decoded()) val result = router(request).getFunction(path).traverse { f => request.as[String].flatMap { payload => f(payload) match { diff --git a/jsdomClient/src/main/scala/HttpRpcTransport.scala b/jsdomClient/src/main/scala/HttpRpcTransport.scala index 92fbdb82..16372389 100644 --- a/jsdomClient/src/main/scala/HttpRpcTransport.scala +++ b/jsdomClient/src/main/scala/HttpRpcTransport.scala @@ -19,7 +19,7 @@ object HttpRpcTransport { def apply[F[_]: Async](config: F[HttpRequestConfig]): RequestTransport[String, F] = new RequestTransport[String, F] { override def apply(request: Request[String]): F[String] = for { config <- config - url = s"${config.baseUri}${request.path.mkString("/")}" + url = s"${config.baseUri}/${request.path.apiName}/${request.path.methodName}" requestArgs = new dom.RequestInit { headers = config.headers.toJSDictionary; method = dom.HttpMethod.POST; body = request.payload } result <- Async[F].fromThenable(Async[F].delay[js.Thenable[String]](dom.fetch(url, requestArgs).`then`[String](_.text()))) } yield result diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index 91ff55ab..d743f478 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -1,5 +1,7 @@ package sloth.internal +import sloth.RequestPath + import scala.reflect.macros.blackbox.Context object Validator { @@ -16,8 +18,10 @@ class Translator[C <: Context](val c: C) { import c.universe._ import Validator._ - val slothPkg = q"_root_.sloth" - val internalPkg = q"_root_.sloth.internal" + object implicits { + implicit val liftRequestPath: Liftable[RequestPath] = + Liftable[RequestPath]{ r => q"new _root_.sloth.RequestPath(${r.apiName}, ${r.methodName})" } + } def abort(msg: String) = c.abort(c.enclosingPosition, msg) @@ -116,6 +120,7 @@ class Translator[C <: Context](val c: C) { object Translator { def apply[T](c: Context)(f: Translator[c.type] => c.Tree): c.Expr[T] = { val tree = f(new Translator(c)) +// println(tree) c.Expr(tree) } } @@ -125,6 +130,7 @@ object TraitMacro { (c: Context) (impl: c.Tree) (implicit traitTag: c.WeakTypeTag[Trait], resultTag: c.WeakTypeTag[Result[_]]): c.Expr[Trait] = Translator(c) { t => + import t.implicits._ import c.universe._ val validMethods = t.supportedMethodsInType(traitTag.tpe, resultTag.tpe) @@ -132,7 +138,7 @@ object TraitMacro { val traitPathPart = t.traitPathPart(traitTag.tpe) val methodImplList = validMethods.collect { case (symbol, method) => val methodPathPart = t.methodPathPart(symbol) - val path = traitPathPart :: methodPathPart :: Nil + val path = RequestPath(traitPathPart, methodPathPart) val parameters = t.paramsAsValDefs(method) val paramsType = t.paramsType(method) val paramListValue = t.wrapAsParamsType(method) @@ -184,18 +190,17 @@ object RouterMacro { val validMethods = t.supportedMethodsInType(traitTag.tpe, resultTag.tpe) val traitPathPart = t.traitPathPart(traitTag.tpe) - val methodTuples = validMethods.map { case (symbol, method) => + val methodCases = validMethods.map { case (symbol, method) => val methodPathPart = t.methodPathPart(symbol) - val path = traitPathPart :: methodPathPart :: Nil val paramsType = t.paramsType(method) val argParams = t.objectToParams(method, TermName("args")) val innerReturnType = t.getInnerTypeOutOfReturnType(resultTag.tpe, method.finalResultType) val payloadFunction = - q"""(payload: ${pickleTypeTag.tpe}) => impl.execute[${paramsType}, $innerReturnType]($path, payload) { args => + q"""(payload: ${pickleTypeTag.tpe}) => impl.execute[${paramsType}, $innerReturnType](requestPath, payload) { args => value.${symbol.name.toTermName}(...$argParams) }""" - q"($methodPathPart, $payloadFunction)" + cq"$methodPathPart => Some($payloadFunction)" } q""" @@ -203,7 +208,14 @@ object RouterMacro { val implRouter = ${c.prefix} val impl = $impl - implRouter.orElse($traitPathPart, scala.collection.immutable.Map(..$methodTuples)) + implRouter.orElse { requestPath => + if (requestPath.apiName == $traitPathPart) { + requestPath.methodName match { + case ..$methodCases + case _ => None + } + } else None + } """ } diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 08c60b18..566b3db6 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -7,6 +7,13 @@ import scala.annotation.meta.param import scala.NonEmptyTuple import scala.quoted.runtime.StopMacroExpansion +private implicit val toExprRequestPath: ToExpr[RequestPath] = new ToExpr[RequestPath] { + def apply(path: RequestPath)(using Quotes): Expr[RequestPath] = { + import quotes.reflect._ + '{ RequestPath(${Expr(path.apiName)}, ${Expr(path.methodName)}) } + } +} + private def getPathName(using Quotes)(symbol: quotes.reflect.Symbol): String = { import quotes.reflect.* @@ -161,7 +168,8 @@ object TraitMacro { val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => val body = (cls.declaredMethods.zip(methods)).map { case (method, origMethod) => val methodPathPart = getPathName(origMethod) - val path = traitPathPart :: methodPathPart :: Nil + val path = RequestPath(traitPathPart, methodPathPart) + val pathExpr = Expr(path) DefDef(method, { argss => // check argss and method.paramSyms have same length outside and inside @@ -170,7 +178,6 @@ object TraitMacro { argss.zip(method.paramSymss).forall { case (a,b) => a.length == b.length } Option.when(sameLength) { - val pathExpr = Expr(path) val tupleExpr = argss.flatten match { case Nil => '{()} case arg :: Nil => arg.asExpr @@ -214,7 +221,7 @@ object TraitMacro { Block(List(clsDef), newCls) } - // println(result.show) +// println(result.show) result.asExprOf[Trait] } } @@ -243,11 +250,9 @@ object RouterMacro { type FunctionOutput = Either[ServerFailure, Result[PickleType]] val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => - val methodDefinitions = methods.map { method => + def methodCases(requestPathTerm: Term) = methods.map { method => val methodPathPart = getPathName(method) - val path = traitPathPart :: methodPathPart :: Nil - - val pathExpr = Expr(path) + val path = RequestPath(traitPathPart, methodPathPart) val returnType = getInnerTypeOutOfReturnType[Trait, Result](method) @@ -301,23 +306,29 @@ object RouterMacro { Select(implRef, routerImplType.declaredMethod("execute").head), List(tupleTypeTree, returnTypeTree) ), - List(pathExpr.asTerm, payloadArg.asExpr.asTerm) + List(requestPathTerm, payloadArg.asExpr.asTerm) ), List(instanceLambda) ) }) - '{ - (${Expr(methodPathPart)}, ${lambda.asExprOf[FunctionInput => FunctionOutput]}) - } + val caseBody = '{ Some(${lambda.asExprOf[FunctionInput => FunctionOutput]}) } + CaseDef(Literal(StringConstant(methodPathPart)), None, caseBody.asTerm) } '{ - ${prefix}.orElse(${Expr(traitPathPart)}, Map.from(${Expr.ofList(methodDefinitions)})) + ${prefix}.orElse { requestPath => + if (requestPath.apiName == ${Expr(traitPathPart)}) { + ${Match( + '{requestPath.methodName}.asTerm, + methodCases('{requestPath}.asTerm) :+ CaseDef(Wildcard(), None, '{ None }.asTerm) + ).asExprOf[Option[FunctionInput => FunctionOutput]]} + } else None + } }.asTerm } - // println(result.show) +// println(result.show) result.asExprOf[Router[PickleType, Result]] } } diff --git a/sloth/src/main/scala/Failures.scala b/sloth/src/main/scala/Failures.scala index 81df9b01..f951c3cb 100644 --- a/sloth/src/main/scala/Failures.scala +++ b/sloth/src/main/scala/Failures.scala @@ -4,7 +4,7 @@ sealed trait ServerFailure { def toException = ServerException(this) } object ServerFailure { - case class PathNotFound(path: List[String]) extends ServerFailure + case class PathNotFound(path: RequestPath) extends ServerFailure case class HandlerError(ex: Throwable) extends ServerFailure case class DeserializerError(ex: Throwable) extends ServerFailure } @@ -17,7 +17,6 @@ sealed trait ClientFailure { object ClientFailure { case class TransportError(ex: Throwable) extends ClientFailure case class DeserializerError(ex: Throwable) extends ClientFailure - } case class ClientException(failure: ClientFailure) extends Exception(failure.toString) diff --git a/sloth/src/main/scala/LogHandler.scala b/sloth/src/main/scala/LogHandler.scala index 32d7d616..f36f91a9 100644 --- a/sloth/src/main/scala/LogHandler.scala +++ b/sloth/src/main/scala/LogHandler.scala @@ -1,10 +1,10 @@ package sloth trait LogHandler[Result[_]] { - def logRequest[A, T](path: List[String], argumentObject: A, result: Result[T]): Result[T] + def logRequest[A, T](path: RequestPath, argumentObject: A, result: Result[T]): Result[T] } object LogHandler { def empty[Result[_]]: LogHandler[Result] = new LogHandler[Result] { - def logRequest[A, T](path: List[String], argumentObject: A, result: Result[T]): Result[T] = result + def logRequest[A, T](path: RequestPath, argumentObject: A, result: Result[T]): Result[T] = result } } diff --git a/sloth/src/main/scala/Request.scala b/sloth/src/main/scala/Request.scala index fb188faa..267b3e05 100644 --- a/sloth/src/main/scala/Request.scala +++ b/sloth/src/main/scala/Request.scala @@ -1,3 +1,4 @@ package sloth -case class Request[T](path: List[String], payload: T) +case class RequestPath(apiName: String, methodName: String) +case class Request[T](path: RequestPath, payload: T) diff --git a/sloth/src/main/scala/Router.scala b/sloth/src/main/scala/Router.scala index c0386832..7e9535a4 100644 --- a/sloth/src/main/scala/Router.scala +++ b/sloth/src/main/scala/Router.scala @@ -7,7 +7,7 @@ import cats.implicits._ import cats.~> trait Router[PickleType, Result[_]] { - protected def apiMap: Router.ApiMap[PickleType, Result] + val getFunction: Router.ApiMapping[PickleType, Result] def apply(request: Request[PickleType]): Either[ServerFailure, Result[PickleType]] = getFunction(request.path) match { @@ -15,40 +15,34 @@ trait Router[PickleType, Result[_]] { case None => Left(ServerFailure.PathNotFound(request.path)) } - def getFunction(path: List[String]): Option[PickleType => Either[ServerFailure, Result[PickleType]]] = - path match { - case apiName :: methodName :: Nil => apiMap.get(apiName).flatMap(_.get(methodName)) - case _ => None - } - - def orElse(name: String, value: Router.ApiMapValue[PickleType, Result]): Router[PickleType, Result] + def orElse(collect: Router.ApiMapping[PickleType, Result]): Router[PickleType, Result] } -class RouterCo[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], protected val apiMap: Router.ApiMap[PickleType, Result])(implicit - private[sloth] val functor: Functor[Result] +class RouterCo[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val getFunction: Router.ApiMapping[PickleType, Result])(implicit + private[sloth] val functor: Functor[Result] ) extends Router[PickleType, Result] with PlatformSpecificRouterCo[PickleType, Result] { - def orElse(name: String, value: Router.ApiMapValue[PickleType, Result]): RouterCo[PickleType, Result] = new RouterCo(logger, apiMap + (name -> value)) + def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterCo[PickleType, Result] = new RouterCo(logger, request => getFunction(request).orElse(collect(request))) - def mapResult[R[_]: Functor](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterCo(logger, apiMap.map { case (k, v) => (k, v.map { case (k, v) => (k, v.map(_.map(f.apply))) }.toMap) }.toMap) + def mapResult[R[_]: Functor](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterCo(logger, request => getFunction(request).map { case v => v.map(_.map(f.apply)) }) } -class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], protected val apiMap: Router.ApiMap[PickleType, Result])(implicit - private[sloth] val routerHandler: RouterContraHandler[Result] +class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val getFunction: Router.ApiMapping[PickleType, Result])(implicit + private[sloth] val routerHandler: RouterContraHandler[Result] ) extends Router[PickleType, Result] with PlatformSpecificRouterContra[PickleType, Result] { - def orElse(name: String, value: Router.ApiMapValue[PickleType, Result]): RouterContra[PickleType, Result] = new RouterContra(logger, apiMap + (name -> value)) + def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterContra[PickleType, Result] = new RouterContra(logger, request => getFunction(request).orElse(collect(request))) - def mapResult[R[_]: RouterContraHandler](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterContra(logger, apiMap.map { case (k, v) => (k, v.map { case (k, v) => (k, v.map(_.map(f.apply))) }.toMap) }.toMap) + def mapResult[R[_]: RouterContraHandler](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterContra(logger, request => getFunction(request).map { case v => v.map(_.map(f.apply)) }) } object Router { - type ApiMapValue[PickleType, Result[_]] = Map[String, PickleType => Either[ServerFailure, Result[PickleType]]] - type ApiMap[PickleType, Result[_]] = Map[String, ApiMapValue[PickleType, Result]] + type ApiMapping[PickleType, Result[_]] = RequestPath => Option[PickleType => Either[ServerFailure, Result[PickleType]]] + private val emptyApiMapping: Any => None.type = (_: Any) => None def apply[PickleType, Result[_]: Functor]: RouterCo[PickleType, Result] = apply(LogHandler.empty[Result]) - def apply[PickleType, Result[_]: Functor](logger: LogHandler[Result]): RouterCo[PickleType, Result] = new RouterCo[PickleType, Result](logger, Map.empty) + def apply[PickleType, Result[_]: Functor](logger: LogHandler[Result]): RouterCo[PickleType, Result] = new RouterCo[PickleType, Result](logger, emptyApiMapping) def contra[PickleType, Result[_]: RouterContraHandler]: RouterContra[PickleType, Result] = contra(LogHandler.empty[Result]) - def contra[PickleType, Result[_]: RouterContraHandler](logger: LogHandler[Result]): RouterContra[PickleType, Result] = new RouterContra[PickleType, Result](logger, Map.empty) + def contra[PickleType, Result[_]: RouterContraHandler](logger: LogHandler[Result]): RouterContra[PickleType, Result] = new RouterContra[PickleType, Result](logger, emptyApiMapping) } diff --git a/sloth/src/main/scala/internal/Impls.scala b/sloth/src/main/scala/internal/Impls.scala index 4454c34d..8c37f21d 100644 --- a/sloth/src/main/scala/internal/Impls.scala +++ b/sloth/src/main/scala/internal/Impls.scala @@ -6,7 +6,7 @@ import chameleon.{Serializer, Deserializer} import scala.util.{Success, Failure, Try} class RouterImpl[PickleType, Result[_]](router: RouterCo[PickleType, Result]) { - def execute[T, R](path: List[String], arguments: PickleType)(call: T => Result[R])(implicit deserializer: Deserializer[T, PickleType], serializer: Serializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { + def execute[T, R](path: RequestPath, arguments: PickleType)(call: T => Result[R])(implicit deserializer: Deserializer[T, PickleType], serializer: Serializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { deserializer.deserialize(arguments) match { case Right(arguments) => Try(call(arguments)) match { @@ -21,7 +21,7 @@ class RouterImpl[PickleType, Result[_]](router: RouterCo[PickleType, Result]) { } class RouterContraImpl[PickleType, Result[_]](router: RouterContra[PickleType, Result]) { - def execute[T, R](path: List[String], arguments: PickleType)(call: T => Result[R])(implicit deserializerT: Deserializer[T, PickleType], deserializerR: Deserializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { + def execute[T, R](path: RequestPath, arguments: PickleType)(call: T => Result[R])(implicit deserializerT: Deserializer[T, PickleType], deserializerR: Deserializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { deserializerT.deserialize(arguments) match { case Right(arguments) => Try(call(arguments)) match { @@ -39,7 +39,7 @@ class RouterContraImpl[PickleType, Result[_]](router: RouterContra[PickleType, R class ClientImpl[PickleType, Result[_]](client: ClientCo[PickleType, Result]) { - def execute[T, R](path: List[String], arguments: T)(implicit deserializer: Deserializer[R, PickleType], serializer: Serializer[T, PickleType]): Result[R] = { + def execute[T, R](path: RequestPath, arguments: T)(implicit deserializer: Deserializer[R, PickleType], serializer: Serializer[T, PickleType]): Result[R] = { val serializedArguments = serializer.serialize(arguments) val request: Request[PickleType] = Request(path, serializedArguments) val result: Result[R] = Try(client.transport(request)) match { @@ -58,7 +58,7 @@ class ClientImpl[PickleType, Result[_]](client: ClientCo[PickleType, Result]) { class ClientContraImpl[PickleType, Result[_]](client: ClientContra[PickleType, Result]) { - def execute[T, R](path: List[String], arguments: T)(implicit serializerR: Serializer[R, PickleType], serializerT: Serializer[T, PickleType]): Result[R] = { + def execute[T, R](path: RequestPath, arguments: T)(implicit serializerR: Serializer[R, PickleType], serializerT: Serializer[T, PickleType]): Result[R] = { val serializedArguments = serializerT.serialize(arguments) val request: Request[PickleType] = Request(path, serializedArguments) val result: Result[R] = Try(client.transport(request)) match { diff --git a/sloth/src/test/scala/ImplsSpec.scala b/sloth/src/test/scala/ImplsSpec.scala index 91e367ac..134518f6 100644 --- a/sloth/src/test/scala/ImplsSpec.scala +++ b/sloth/src/test/scala/ImplsSpec.scala @@ -21,7 +21,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val resultValue = "\"Argument(1)\"" - val result = impl.execute[Argument, String](Nil, pickledInput)(_.toString) + val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput)(_.toString) result mustEqual Right(resultValue) } @@ -33,7 +33,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val exception = new Exception("meh") - val result = impl.execute[Argument, String](Nil, pickledInput)(_ => throw exception) + val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput)(_ => throw exception) result mustEqual Left(ServerFailure.HandlerError(exception)) } @@ -51,7 +51,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val receivedParameters = collection.mutable.ArrayBuffer.empty[Argument] val receivedStrings = collection.mutable.ArrayBuffer.empty[String] - val result = impl.execute[Argument, String](Nil, pickledInput) { argument => + val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput) { argument => receivedParameters += argument { string => @@ -73,7 +73,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val exception = new Exception("meh") - val result = impl.execute[Argument, String](Nil, pickledInput)(_ => throw exception) + val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput)(_ => throw exception) result mustEqual Left(ServerFailure.HandlerError(exception)) } @@ -90,7 +90,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientImpl(client) val argument = Argument(1) - val result = impl.execute[Argument, Argument]("api" :: "f" :: Nil, argument) + val result = impl.execute[Argument, Argument](RequestPath("api", "f"), argument) result mustEqual Right(argument) } @@ -102,7 +102,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientImpl(client) val argument = Argument(1) - val result = impl.execute[Argument, Double]("api" :: "f" :: Nil, argument) + val result = impl.execute[Argument, Double](RequestPath("api", "f"), argument) result mustEqual Left(ClientFailure.TransportError(exception)) } @@ -125,7 +125,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientContraImpl(client) val argument = Argument(1) - val resultf = impl.execute[Argument, Argument]("api" :: "f" :: Nil, argument) + val resultf = impl.execute[Argument, Argument](RequestPath("api", "f"), argument) resultf(Argument(2)) mustEqual Right(()) @@ -140,7 +140,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientContraImpl(client) val argument = Argument(1) - val resultf = impl.execute[Argument, Double]("api" :: "f" :: Nil, argument) + val resultf = impl.execute[Argument, Double](RequestPath("api", "f"), argument) resultf(2.0) mustEqual Left(ClientFailure.TransportError(exception)) } From 235be869f0523c033591e3713f1d862255e8d8a1 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 18:56:29 +0200 Subject: [PATCH 02/14] rename and compatability --- .../src/main/scala/HttpRpcTransport.scala | 10 ++++---- .../src/main/scala/HttpRpcRoutes.scala | 12 +++++----- .../src/main/scala/HttpRpcTransport.scala | 2 +- sloth/src/main/scala-2/internal/Macros.scala | 16 ++++++------- sloth/src/main/scala-3/internal/Macros.scala | 22 +++++++++--------- sloth/src/main/scala/Failures.scala | 7 +++++- sloth/src/main/scala/LogHandler.scala | 4 ++-- sloth/src/main/scala/Request.scala | 17 ++++++++++++-- sloth/src/main/scala/Router.scala | 23 +++++++++++-------- sloth/src/main/scala/internal/Impls.scala | 8 +++---- sloth/src/test/scala/ImplsSpec.scala | 16 ++++++------- 11 files changed, 79 insertions(+), 58 deletions(-) diff --git a/http4sClient/src/main/scala/HttpRpcTransport.scala b/http4sClient/src/main/scala/HttpRpcTransport.scala index 2bbf8015..afd5df85 100644 --- a/http4sClient/src/main/scala/HttpRpcTransport.scala +++ b/http4sClient/src/main/scala/HttpRpcTransport.scala @@ -5,16 +5,16 @@ import cats.implicits._ import org.http4s.client.Client import org.http4s.{EntityBody, EntityDecoder, EntityEncoder, Headers, HttpVersion, Method, Request, ServerSentEvent, Uri} import fs2.Stream -import sloth.{RequestPath, RequestTransport} +import sloth.{Endpoint, RequestTransport} case class HttpRequestConfig( baseUri: Uri = Uri(path = Uri.Path.Root), headers: Headers = Headers.empty, httpVersion: HttpVersion = HttpVersion.`HTTP/1.1`, ) { - def toRequest[F[_]](requestPath: RequestPath, entityBody: EntityBody[F]): Request[F] = Request[F]( + def toRequest[F[_]](endpoint: Endpoint, entityBody: EntityBody[F]): Request[F] = Request[F]( method = Method.POST, - uri = baseUri / requestPath.apiName / requestPath.methodName, + uri = baseUri / endpoint.apiName / endpoint.methodName, httpVersion = httpVersion, headers = headers, body = entityBody, @@ -38,7 +38,7 @@ object HttpRpcTransport { ): RequestTransport[PickleType, F] = new sloth.RequestTransport[PickleType, F] { override def apply(request: sloth.Request[PickleType]): F[PickleType] = for { config <- config - responseBody <- client.expect[PickleType](config.toRequest(request.path, encoder.toEntity(request.payload).body)) + responseBody <- client.expect[PickleType](config.toRequest(request.endpoint, encoder.toEntity(request.payload).body)) } yield responseBody } @@ -52,7 +52,7 @@ object HttpRpcTransport { ): RequestTransport[String, Stream[F, *]] = new sloth.RequestTransport[String, Stream[F, *]] { override def apply(request: sloth.Request[String]): Stream[F, String] = for { config <- Stream.eval(config) - response <- Stream.resource(client.run(config.toRequest(request.path, EntityEncoder[F, String].toEntity(request.payload).body))) + response <- Stream.resource(client.run(config.toRequest(request.endpoint, EntityEncoder[F, String].toEntity(request.payload).body))) event <- response.body.through(ServerSentEvent.decoder[F]) data <- Stream.fromOption(event.data) } yield data diff --git a/http4sServer/src/main/scala/HttpRpcRoutes.scala b/http4sServer/src/main/scala/HttpRpcRoutes.scala index a995fb69..d9ef4cc0 100644 --- a/http4sServer/src/main/scala/HttpRpcRoutes.scala +++ b/http4sServer/src/main/scala/HttpRpcRoutes.scala @@ -6,7 +6,7 @@ import cats.effect.Concurrent import org.http4s._ import org.http4s.dsl.Http4sDsl import fs2.Stream -import sloth.{RequestPath, Router, ServerFailure} +import sloth.{Endpoint, Router, ServerFailure} object HttpRpcRoutes { @@ -25,8 +25,8 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { case Vector(apiName, methodName) => - val path = RequestPath(apiName.decoded(), methodName.decoded()) - val result = router(request).getFunction(path).traverse { f => + val endpoint = Endpoint(apiName.decoded(), methodName.decoded()) + val result = router(request).get(endpoint).traverse { f => request.as[PickleType].flatMap { payload => f(payload) match { case Left(error) => serverFailureToResponse[F](dsl, onError)(error) @@ -56,8 +56,8 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { case Vector(apiName, methodName) => - val path = RequestPath(apiName.decoded(), methodName.decoded()) - val result = router(request).getFunction(path).traverse { f => + val endpoint = Endpoint(apiName.decoded(), methodName.decoded()) + val result = router(request).get(endpoint).traverse { f => request.as[String].flatMap { payload => f(payload) match { case Left(error) => serverFailureToResponse[F](dsl, onError)(error) @@ -75,7 +75,7 @@ object HttpRpcRoutes { private def serverFailureToResponse[F[_]: Concurrent](dsl: Http4sDsl[F], onError: PartialFunction[Throwable, F[Response[F]]])(failure: ServerFailure): F[Response[F]] = { import dsl._ failure match { - case ServerFailure.PathNotFound(_) => NotFound() + case ServerFailure.EndpointNotFound(_) => NotFound() case ServerFailure.HandlerError(err) => onError.lift(err).getOrElse(InternalServerError(err.getMessage)) case ServerFailure.DeserializerError(err) => onError.lift(err).getOrElse(BadRequest(err.getMessage)) } diff --git a/jsdomClient/src/main/scala/HttpRpcTransport.scala b/jsdomClient/src/main/scala/HttpRpcTransport.scala index 16372389..be19f3bc 100644 --- a/jsdomClient/src/main/scala/HttpRpcTransport.scala +++ b/jsdomClient/src/main/scala/HttpRpcTransport.scala @@ -19,7 +19,7 @@ object HttpRpcTransport { def apply[F[_]: Async](config: F[HttpRequestConfig]): RequestTransport[String, F] = new RequestTransport[String, F] { override def apply(request: Request[String]): F[String] = for { config <- config - url = s"${config.baseUri}/${request.path.apiName}/${request.path.methodName}" + url = s"${config.baseUri}/${request.endpoint.apiName}/${request.endpoint.methodName}" requestArgs = new dom.RequestInit { headers = config.headers.toJSDictionary; method = dom.HttpMethod.POST; body = request.payload } result <- Async[F].fromThenable(Async[F].delay[js.Thenable[String]](dom.fetch(url, requestArgs).`then`[String](_.text()))) } yield result diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index d743f478..436a9969 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -1,6 +1,6 @@ package sloth.internal -import sloth.RequestPath +import sloth.Endpoint import scala.reflect.macros.blackbox.Context @@ -19,8 +19,8 @@ class Translator[C <: Context](val c: C) { import Validator._ object implicits { - implicit val liftRequestPath: Liftable[RequestPath] = - Liftable[RequestPath]{ r => q"new _root_.sloth.RequestPath(${r.apiName}, ${r.methodName})" } + implicit val liftEndpoint: Liftable[Endpoint] = + Liftable[Endpoint]{ r => q"new _root_.sloth.Endpoint(${r.apiName}, ${r.methodName})" } } def abort(msg: String) = c.abort(c.enclosingPosition, msg) @@ -138,7 +138,7 @@ object TraitMacro { val traitPathPart = t.traitPathPart(traitTag.tpe) val methodImplList = validMethods.collect { case (symbol, method) => val methodPathPart = t.methodPathPart(symbol) - val path = RequestPath(traitPathPart, methodPathPart) + val path = Endpoint(traitPathPart, methodPathPart) val parameters = t.paramsAsValDefs(method) val paramsType = t.paramsType(method) val paramListValue = t.wrapAsParamsType(method) @@ -196,7 +196,7 @@ object RouterMacro { val argParams = t.objectToParams(method, TermName("args")) val innerReturnType = t.getInnerTypeOutOfReturnType(resultTag.tpe, method.finalResultType) val payloadFunction = - q"""(payload: ${pickleTypeTag.tpe}) => impl.execute[${paramsType}, $innerReturnType](requestPath, payload) { args => + q"""(payload: ${pickleTypeTag.tpe}) => impl.execute[${paramsType}, $innerReturnType](endpoint, payload) { args => value.${symbol.name.toTermName}(...$argParams) }""" @@ -208,9 +208,9 @@ object RouterMacro { val implRouter = ${c.prefix} val impl = $impl - implRouter.orElse { requestPath => - if (requestPath.apiName == $traitPathPart) { - requestPath.methodName match { + implRouter.orElse { endpoint => + if (endpoint.apiName == $traitPathPart) { + endpoint.methodName match { case ..$methodCases case _ => None } diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 566b3db6..28eb3d51 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -7,10 +7,10 @@ import scala.annotation.meta.param import scala.NonEmptyTuple import scala.quoted.runtime.StopMacroExpansion -private implicit val toExprRequestPath: ToExpr[RequestPath] = new ToExpr[RequestPath] { - def apply(path: RequestPath)(using Quotes): Expr[RequestPath] = { +private implicit val toExprEndpoint: ToExpr[Endpoint] = new ToExpr[Endpoint] { + def apply(path: Endpoint)(using Quotes): Expr[Endpoint] = { import quotes.reflect._ - '{ RequestPath(${Expr(path.apiName)}, ${Expr(path.methodName)}) } + '{ Endpoint(${Expr(path.apiName)}, ${Expr(path.methodName)}) } } } @@ -168,7 +168,7 @@ object TraitMacro { val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => val body = (cls.declaredMethods.zip(methods)).map { case (method, origMethod) => val methodPathPart = getPathName(origMethod) - val path = RequestPath(traitPathPart, methodPathPart) + val path = Endpoint(traitPathPart, methodPathPart) val pathExpr = Expr(path) DefDef(method, { argss => @@ -250,9 +250,9 @@ object RouterMacro { type FunctionOutput = Either[ServerFailure, Result[PickleType]] val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => - def methodCases(requestPathTerm: Term) = methods.map { method => + def methodCases(endpointTerm: Term) = methods.map { method => val methodPathPart = getPathName(method) - val path = RequestPath(traitPathPart, methodPathPart) + val path = Endpoint(traitPathPart, methodPathPart) val returnType = getInnerTypeOutOfReturnType[Trait, Result](method) @@ -306,7 +306,7 @@ object RouterMacro { Select(implRef, routerImplType.declaredMethod("execute").head), List(tupleTypeTree, returnTypeTree) ), - List(requestPathTerm, payloadArg.asExpr.asTerm) + List(endpointTerm, payloadArg.asExpr.asTerm) ), List(instanceLambda) ) @@ -317,11 +317,11 @@ object RouterMacro { } '{ - ${prefix}.orElse { requestPath => - if (requestPath.apiName == ${Expr(traitPathPart)}) { + ${prefix}.orElse { endpoint => + if (endpoint.apiName == ${Expr(traitPathPart)}) { ${Match( - '{requestPath.methodName}.asTerm, - methodCases('{requestPath}.asTerm) :+ CaseDef(Wildcard(), None, '{ None }.asTerm) + '{endpoint.methodName}.asTerm, + methodCases('{endpoint}.asTerm) :+ CaseDef(Wildcard(), None, '{ None }.asTerm) ).asExprOf[Option[FunctionInput => FunctionOutput]]} } else None } diff --git a/sloth/src/main/scala/Failures.scala b/sloth/src/main/scala/Failures.scala index f951c3cb..eadcc333 100644 --- a/sloth/src/main/scala/Failures.scala +++ b/sloth/src/main/scala/Failures.scala @@ -4,9 +4,14 @@ sealed trait ServerFailure { def toException = ServerException(this) } object ServerFailure { - case class PathNotFound(path: RequestPath) extends ServerFailure + case class EndpointNotFound(path: Endpoint) extends ServerFailure case class HandlerError(ex: Throwable) extends ServerFailure case class DeserializerError(ex: Throwable) extends ServerFailure + + @deprecated("Use EndpointNotFound instead", "0.8.0") + val PathNotFound = EndpointNotFound + @deprecated("Use EndpointNotFound instead", "0.8.0") + type PathNotFound = EndpointNotFound } case class ServerException(failure: ServerFailure) extends Exception(failure.toString) diff --git a/sloth/src/main/scala/LogHandler.scala b/sloth/src/main/scala/LogHandler.scala index f36f91a9..084e0ffe 100644 --- a/sloth/src/main/scala/LogHandler.scala +++ b/sloth/src/main/scala/LogHandler.scala @@ -1,10 +1,10 @@ package sloth trait LogHandler[Result[_]] { - def logRequest[A, T](path: RequestPath, argumentObject: A, result: Result[T]): Result[T] + def logRequest[A, T](endpoint: Endpoint, argumentObject: A, result: Result[T]): Result[T] } object LogHandler { def empty[Result[_]]: LogHandler[Result] = new LogHandler[Result] { - def logRequest[A, T](path: RequestPath, argumentObject: A, result: Result[T]): Result[T] = result + def logRequest[A, T](endpoint: Endpoint, argumentObject: A, result: Result[T]): Result[T] = result } } diff --git a/sloth/src/main/scala/Request.scala b/sloth/src/main/scala/Request.scala index 267b3e05..987484de 100644 --- a/sloth/src/main/scala/Request.scala +++ b/sloth/src/main/scala/Request.scala @@ -1,4 +1,17 @@ package sloth -case class RequestPath(apiName: String, methodName: String) -case class Request[T](path: RequestPath, payload: T) +case class Endpoint(apiName: String, methodName: String) + +case class Request[T](endpoint: Endpoint, payload: T) { + @deprecated("Use .endpoint instead", "0.8.0") + def path: List[String] = List(endpoint.apiName, endpoint.methodName) +} +object Request { + @deprecated("""Use Request(Endpoint("Api", "method"), payload) instead""", "0.8.0") + def apply[T](path: List[String], payload: T): Request[T] = Request(endpointFromList(path), payload) + + private[sloth] def endpointFromList(path: List[String]) = path match { + case List(traitName, methodName) => Endpoint(apiName = traitName, methodName = methodName) + case _ => Endpoint("", "") + } +} diff --git a/sloth/src/main/scala/Router.scala b/sloth/src/main/scala/Router.scala index 7e9535a4..b6750f7c 100644 --- a/sloth/src/main/scala/Router.scala +++ b/sloth/src/main/scala/Router.scala @@ -7,37 +7,40 @@ import cats.implicits._ import cats.~> trait Router[PickleType, Result[_]] { - val getFunction: Router.ApiMapping[PickleType, Result] + val get: Router.ApiMapping[PickleType, Result] def apply(request: Request[PickleType]): Either[ServerFailure, Result[PickleType]] = - getFunction(request.path) match { + get(request.endpoint) match { case Some(function) => function(request.payload) - case None => Left(ServerFailure.PathNotFound(request.path)) + case None => Left(ServerFailure.EndpointNotFound(request.endpoint)) } + @deprecated("Use get(endpoint) instead", "0.8.0") + def getFunction(path: List[String]): Option[PickleType => Either[ServerFailure, Result[PickleType]]] = get(Request.endpointFromList(path)) + def orElse(collect: Router.ApiMapping[PickleType, Result]): Router[PickleType, Result] } -class RouterCo[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val getFunction: Router.ApiMapping[PickleType, Result])(implicit +class RouterCo[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val get: Router.ApiMapping[PickleType, Result])(implicit private[sloth] val functor: Functor[Result] ) extends Router[PickleType, Result] with PlatformSpecificRouterCo[PickleType, Result] { - def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterCo[PickleType, Result] = new RouterCo(logger, request => getFunction(request).orElse(collect(request))) + def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterCo[PickleType, Result] = new RouterCo(logger, request => get(request).orElse(collect(request))) - def mapResult[R[_]: Functor](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterCo(logger, request => getFunction(request).map { case v => v.map(_.map(f.apply)) }) + def mapResult[R[_]: Functor](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterCo(logger, request => get(request).map { case v => v.map(_.map(f.apply)) }) } -class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val getFunction: Router.ApiMapping[PickleType, Result])(implicit +class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val get: Router.ApiMapping[PickleType, Result])(implicit private[sloth] val routerHandler: RouterContraHandler[Result] ) extends Router[PickleType, Result] with PlatformSpecificRouterContra[PickleType, Result] { - def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterContra[PickleType, Result] = new RouterContra(logger, request => getFunction(request).orElse(collect(request))) + def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterContra[PickleType, Result] = new RouterContra(logger, request => get(request).orElse(collect(request))) - def mapResult[R[_]: RouterContraHandler](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterContra(logger, request => getFunction(request).map { case v => v.map(_.map(f.apply)) }) + def mapResult[R[_]: RouterContraHandler](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterContra(logger, request => get(request).map { case v => v.map(_.map(f.apply)) }) } object Router { - type ApiMapping[PickleType, Result[_]] = RequestPath => Option[PickleType => Either[ServerFailure, Result[PickleType]]] + type ApiMapping[PickleType, Result[_]] = Endpoint => Option[PickleType => Either[ServerFailure, Result[PickleType]]] private val emptyApiMapping: Any => None.type = (_: Any) => None def apply[PickleType, Result[_]: Functor]: RouterCo[PickleType, Result] = apply(LogHandler.empty[Result]) diff --git a/sloth/src/main/scala/internal/Impls.scala b/sloth/src/main/scala/internal/Impls.scala index 8c37f21d..21b8e0dc 100644 --- a/sloth/src/main/scala/internal/Impls.scala +++ b/sloth/src/main/scala/internal/Impls.scala @@ -6,7 +6,7 @@ import chameleon.{Serializer, Deserializer} import scala.util.{Success, Failure, Try} class RouterImpl[PickleType, Result[_]](router: RouterCo[PickleType, Result]) { - def execute[T, R](path: RequestPath, arguments: PickleType)(call: T => Result[R])(implicit deserializer: Deserializer[T, PickleType], serializer: Serializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { + def execute[T, R](path: Endpoint, arguments: PickleType)(call: T => Result[R])(implicit deserializer: Deserializer[T, PickleType], serializer: Serializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { deserializer.deserialize(arguments) match { case Right(arguments) => Try(call(arguments)) match { @@ -21,7 +21,7 @@ class RouterImpl[PickleType, Result[_]](router: RouterCo[PickleType, Result]) { } class RouterContraImpl[PickleType, Result[_]](router: RouterContra[PickleType, Result]) { - def execute[T, R](path: RequestPath, arguments: PickleType)(call: T => Result[R])(implicit deserializerT: Deserializer[T, PickleType], deserializerR: Deserializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { + def execute[T, R](path: Endpoint, arguments: PickleType)(call: T => Result[R])(implicit deserializerT: Deserializer[T, PickleType], deserializerR: Deserializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { deserializerT.deserialize(arguments) match { case Right(arguments) => Try(call(arguments)) match { @@ -39,7 +39,7 @@ class RouterContraImpl[PickleType, Result[_]](router: RouterContra[PickleType, R class ClientImpl[PickleType, Result[_]](client: ClientCo[PickleType, Result]) { - def execute[T, R](path: RequestPath, arguments: T)(implicit deserializer: Deserializer[R, PickleType], serializer: Serializer[T, PickleType]): Result[R] = { + def execute[T, R](path: Endpoint, arguments: T)(implicit deserializer: Deserializer[R, PickleType], serializer: Serializer[T, PickleType]): Result[R] = { val serializedArguments = serializer.serialize(arguments) val request: Request[PickleType] = Request(path, serializedArguments) val result: Result[R] = Try(client.transport(request)) match { @@ -58,7 +58,7 @@ class ClientImpl[PickleType, Result[_]](client: ClientCo[PickleType, Result]) { class ClientContraImpl[PickleType, Result[_]](client: ClientContra[PickleType, Result]) { - def execute[T, R](path: RequestPath, arguments: T)(implicit serializerR: Serializer[R, PickleType], serializerT: Serializer[T, PickleType]): Result[R] = { + def execute[T, R](path: Endpoint, arguments: T)(implicit serializerR: Serializer[R, PickleType], serializerT: Serializer[T, PickleType]): Result[R] = { val serializedArguments = serializerT.serialize(arguments) val request: Request[PickleType] = Request(path, serializedArguments) val result: Result[R] = Try(client.transport(request)) match { diff --git a/sloth/src/test/scala/ImplsSpec.scala b/sloth/src/test/scala/ImplsSpec.scala index 134518f6..6d0ebda3 100644 --- a/sloth/src/test/scala/ImplsSpec.scala +++ b/sloth/src/test/scala/ImplsSpec.scala @@ -21,7 +21,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val resultValue = "\"Argument(1)\"" - val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput)(_.toString) + val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput)(_.toString) result mustEqual Right(resultValue) } @@ -33,7 +33,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val exception = new Exception("meh") - val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput)(_ => throw exception) + val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput)(_ => throw exception) result mustEqual Left(ServerFailure.HandlerError(exception)) } @@ -51,7 +51,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val receivedParameters = collection.mutable.ArrayBuffer.empty[Argument] val receivedStrings = collection.mutable.ArrayBuffer.empty[String] - val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput) { argument => + val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput) { argument => receivedParameters += argument { string => @@ -73,7 +73,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val exception = new Exception("meh") - val result = impl.execute[Argument, String](RequestPath("eine", "methode"), pickledInput)(_ => throw exception) + val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput)(_ => throw exception) result mustEqual Left(ServerFailure.HandlerError(exception)) } @@ -90,7 +90,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientImpl(client) val argument = Argument(1) - val result = impl.execute[Argument, Argument](RequestPath("api", "f"), argument) + val result = impl.execute[Argument, Argument](Endpoint("api", "f"), argument) result mustEqual Right(argument) } @@ -102,7 +102,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientImpl(client) val argument = Argument(1) - val result = impl.execute[Argument, Double](RequestPath("api", "f"), argument) + val result = impl.execute[Argument, Double](Endpoint("api", "f"), argument) result mustEqual Left(ClientFailure.TransportError(exception)) } @@ -125,7 +125,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientContraImpl(client) val argument = Argument(1) - val resultf = impl.execute[Argument, Argument](RequestPath("api", "f"), argument) + val resultf = impl.execute[Argument, Argument](Endpoint("api", "f"), argument) resultf(Argument(2)) mustEqual Right(()) @@ -140,7 +140,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientContraImpl(client) val argument = Argument(1) - val resultf = impl.execute[Argument, Double](RequestPath("api", "f"), argument) + val resultf = impl.execute[Argument, Double](Endpoint("api", "f"), argument) resultf(2.0) mustEqual Left(ClientFailure.TransportError(exception)) } From 1661fb0781a784dc3120fe045cc7b6792ce13de1 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 19:14:54 +0200 Subject: [PATCH 03/14] more renaming and compat --- README.md | 33 +++++++++++--------- sloth/src/main/scala-2/internal/Macros.scala | 12 +++---- sloth/src/main/scala-3/internal/Macros.scala | 20 ++++++------ sloth/src/main/scala/Annotations.scala | 2 +- sloth/src/main/scala/package.scala | 4 +++ sloth/src/test/scala/SlothSpec.scala | 2 +- 6 files changed, 41 insertions(+), 32 deletions(-) create mode 100644 sloth/src/main/scala/package.scala diff --git a/README.md b/README.md index 229e978b..05d67120 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ For logging, you can define a `LogHandler`, which can log each request including Define it when creating the `Client`: ```scala object MyLogHandler extends LogHandler[ClientResult[_]] { - def logRequest[T](path: RequestPath, argumentObject: Any, result: ClientResult[T]): ClientResult[T] = ??? + def logRequest[T](endpoint: Endpoint, argumentObject: Any, result: ClientResult[T]): ClientResult[T] = ??? } val client = Client[PickleType, ClientResult](Transport, MyLogHandler) @@ -196,7 +196,7 @@ val client = Client[PickleType, ClientResult](Transport, MyLogHandler) Define it when creating the `Router`: ```scala object MyLogHandler extends LogHandler[ServerResult[_]] { - def logRequest[T](path: RequestPath, argumentObject: Any, result: ServerResult[T]): ServerResult[T] = ??? + def logRequest[T](endpoint: Endpoint, argumentObject: Any, result: ServerResult[T]): ServerResult[T] = ??? } val router = Router[PickleType, ServerResult](MyLogHandler) @@ -204,11 +204,11 @@ val router = Router[PickleType, ServerResult](MyLogHandler) ### Method overloading -When overloading methods with different parameter lists, sloth does not have a unique path (because it is derived from the trait name and the method name). Here you will need to provide your own path name: +When overloading methods with different parameter lists, sloth does not have a unique endpoint (because it is derived from the trait name and the method name). Here you will need to provide your own endpoint name: ```scala trait Api { def fun(i: Int): F[Int] - @PathName("funWithString") + @EndpointName("funWithString") def fun(i: Int, s: String): F[Int] } ``` @@ -223,27 +223,32 @@ In the above examples, we used the type `ByteBuffer` to select the serialization Sloth derives all information about an API from a scala trait. For example: ```scala -// @PathName("apiName") +// @EndpointName("apiName") trait Api { - // @PathName("funName") + // @EndpointName("funName") def fun(a: Int, b: String)(c: Double): F[Int] } ``` For each declared method in this trait (in this case `fun`): -* Calculate method path: `RequestPath("Api", "fun")` (`PathName` annotations on the trait or method are taken into account). +* Calculate method endpoint: `Endpoint("Api", "fun")` (`EndpointName` annotations on the trait or method are taken into account). * Serialize the method parameters as a tuple: `(a, b, c)`. ### Server -When calling `router.route[Api](impl)`, a macro generates a function that maps a method path and the pickled arguments to a pickled result. This basically boils down to: +When calling `router.route[Api](impl)`, a macro generates a function that maps a method endpoint and the pickled arguments to a pickled result. This basically boils down to: ```scala -HashMap("Api" -> HashMap("fun" -> { payload => - // deserialize payload - // call Api implementation impl with arguments - // return serialized response -})) +{ (endpoint: sloth.Endpoint) => + if (endpoint.apiName = "Api") endpoint.methodName match { + case "fun" => Some({ payload => + // deserialize payload + // call Api implementation impl with arguments + // return serialized response + }) + case _ => None + } else None +} ``` ### Client @@ -254,7 +259,7 @@ When calling `client.wire[Api]`, a macro generates an instance of `Api` by imple new Api { def fun(a: Int, b: String)(c: Double): F[Int] = { // serialize arguments - // call RequestTransport transport with method path and arguments + // call RequestTransport transport with method endpoint and arguments // return deserialized response } } diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index 436a9969..423de27d 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -40,11 +40,11 @@ class Translator[C <: Context](val c: C) { private def validateAllMethods(methods: List[(MethodSymbol, Type)]): List[Either[String, (MethodSymbol, Type)]] = methods.groupBy(m => methodPathPart(m._1)).map { case (_, x :: Nil) => Right(x) - case (k, _) => Left(s"""method $k is overloaded (rename the method or add a @PathName("other-name"))""") + case (k, _) => Left(s"""method $k is overloaded (rename the method or add a @EndpointName("other-name"))""") }.toList - private def findPathName(annotations: Seq[Annotation]) = annotations.reverse.map(_.tree).collectFirst { - case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.PathName] => name.toString + private def findEndpointName(annotations: Seq[Annotation]) = annotations.reverse.map(_.tree).collectFirst { + case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.EndpointName] => name.toString } private def eitherSeq[A, B](list: List[Either[A, B]]): Either[List[A], List[B]] = list.partition(_.isLeft) match { @@ -76,10 +76,10 @@ class Translator[C <: Context](val c: C) { //TODO what about fqn for trait to not have overlaps? def traitPathPart(tpe: Type): String = - findPathName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString) + findEndpointName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString) def methodPathPart(m: MethodSymbol): String = - findPathName(m.annotations).getOrElse(m.name.toString) + findEndpointName(m.annotations).getOrElse(m.name.toString) def paramAsValDef(p: Symbol): ValDef = q"val ${p.name.toTermName}: ${p.typeSignature}" def paramsAsValDefs(m: Type): List[List[ValDef]] = m.paramLists.map(_.map(paramAsValDef)) @@ -120,7 +120,7 @@ class Translator[C <: Context](val c: C) { object Translator { def apply[T](c: Context)(f: Translator[c.type] => c.Tree): c.Expr[T] = { val tree = f(new Translator(c)) -// println(tree) + println(tree) c.Expr(tree) } } diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 28eb3d51..0f9483a5 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -14,11 +14,11 @@ private implicit val toExprEndpoint: ToExpr[Endpoint] = new ToExpr[Endpoint] { } } -private def getPathName(using Quotes)(symbol: quotes.reflect.Symbol): String = { +private def getEndpointName(using Quotes)(symbol: quotes.reflect.Symbol): String = { import quotes.reflect.* symbol.annotations.collectFirst { - case Apply(Select(New(annotation), _), Literal(constant) :: Nil) if annotation.tpe =:= TypeRepr.of[PathName] => + case Apply(Select(New(annotation), _), Literal(constant) :: Nil) if annotation.tpe =:= TypeRepr.of[EndpointName] => constant.value.asInstanceOf[String] }.getOrElse(symbol.name) } @@ -87,8 +87,8 @@ def createTypeTreeTuple(using Quotes)(tupleTypesList: List[quotes.reflect.TypeRe private def checkMethodErrors[Trait: Type, Result[_]: Type](using q: Quotes)(methods: Seq[quotes.reflect.Symbol]): Unit = { import quotes.reflect.* - val duplicateErrors = methods.groupBy(getPathName).collect { case (name, symbols) if symbols.size > 1 => - val message = s"Method $name is overloaded, please rename one of the methods or use the PathName annotation to disambiguate" + val duplicateErrors = methods.groupBy(getEndpointName).collect { case (name, symbols) if symbols.size > 1 => + val message = s"Method $name is overloaded, please rename one of the methods or use the EndpointName annotation to disambiguate" (message, symbols.flatMap(_.pos).lastOption) } @@ -155,7 +155,7 @@ object TraitMacro { val methods = definedMethodsInType[Trait] checkMethodErrors[Trait, Result](methods) - val traitPathPart = getPathName(TypeRepr.of[Trait].typeSymbol) + val traitPathPart = getEndpointName(TypeRepr.of[Trait].typeSymbol) def decls(cls: Symbol): List[Symbol] = methods.map { method => val methodType = TypeRepr.of[Trait].memberType(method) @@ -167,7 +167,7 @@ object TraitMacro { val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => val body = (cls.declaredMethods.zip(methods)).map { case (method, origMethod) => - val methodPathPart = getPathName(origMethod) + val methodPathPart = getEndpointName(origMethod) val path = Endpoint(traitPathPart, methodPathPart) val pathExpr = Expr(path) @@ -221,7 +221,7 @@ object TraitMacro { Block(List(clsDef), newCls) } -// println(result.show) + println(result.show) result.asExprOf[Trait] } } @@ -244,14 +244,14 @@ object RouterMacro { val methods = definedMethodsInType[Trait] checkMethodErrors[Trait, Result](methods) - val traitPathPart = getPathName(TypeRepr.of[Trait].typeSymbol) + val traitPathPart = getEndpointName(TypeRepr.of[Trait].typeSymbol) type FunctionInput = PickleType type FunctionOutput = Either[ServerFailure, Result[PickleType]] val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => def methodCases(endpointTerm: Term) = methods.map { method => - val methodPathPart = getPathName(method) + val methodPathPart = getEndpointName(method) val path = Endpoint(traitPathPart, methodPathPart) val returnType = getInnerTypeOutOfReturnType[Trait, Result](method) @@ -328,7 +328,7 @@ object RouterMacro { }.asTerm } -// println(result.show) + println(result.show) result.asExprOf[Router[PickleType, Result]] } } diff --git a/sloth/src/main/scala/Annotations.scala b/sloth/src/main/scala/Annotations.scala index d935ecf8..0be7fbff 100644 --- a/sloth/src/main/scala/Annotations.scala +++ b/sloth/src/main/scala/Annotations.scala @@ -2,4 +2,4 @@ package sloth import scala.annotation.StaticAnnotation -class PathName(val name: String) extends StaticAnnotation +class EndpointName(val name: String) extends StaticAnnotation diff --git a/sloth/src/main/scala/package.scala b/sloth/src/main/scala/package.scala new file mode 100644 index 00000000..92f9a6d2 --- /dev/null +++ b/sloth/src/main/scala/package.scala @@ -0,0 +1,4 @@ +package object sloth { + @deprecated("Use EndpointName instead", "0.8.0") + type PathName = EndpointName +} diff --git a/sloth/src/test/scala/SlothSpec.scala b/sloth/src/test/scala/SlothSpec.scala index 24bbee4a..cac155c0 100644 --- a/sloth/src/test/scala/SlothSpec.scala +++ b/sloth/src/test/scala/SlothSpec.scala @@ -23,7 +23,7 @@ trait SingleApi { // def bum(a: Int): Option[Int] // should not compile because of generic parameter // def bom[T](a: T): Future[Int] - @PathName("kanone") // does not compile without PathName, because overloaded + @EndpointName("kanone") // does not compile without EndpointName, because overloaded def foo: Future[Int] } object SingleApiImpl extends SingleApi { From 6ec330a2fd5fc881c11df09fb6c0e6ae7c9122d9 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 19:55:08 +0200 Subject: [PATCH 04/14] Update sloth/src/main/scala-3/internal/Macros.scala --- sloth/src/main/scala-3/internal/Macros.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 0f9483a5..594591ca 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -221,7 +221,7 @@ object TraitMacro { Block(List(clsDef), newCls) } - println(result.show) + // println(result.show) result.asExprOf[Trait] } } From d4d2c61010b1ae4271a37383f38f7a3732a100c7 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 19:55:13 +0200 Subject: [PATCH 05/14] Update sloth/src/main/scala-3/internal/Macros.scala --- sloth/src/main/scala-3/internal/Macros.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 594591ca..286e61c5 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -328,7 +328,7 @@ object RouterMacro { }.asTerm } - println(result.show) + // println(result.show) result.asExprOf[Router[PickleType, Result]] } } From 47415b1d458d470c90dffe3016b1ee3f2acb9a1a Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 19:55:18 +0200 Subject: [PATCH 06/14] Update sloth/src/main/scala-2/internal/Macros.scala --- sloth/src/main/scala-2/internal/Macros.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index 423de27d..ebfa6e29 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -120,7 +120,7 @@ class Translator[C <: Context](val c: C) { object Translator { def apply[T](c: Context)(f: Translator[c.type] => c.Tree): c.Expr[T] = { val tree = f(new Translator(c)) - println(tree) + // println(tree) c.Expr(tree) } } From b04029c83ad8cc69feb27c5d3bbea3d370fb17ca Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Mon, 24 Jun 2024 19:55:53 +0200 Subject: [PATCH 07/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05d67120..740e34ce 100644 --- a/README.md +++ b/README.md @@ -240,7 +240,7 @@ When calling `router.route[Api](impl)`, a macro generates a function that maps a ```scala { (endpoint: sloth.Endpoint) => - if (endpoint.apiName = "Api") endpoint.methodName match { + if (endpoint.apiName == "Api") endpoint.methodName match { case "fun" => Some({ payload => // deserialize payload // call Api implementation impl with arguments From 37d1a4c232d7f24fb4a5a655faf0130b3bdad6dc Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 19:21:45 +0200 Subject: [PATCH 08/14] rename endpoint to method --- README.md | 24 ++++++------ .../src/main/scala/HttpRpcTransport.scala | 19 +++++----- .../src/main/scala/HttpRpcRoutes.scala | 25 ++++++------ .../src/main/scala/HttpRpcTransport.scala | 2 +- sloth/src/main/scala-2/internal/Macros.scala | 26 ++++++------- sloth/src/main/scala-3/internal/Macros.scala | 38 +++++++++---------- sloth/src/main/scala/Annotations.scala | 2 +- sloth/src/main/scala/Failures.scala | 10 ++--- sloth/src/main/scala/LogHandler.scala | 4 +- sloth/src/main/scala/Request.scala | 18 ++++----- sloth/src/main/scala/Router.scala | 10 ++--- sloth/src/main/scala/internal/Impls.scala | 8 ++-- sloth/src/main/scala/package.scala | 4 +- sloth/src/test/scala/ImplsSpec.scala | 16 ++++---- sloth/src/test/scala/SlothSpec.scala | 2 +- 15 files changed, 103 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index 740e34ce..60153cb5 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ val router = Router[ByteBuffer, Future].route[Api](ApiImpl) Use it to route requests to your Api implementation: ```scala -val result = router(Request[ByteBuffer](RequestPath(apiName = "Api", methodName = "fun"), bytes)) +val result = router(Request[ByteBuffer](Method(apiName = "Api", methodName = "fun"), bytes)) // Now result contains the serialized Int result returned by the method ApiImpl.fun ``` @@ -187,7 +187,7 @@ For logging, you can define a `LogHandler`, which can log each request including Define it when creating the `Client`: ```scala object MyLogHandler extends LogHandler[ClientResult[_]] { - def logRequest[T](endpoint: Endpoint, argumentObject: Any, result: ClientResult[T]): ClientResult[T] = ??? + def logRequest[T](method: Method, argumentObject: Any, result: ClientResult[T]): ClientResult[T] = ??? } val client = Client[PickleType, ClientResult](Transport, MyLogHandler) @@ -196,7 +196,7 @@ val client = Client[PickleType, ClientResult](Transport, MyLogHandler) Define it when creating the `Router`: ```scala object MyLogHandler extends LogHandler[ServerResult[_]] { - def logRequest[T](endpoint: Endpoint, argumentObject: Any, result: ServerResult[T]): ServerResult[T] = ??? + def logRequest[T](method: Method, argumentObject: Any, result: ServerResult[T]): ServerResult[T] = ??? } val router = Router[PickleType, ServerResult](MyLogHandler) @@ -204,11 +204,11 @@ val router = Router[PickleType, ServerResult](MyLogHandler) ### Method overloading -When overloading methods with different parameter lists, sloth does not have a unique endpoint (because it is derived from the trait name and the method name). Here you will need to provide your own endpoint name: +When overloading methods with different parameter lists, sloth cannot have uniquely identify the method (because it is referenced with the trait name and the method name). Here you will need to provide a custom name: ```scala trait Api { def fun(i: Int): F[Int] - @EndpointName("funWithString") + @Name("funWithString") def fun(i: Int, s: String): F[Int] } ``` @@ -223,24 +223,24 @@ In the above examples, we used the type `ByteBuffer` to select the serialization Sloth derives all information about an API from a scala trait. For example: ```scala -// @EndpointName("apiName") +// @Name("apiName") trait Api { - // @EndpointName("funName") + // @Name("funName") def fun(a: Int, b: String)(c: Double): F[Int] } ``` For each declared method in this trait (in this case `fun`): -* Calculate method endpoint: `Endpoint("Api", "fun")` (`EndpointName` annotations on the trait or method are taken into account). +* Calculate method method: `Method("Api", "fun")` (`Name` annotations on the trait or method are taken into account). * Serialize the method parameters as a tuple: `(a, b, c)`. ### Server -When calling `router.route[Api](impl)`, a macro generates a function that maps a method endpoint and the pickled arguments to a pickled result. This basically boils down to: +When calling `router.route[Api](impl)`, a macro generates a function that maps a method method and the pickled arguments to a pickled result. This basically boils down to: ```scala -{ (endpoint: sloth.Endpoint) => - if (endpoint.apiName == "Api") endpoint.methodName match { +{ (method: sloth.Method) => + if (method.apiName = "Api") method.methodName match { case "fun" => Some({ payload => // deserialize payload // call Api implementation impl with arguments @@ -259,7 +259,7 @@ When calling `client.wire[Api]`, a macro generates an instance of `Api` by imple new Api { def fun(a: Int, b: String)(c: Double): F[Int] = { // serialize arguments - // call RequestTransport transport with method endpoint and arguments + // call RequestTransport transport with method method and arguments // return deserialized response } } diff --git a/http4sClient/src/main/scala/HttpRpcTransport.scala b/http4sClient/src/main/scala/HttpRpcTransport.scala index afd5df85..58e354e6 100644 --- a/http4sClient/src/main/scala/HttpRpcTransport.scala +++ b/http4sClient/src/main/scala/HttpRpcTransport.scala @@ -5,16 +5,15 @@ import cats.implicits._ import org.http4s.client.Client import org.http4s.{EntityBody, EntityDecoder, EntityEncoder, Headers, HttpVersion, Method, Request, ServerSentEvent, Uri} import fs2.Stream -import sloth.{Endpoint, RequestTransport} case class HttpRequestConfig( baseUri: Uri = Uri(path = Uri.Path.Root), headers: Headers = Headers.empty, httpVersion: HttpVersion = HttpVersion.`HTTP/1.1`, ) { - def toRequest[F[_]](endpoint: Endpoint, entityBody: EntityBody[F]): Request[F] = Request[F]( + def toRequest[F[_]](method: sloth.Method, entityBody: EntityBody[F]): Request[F] = Request[F]( method = Method.POST, - uri = baseUri / endpoint.apiName / endpoint.methodName, + uri = baseUri / method.apiName / method.methodName, httpVersion = httpVersion, headers = headers, body = entityBody, @@ -27,7 +26,7 @@ object HttpRpcTransport { )(implicit encoder: EntityEncoder[F, PickleType], decoder: EntityDecoder[F, PickleType] - ): RequestTransport[PickleType, F] = apply(client, HttpRequestConfig().pure[F]) + ): sloth.RequestTransport[PickleType, F] = apply(client, HttpRequestConfig().pure[F]) def apply[PickleType, F[_]: Concurrent]( client: Client[F], @@ -35,26 +34,26 @@ object HttpRpcTransport { )(implicit encoder: EntityEncoder[F, PickleType], decoder: EntityDecoder[F, PickleType] - ): RequestTransport[PickleType, F] = new sloth.RequestTransport[PickleType, F] { + ): sloth.RequestTransport[PickleType, F] = new sloth.RequestTransport[PickleType, F] { override def apply(request: sloth.Request[PickleType]): F[PickleType] = for { config <- config - responseBody <- client.expect[PickleType](config.toRequest(request.endpoint, encoder.toEntity(request.payload).body)) + responseBody <- client.expect[PickleType](config.toRequest(request.method, encoder.toEntity(request.payload).body)) } yield responseBody } def eventStream[F[_]: Concurrent]( client: Client[F], - ): RequestTransport[String, Stream[F, *]] = eventStream(client, HttpRequestConfig().pure[F]) + ): sloth.RequestTransport[String, Stream[F, *]] = eventStream(client, HttpRequestConfig().pure[F]) def eventStream[F[_]: Concurrent]( client: Client[F], config: F[HttpRequestConfig] - ): RequestTransport[String, Stream[F, *]] = new sloth.RequestTransport[String, Stream[F, *]] { + ): sloth.RequestTransport[String, Stream[F, *]] = new sloth.RequestTransport[String, Stream[F, *]] { override def apply(request: sloth.Request[String]): Stream[F, String] = for { config <- Stream.eval(config) - response <- Stream.resource(client.run(config.toRequest(request.endpoint, EntityEncoder[F, String].toEntity(request.payload).body))) + response <- Stream.resource(client.run(config.toRequest(request.method, EntityEncoder[F, String].toEntity(request.payload).body))) event <- response.body.through(ServerSentEvent.decoder[F]) data <- Stream.fromOption(event.data) } yield data } -} \ No newline at end of file +} diff --git a/http4sServer/src/main/scala/HttpRpcRoutes.scala b/http4sServer/src/main/scala/HttpRpcRoutes.scala index d9ef4cc0..da3b6e51 100644 --- a/http4sServer/src/main/scala/HttpRpcRoutes.scala +++ b/http4sServer/src/main/scala/HttpRpcRoutes.scala @@ -6,17 +6,16 @@ import cats.effect.Concurrent import org.http4s._ import org.http4s.dsl.Http4sDsl import fs2.Stream -import sloth.{Endpoint, Router, ServerFailure} object HttpRpcRoutes { def apply[PickleType: EntityDecoder[F, *]: EntityEncoder[F, *], F[_]: Concurrent]( - router: Router[PickleType, F], + router: sloth.Router[PickleType, F], onError: PartialFunction[Throwable, F[Response[F]]] = PartialFunction.empty ): HttpRoutes[F] = withRequest[PickleType, F](_ => router, onError) def withRequest[PickleType: EntityDecoder[F, *]: EntityEncoder[F, *], F[_]: Concurrent]( - router: Request[F] => Router[PickleType, F], + router: Request[F] => sloth.Router[PickleType, F], onError: PartialFunction[Throwable, F[Response[F]]] = PartialFunction.empty ): HttpRoutes[F] = { val dsl = Http4sDsl[F] @@ -25,8 +24,8 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { case Vector(apiName, methodName) => - val endpoint = Endpoint(apiName.decoded(), methodName.decoded()) - val result = router(request).get(endpoint).traverse { f => + val method = sloth.Method(apiName.decoded(), methodName.decoded()) + val result = router(request).get(method).traverse { f => request.as[PickleType].flatMap { payload => f(payload) match { case Left(error) => serverFailureToResponse[F](dsl, onError)(error) @@ -42,12 +41,12 @@ object HttpRpcRoutes { } def eventStream[F[_]: Concurrent]( - router: Router[String, Stream[F, *]], + router: sloth.Router[String, Stream[F, *]], onError: PartialFunction[Throwable, F[Response[F]]] = PartialFunction.empty ): HttpRoutes[F] = eventStreamWithRequest[F](_ => router, onError) def eventStreamWithRequest[F[_]: Concurrent]( - router: Request[F] => Router[String, Stream[F, *]], + router: Request[F] => sloth.Router[String, Stream[F, *]], onError: PartialFunction[Throwable, F[Response[F]]] = PartialFunction.empty ): HttpRoutes[F] = { val dsl = Http4sDsl[F] @@ -56,8 +55,8 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { case Vector(apiName, methodName) => - val endpoint = Endpoint(apiName.decoded(), methodName.decoded()) - val result = router(request).get(endpoint).traverse { f => + val method = sloth.Method(apiName.decoded(), methodName.decoded()) + val result = router(request).get(method).traverse { f => request.as[String].flatMap { payload => f(payload) match { case Left(error) => serverFailureToResponse[F](dsl, onError)(error) @@ -72,12 +71,12 @@ object HttpRpcRoutes { } } - private def serverFailureToResponse[F[_]: Concurrent](dsl: Http4sDsl[F], onError: PartialFunction[Throwable, F[Response[F]]])(failure: ServerFailure): F[Response[F]] = { + private def serverFailureToResponse[F[_]: Concurrent](dsl: Http4sDsl[F], onError: PartialFunction[Throwable, F[Response[F]]])(failure: sloth.ServerFailure): F[Response[F]] = { import dsl._ failure match { - case ServerFailure.EndpointNotFound(_) => NotFound() - case ServerFailure.HandlerError(err) => onError.lift(err).getOrElse(InternalServerError(err.getMessage)) - case ServerFailure.DeserializerError(err) => onError.lift(err).getOrElse(BadRequest(err.getMessage)) + case sloth.ServerFailure.MethodNotFound(_) => NotFound() + case sloth.ServerFailure.HandlerError(err) => onError.lift(err).getOrElse(InternalServerError(err.getMessage)) + case sloth.ServerFailure.DeserializerError(err) => onError.lift(err).getOrElse(BadRequest(err.getMessage)) } } } diff --git a/jsdomClient/src/main/scala/HttpRpcTransport.scala b/jsdomClient/src/main/scala/HttpRpcTransport.scala index be19f3bc..c565e2ee 100644 --- a/jsdomClient/src/main/scala/HttpRpcTransport.scala +++ b/jsdomClient/src/main/scala/HttpRpcTransport.scala @@ -19,7 +19,7 @@ object HttpRpcTransport { def apply[F[_]: Async](config: F[HttpRequestConfig]): RequestTransport[String, F] = new RequestTransport[String, F] { override def apply(request: Request[String]): F[String] = for { config <- config - url = s"${config.baseUri}/${request.endpoint.apiName}/${request.endpoint.methodName}" + url = s"${config.baseUri}/${request.method.apiName}/${request.method.methodName}" requestArgs = new dom.RequestInit { headers = config.headers.toJSDictionary; method = dom.HttpMethod.POST; body = request.payload } result <- Async[F].fromThenable(Async[F].delay[js.Thenable[String]](dom.fetch(url, requestArgs).`then`[String](_.text()))) } yield result diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index ebfa6e29..c0f1951f 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -1,6 +1,6 @@ package sloth.internal -import sloth.Endpoint +import sloth.Method import scala.reflect.macros.blackbox.Context @@ -19,8 +19,8 @@ class Translator[C <: Context](val c: C) { import Validator._ object implicits { - implicit val liftEndpoint: Liftable[Endpoint] = - Liftable[Endpoint]{ r => q"new _root_.sloth.Endpoint(${r.apiName}, ${r.methodName})" } + implicit val liftMethod: Liftable[Method] = + Liftable[Method]{ r => q"new _root_.sloth.Method(${r.apiName}, ${r.methodName})" } } def abort(msg: String) = c.abort(c.enclosingPosition, msg) @@ -40,11 +40,11 @@ class Translator[C <: Context](val c: C) { private def validateAllMethods(methods: List[(MethodSymbol, Type)]): List[Either[String, (MethodSymbol, Type)]] = methods.groupBy(m => methodPathPart(m._1)).map { case (_, x :: Nil) => Right(x) - case (k, _) => Left(s"""method $k is overloaded (rename the method or add a @EndpointName("other-name"))""") + case (k, _) => Left(s"""Method $k is overloaded, please rename one of the methods or use the @Name("other-name") annotation to disambiguate""") }.toList - private def findEndpointName(annotations: Seq[Annotation]) = annotations.reverse.map(_.tree).collectFirst { - case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.EndpointName] => name.toString + private def findCustomName(annotations: Seq[Annotation]) = annotations.reverse.map(_.tree).collectFirst { + case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.Name] => name.toString } private def eitherSeq[A, B](list: List[Either[A, B]]): Either[List[A], List[B]] = list.partition(_.isLeft) match { @@ -76,10 +76,10 @@ class Translator[C <: Context](val c: C) { //TODO what about fqn for trait to not have overlaps? def traitPathPart(tpe: Type): String = - findEndpointName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString) + findCustomName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString) def methodPathPart(m: MethodSymbol): String = - findEndpointName(m.annotations).getOrElse(m.name.toString) + findCustomName(m.annotations).getOrElse(m.name.toString) def paramAsValDef(p: Symbol): ValDef = q"val ${p.name.toTermName}: ${p.typeSignature}" def paramsAsValDefs(m: Type): List[List[ValDef]] = m.paramLists.map(_.map(paramAsValDef)) @@ -138,7 +138,7 @@ object TraitMacro { val traitPathPart = t.traitPathPart(traitTag.tpe) val methodImplList = validMethods.collect { case (symbol, method) => val methodPathPart = t.methodPathPart(symbol) - val path = Endpoint(traitPathPart, methodPathPart) + val path = Method(traitPathPart, methodPathPart) val parameters = t.paramsAsValDefs(method) val paramsType = t.paramsType(method) val paramListValue = t.wrapAsParamsType(method) @@ -196,7 +196,7 @@ object RouterMacro { val argParams = t.objectToParams(method, TermName("args")) val innerReturnType = t.getInnerTypeOutOfReturnType(resultTag.tpe, method.finalResultType) val payloadFunction = - q"""(payload: ${pickleTypeTag.tpe}) => impl.execute[${paramsType}, $innerReturnType](endpoint, payload) { args => + q"""(payload: ${pickleTypeTag.tpe}) => impl.execute[${paramsType}, $innerReturnType](method, payload) { args => value.${symbol.name.toTermName}(...$argParams) }""" @@ -208,9 +208,9 @@ object RouterMacro { val implRouter = ${c.prefix} val impl = $impl - implRouter.orElse { endpoint => - if (endpoint.apiName == $traitPathPart) { - endpoint.methodName match { + implRouter.orElse { method => + if (method.apiName == $traitPathPart) { + method.methodName match { case ..$methodCases case _ => None } diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 286e61c5..0570f9ad 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -7,18 +7,18 @@ import scala.annotation.meta.param import scala.NonEmptyTuple import scala.quoted.runtime.StopMacroExpansion -private implicit val toExprEndpoint: ToExpr[Endpoint] = new ToExpr[Endpoint] { - def apply(path: Endpoint)(using Quotes): Expr[Endpoint] = { +private implicit val toExprMethod: ToExpr[Method] = new ToExpr[Method] { + def apply(path: Method)(using Quotes): Expr[Method] = { import quotes.reflect._ - '{ Endpoint(${Expr(path.apiName)}, ${Expr(path.methodName)}) } + '{ Method(${Expr(path.apiName)}, ${Expr(path.methodName)}) } } } -private def getEndpointName(using Quotes)(symbol: quotes.reflect.Symbol): String = { +private def getCustomName(using Quotes)(symbol: quotes.reflect.Symbol): String = { import quotes.reflect.* symbol.annotations.collectFirst { - case Apply(Select(New(annotation), _), Literal(constant) :: Nil) if annotation.tpe =:= TypeRepr.of[EndpointName] => + case Apply(Select(New(annotation), _), Literal(constant) :: Nil) if annotation.tpe =:= TypeRepr.of[sloth.Name] => constant.value.asInstanceOf[String] }.getOrElse(symbol.name) } @@ -87,8 +87,8 @@ def createTypeTreeTuple(using Quotes)(tupleTypesList: List[quotes.reflect.TypeRe private def checkMethodErrors[Trait: Type, Result[_]: Type](using q: Quotes)(methods: Seq[quotes.reflect.Symbol]): Unit = { import quotes.reflect.* - val duplicateErrors = methods.groupBy(getEndpointName).collect { case (name, symbols) if symbols.size > 1 => - val message = s"Method $name is overloaded, please rename one of the methods or use the EndpointName annotation to disambiguate" + val duplicateErrors = methods.groupBy(getCustomName).collect { case (name, symbols) if symbols.size > 1 => + val message = s"""Method $name is overloaded, please rename one of the methods or use the @Name("other-name") annotation to disambiguate""" (message, symbols.flatMap(_.pos).lastOption) } @@ -155,7 +155,7 @@ object TraitMacro { val methods = definedMethodsInType[Trait] checkMethodErrors[Trait, Result](methods) - val traitPathPart = getEndpointName(TypeRepr.of[Trait].typeSymbol) + val traitPathPart = getCustomName(TypeRepr.of[Trait].typeSymbol) def decls(cls: Symbol): List[Symbol] = methods.map { method => val methodType = TypeRepr.of[Trait].memberType(method) @@ -167,8 +167,8 @@ object TraitMacro { val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => val body = (cls.declaredMethods.zip(methods)).map { case (method, origMethod) => - val methodPathPart = getEndpointName(origMethod) - val path = Endpoint(traitPathPart, methodPathPart) + val methodPathPart = getCustomName(origMethod) + val path = Method(traitPathPart, methodPathPart) val pathExpr = Expr(path) DefDef(method, { argss => @@ -244,15 +244,15 @@ object RouterMacro { val methods = definedMethodsInType[Trait] checkMethodErrors[Trait, Result](methods) - val traitPathPart = getEndpointName(TypeRepr.of[Trait].typeSymbol) + val traitPathPart = getCustomName(TypeRepr.of[Trait].typeSymbol) type FunctionInput = PickleType type FunctionOutput = Either[ServerFailure, Result[PickleType]] val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef => - def methodCases(endpointTerm: Term) = methods.map { method => - val methodPathPart = getEndpointName(method) - val path = Endpoint(traitPathPart, methodPathPart) + def methodCases(methodTerm: Term) = methods.map { method => + val methodPathPart = getCustomName(method) + val path = Method(traitPathPart, methodPathPart) val returnType = getInnerTypeOutOfReturnType[Trait, Result](method) @@ -306,7 +306,7 @@ object RouterMacro { Select(implRef, routerImplType.declaredMethod("execute").head), List(tupleTypeTree, returnTypeTree) ), - List(endpointTerm, payloadArg.asExpr.asTerm) + List(methodTerm, payloadArg.asExpr.asTerm) ), List(instanceLambda) ) @@ -317,11 +317,11 @@ object RouterMacro { } '{ - ${prefix}.orElse { endpoint => - if (endpoint.apiName == ${Expr(traitPathPart)}) { + ${prefix}.orElse { method => + if (method.apiName == ${Expr(traitPathPart)}) { ${Match( - '{endpoint.methodName}.asTerm, - methodCases('{endpoint}.asTerm) :+ CaseDef(Wildcard(), None, '{ None }.asTerm) + '{method.methodName}.asTerm, + methodCases('{method}.asTerm) :+ CaseDef(Wildcard(), None, '{ None }.asTerm) ).asExprOf[Option[FunctionInput => FunctionOutput]]} } else None } diff --git a/sloth/src/main/scala/Annotations.scala b/sloth/src/main/scala/Annotations.scala index 0be7fbff..2c70fe10 100644 --- a/sloth/src/main/scala/Annotations.scala +++ b/sloth/src/main/scala/Annotations.scala @@ -2,4 +2,4 @@ package sloth import scala.annotation.StaticAnnotation -class EndpointName(val name: String) extends StaticAnnotation +class Name(val name: String) extends StaticAnnotation diff --git a/sloth/src/main/scala/Failures.scala b/sloth/src/main/scala/Failures.scala index eadcc333..6bf69cf2 100644 --- a/sloth/src/main/scala/Failures.scala +++ b/sloth/src/main/scala/Failures.scala @@ -4,14 +4,14 @@ sealed trait ServerFailure { def toException = ServerException(this) } object ServerFailure { - case class EndpointNotFound(path: Endpoint) extends ServerFailure + case class MethodNotFound(path: Method) extends ServerFailure case class HandlerError(ex: Throwable) extends ServerFailure case class DeserializerError(ex: Throwable) extends ServerFailure - @deprecated("Use EndpointNotFound instead", "0.8.0") - val PathNotFound = EndpointNotFound - @deprecated("Use EndpointNotFound instead", "0.8.0") - type PathNotFound = EndpointNotFound + @deprecated("Use MethodNotFound instead", "0.8.0") + val PathNotFound = MethodNotFound + @deprecated("Use MethodNotFound instead", "0.8.0") + type PathNotFound = MethodNotFound } case class ServerException(failure: ServerFailure) extends Exception(failure.toString) diff --git a/sloth/src/main/scala/LogHandler.scala b/sloth/src/main/scala/LogHandler.scala index 084e0ffe..93a1c819 100644 --- a/sloth/src/main/scala/LogHandler.scala +++ b/sloth/src/main/scala/LogHandler.scala @@ -1,10 +1,10 @@ package sloth trait LogHandler[Result[_]] { - def logRequest[A, T](endpoint: Endpoint, argumentObject: A, result: Result[T]): Result[T] + def logRequest[A, T](method: Method, argumentObject: A, result: Result[T]): Result[T] } object LogHandler { def empty[Result[_]]: LogHandler[Result] = new LogHandler[Result] { - def logRequest[A, T](endpoint: Endpoint, argumentObject: A, result: Result[T]): Result[T] = result + def logRequest[A, T](method: Method, argumentObject: A, result: Result[T]): Result[T] = result } } diff --git a/sloth/src/main/scala/Request.scala b/sloth/src/main/scala/Request.scala index 987484de..5587ae95 100644 --- a/sloth/src/main/scala/Request.scala +++ b/sloth/src/main/scala/Request.scala @@ -1,17 +1,17 @@ package sloth -case class Endpoint(apiName: String, methodName: String) +case class Method(apiName: String, methodName: String) -case class Request[T](endpoint: Endpoint, payload: T) { - @deprecated("Use .endpoint instead", "0.8.0") - def path: List[String] = List(endpoint.apiName, endpoint.methodName) +case class Request[T](method: Method, payload: T) { + @deprecated("Use .method instead", "0.8.0") + def path: List[String] = List(method.apiName, method.methodName) } object Request { - @deprecated("""Use Request(Endpoint("Api", "method"), payload) instead""", "0.8.0") - def apply[T](path: List[String], payload: T): Request[T] = Request(endpointFromList(path), payload) + @deprecated("""Use Request(Method("Api", "method"), payload) instead""", "0.8.0") + def apply[T](path: List[String], payload: T): Request[T] = Request(methodFromList(path), payload) - private[sloth] def endpointFromList(path: List[String]) = path match { - case List(traitName, methodName) => Endpoint(apiName = traitName, methodName = methodName) - case _ => Endpoint("", "") + private[sloth] def methodFromList(path: List[String]) = path match { + case List(traitName, methodName) => Method(apiName = traitName, methodName = methodName) + case _ => Method("", "") } } diff --git a/sloth/src/main/scala/Router.scala b/sloth/src/main/scala/Router.scala index b6750f7c..d850d6c5 100644 --- a/sloth/src/main/scala/Router.scala +++ b/sloth/src/main/scala/Router.scala @@ -10,13 +10,13 @@ trait Router[PickleType, Result[_]] { val get: Router.ApiMapping[PickleType, Result] def apply(request: Request[PickleType]): Either[ServerFailure, Result[PickleType]] = - get(request.endpoint) match { + get(request.method) match { case Some(function) => function(request.payload) - case None => Left(ServerFailure.EndpointNotFound(request.endpoint)) + case None => Left(ServerFailure.MethodNotFound(request.method)) } - @deprecated("Use get(endpoint) instead", "0.8.0") - def getFunction(path: List[String]): Option[PickleType => Either[ServerFailure, Result[PickleType]]] = get(Request.endpointFromList(path)) + @deprecated("Use get(method) instead", "0.8.0") + def getFunction(path: List[String]): Option[PickleType => Either[ServerFailure, Result[PickleType]]] = get(Request.methodFromList(path)) def orElse(collect: Router.ApiMapping[PickleType, Result]): Router[PickleType, Result] } @@ -40,7 +40,7 @@ class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[ } object Router { - type ApiMapping[PickleType, Result[_]] = Endpoint => Option[PickleType => Either[ServerFailure, Result[PickleType]]] + type ApiMapping[PickleType, Result[_]] = Method => Option[PickleType => Either[ServerFailure, Result[PickleType]]] private val emptyApiMapping: Any => None.type = (_: Any) => None def apply[PickleType, Result[_]: Functor]: RouterCo[PickleType, Result] = apply(LogHandler.empty[Result]) diff --git a/sloth/src/main/scala/internal/Impls.scala b/sloth/src/main/scala/internal/Impls.scala index 21b8e0dc..96edc307 100644 --- a/sloth/src/main/scala/internal/Impls.scala +++ b/sloth/src/main/scala/internal/Impls.scala @@ -6,7 +6,7 @@ import chameleon.{Serializer, Deserializer} import scala.util.{Success, Failure, Try} class RouterImpl[PickleType, Result[_]](router: RouterCo[PickleType, Result]) { - def execute[T, R](path: Endpoint, arguments: PickleType)(call: T => Result[R])(implicit deserializer: Deserializer[T, PickleType], serializer: Serializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { + def execute[T, R](path: Method, arguments: PickleType)(call: T => Result[R])(implicit deserializer: Deserializer[T, PickleType], serializer: Serializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { deserializer.deserialize(arguments) match { case Right(arguments) => Try(call(arguments)) match { @@ -21,7 +21,7 @@ class RouterImpl[PickleType, Result[_]](router: RouterCo[PickleType, Result]) { } class RouterContraImpl[PickleType, Result[_]](router: RouterContra[PickleType, Result]) { - def execute[T, R](path: Endpoint, arguments: PickleType)(call: T => Result[R])(implicit deserializerT: Deserializer[T, PickleType], deserializerR: Deserializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { + def execute[T, R](path: Method, arguments: PickleType)(call: T => Result[R])(implicit deserializerT: Deserializer[T, PickleType], deserializerR: Deserializer[R, PickleType]): Either[ServerFailure, Result[PickleType]] = { deserializerT.deserialize(arguments) match { case Right(arguments) => Try(call(arguments)) match { @@ -39,7 +39,7 @@ class RouterContraImpl[PickleType, Result[_]](router: RouterContra[PickleType, R class ClientImpl[PickleType, Result[_]](client: ClientCo[PickleType, Result]) { - def execute[T, R](path: Endpoint, arguments: T)(implicit deserializer: Deserializer[R, PickleType], serializer: Serializer[T, PickleType]): Result[R] = { + def execute[T, R](path: Method, arguments: T)(implicit deserializer: Deserializer[R, PickleType], serializer: Serializer[T, PickleType]): Result[R] = { val serializedArguments = serializer.serialize(arguments) val request: Request[PickleType] = Request(path, serializedArguments) val result: Result[R] = Try(client.transport(request)) match { @@ -58,7 +58,7 @@ class ClientImpl[PickleType, Result[_]](client: ClientCo[PickleType, Result]) { class ClientContraImpl[PickleType, Result[_]](client: ClientContra[PickleType, Result]) { - def execute[T, R](path: Endpoint, arguments: T)(implicit serializerR: Serializer[R, PickleType], serializerT: Serializer[T, PickleType]): Result[R] = { + def execute[T, R](path: Method, arguments: T)(implicit serializerR: Serializer[R, PickleType], serializerT: Serializer[T, PickleType]): Result[R] = { val serializedArguments = serializerT.serialize(arguments) val request: Request[PickleType] = Request(path, serializedArguments) val result: Result[R] = Try(client.transport(request)) match { diff --git a/sloth/src/main/scala/package.scala b/sloth/src/main/scala/package.scala index 92f9a6d2..7209df8c 100644 --- a/sloth/src/main/scala/package.scala +++ b/sloth/src/main/scala/package.scala @@ -1,4 +1,4 @@ package object sloth { - @deprecated("Use EndpointName instead", "0.8.0") - type PathName = EndpointName + @deprecated("Use Name instead", "0.8.0") + type PathName = Name } diff --git a/sloth/src/test/scala/ImplsSpec.scala b/sloth/src/test/scala/ImplsSpec.scala index 6d0ebda3..0b3a84cc 100644 --- a/sloth/src/test/scala/ImplsSpec.scala +++ b/sloth/src/test/scala/ImplsSpec.scala @@ -21,7 +21,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val resultValue = "\"Argument(1)\"" - val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput)(_.toString) + val result = impl.execute[Argument, String](Method("eine", "methode"), pickledInput)(_.toString) result mustEqual Right(resultValue) } @@ -33,7 +33,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val exception = new Exception("meh") - val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput)(_ => throw exception) + val result = impl.execute[Argument, String](Method("eine", "methode"), pickledInput)(_ => throw exception) result mustEqual Left(ServerFailure.HandlerError(exception)) } @@ -51,7 +51,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val receivedParameters = collection.mutable.ArrayBuffer.empty[Argument] val receivedStrings = collection.mutable.ArrayBuffer.empty[String] - val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput) { argument => + val result = impl.execute[Argument, String](Method("eine", "methode"), pickledInput) { argument => receivedParameters += argument { string => @@ -73,7 +73,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val argument = Argument(1) val pickledInput = Serializer[Argument, PickleType].serialize(argument) val exception = new Exception("meh") - val result = impl.execute[Argument, String](Endpoint("eine", "methode"), pickledInput)(_ => throw exception) + val result = impl.execute[Argument, String](Method("eine", "methode"), pickledInput)(_ => throw exception) result mustEqual Left(ServerFailure.HandlerError(exception)) } @@ -90,7 +90,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientImpl(client) val argument = Argument(1) - val result = impl.execute[Argument, Argument](Endpoint("api", "f"), argument) + val result = impl.execute[Argument, Argument](Method("api", "f"), argument) result mustEqual Right(argument) } @@ -102,7 +102,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientImpl(client) val argument = Argument(1) - val result = impl.execute[Argument, Double](Endpoint("api", "f"), argument) + val result = impl.execute[Argument, Double](Method("api", "f"), argument) result mustEqual Left(ClientFailure.TransportError(exception)) } @@ -125,7 +125,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientContraImpl(client) val argument = Argument(1) - val resultf = impl.execute[Argument, Argument](Endpoint("api", "f"), argument) + val resultf = impl.execute[Argument, Argument](Method("api", "f"), argument) resultf(Argument(2)) mustEqual Right(()) @@ -140,7 +140,7 @@ class ImplsSpec extends AnyFreeSpec with Matchers { val impl = new ClientContraImpl(client) val argument = Argument(1) - val resultf = impl.execute[Argument, Double](Endpoint("api", "f"), argument) + val resultf = impl.execute[Argument, Double](Method("api", "f"), argument) resultf(2.0) mustEqual Left(ClientFailure.TransportError(exception)) } diff --git a/sloth/src/test/scala/SlothSpec.scala b/sloth/src/test/scala/SlothSpec.scala index cac155c0..b77c891a 100644 --- a/sloth/src/test/scala/SlothSpec.scala +++ b/sloth/src/test/scala/SlothSpec.scala @@ -23,7 +23,7 @@ trait SingleApi { // def bum(a: Int): Option[Int] // should not compile because of generic parameter // def bom[T](a: T): Future[Int] - @EndpointName("kanone") // does not compile without EndpointName, because overloaded + @Name("kanone") // does not compile without MethodName, because overloaded def foo: Future[Int] } object SingleApiImpl extends SingleApi { From 31fe55891b89575b49549ef4e38610d80aaa8cf8 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 19:51:32 +0200 Subject: [PATCH 09/14] rename apiName to traitName --- README.md | 6 +++--- http4sClient/src/main/scala/HttpRpcTransport.scala | 2 +- http4sServer/src/main/scala/HttpRpcRoutes.scala | 8 ++++---- jsdomClient/src/main/scala/HttpRpcTransport.scala | 2 +- sloth/src/main/scala-2/internal/Macros.scala | 4 ++-- sloth/src/main/scala-3/internal/Macros.scala | 4 ++-- sloth/src/main/scala/Request.scala | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 60153cb5..6906c88d 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ val router = Router[ByteBuffer, Future].route[Api](ApiImpl) Use it to route requests to your Api implementation: ```scala -val result = router(Request[ByteBuffer](Method(apiName = "Api", methodName = "fun"), bytes)) +val result = router(Request[ByteBuffer](Method(traitName = "Api", methodName = "fun"), bytes)) // Now result contains the serialized Int result returned by the method ApiImpl.fun ``` @@ -223,7 +223,7 @@ In the above examples, we used the type `ByteBuffer` to select the serialization Sloth derives all information about an API from a scala trait. For example: ```scala -// @Name("apiName") +// @Name("traitName") trait Api { // @Name("funName") def fun(a: Int, b: String)(c: Double): F[Int] @@ -240,7 +240,7 @@ When calling `router.route[Api](impl)`, a macro generates a function that maps a ```scala { (method: sloth.Method) => - if (method.apiName = "Api") method.methodName match { + if (method.traitName = "Api") method.methodName match { case "fun" => Some({ payload => // deserialize payload // call Api implementation impl with arguments diff --git a/http4sClient/src/main/scala/HttpRpcTransport.scala b/http4sClient/src/main/scala/HttpRpcTransport.scala index 58e354e6..df78fb99 100644 --- a/http4sClient/src/main/scala/HttpRpcTransport.scala +++ b/http4sClient/src/main/scala/HttpRpcTransport.scala @@ -13,7 +13,7 @@ case class HttpRequestConfig( ) { def toRequest[F[_]](method: sloth.Method, entityBody: EntityBody[F]): Request[F] = Request[F]( method = Method.POST, - uri = baseUri / method.apiName / method.methodName, + uri = baseUri / method.traitName / method.methodName, httpVersion = httpVersion, headers = headers, body = entityBody, diff --git a/http4sServer/src/main/scala/HttpRpcRoutes.scala b/http4sServer/src/main/scala/HttpRpcRoutes.scala index da3b6e51..3dde0f6a 100644 --- a/http4sServer/src/main/scala/HttpRpcRoutes.scala +++ b/http4sServer/src/main/scala/HttpRpcRoutes.scala @@ -23,8 +23,8 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { - case Vector(apiName, methodName) => - val method = sloth.Method(apiName.decoded(), methodName.decoded()) + case Vector(traitName, methodName) => + val method = sloth.Method(traitName.decoded(), methodName.decoded()) val result = router(request).get(method).traverse { f => request.as[PickleType].flatMap { payload => f(payload) match { @@ -54,8 +54,8 @@ object HttpRpcRoutes { HttpRoutes[F] { request => request.pathInfo.segments match { - case Vector(apiName, methodName) => - val method = sloth.Method(apiName.decoded(), methodName.decoded()) + case Vector(traitName, methodName) => + val method = sloth.Method(traitName.decoded(), methodName.decoded()) val result = router(request).get(method).traverse { f => request.as[String].flatMap { payload => f(payload) match { diff --git a/jsdomClient/src/main/scala/HttpRpcTransport.scala b/jsdomClient/src/main/scala/HttpRpcTransport.scala index c565e2ee..bcc7601f 100644 --- a/jsdomClient/src/main/scala/HttpRpcTransport.scala +++ b/jsdomClient/src/main/scala/HttpRpcTransport.scala @@ -19,7 +19,7 @@ object HttpRpcTransport { def apply[F[_]: Async](config: F[HttpRequestConfig]): RequestTransport[String, F] = new RequestTransport[String, F] { override def apply(request: Request[String]): F[String] = for { config <- config - url = s"${config.baseUri}/${request.method.apiName}/${request.method.methodName}" + url = s"${config.baseUri}/${request.method.traitName}/${request.method.methodName}" requestArgs = new dom.RequestInit { headers = config.headers.toJSDictionary; method = dom.HttpMethod.POST; body = request.payload } result <- Async[F].fromThenable(Async[F].delay[js.Thenable[String]](dom.fetch(url, requestArgs).`then`[String](_.text()))) } yield result diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index c0f1951f..9d32329f 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -20,7 +20,7 @@ class Translator[C <: Context](val c: C) { object implicits { implicit val liftMethod: Liftable[Method] = - Liftable[Method]{ r => q"new _root_.sloth.Method(${r.apiName}, ${r.methodName})" } + Liftable[Method]{ r => q"new _root_.sloth.Method(${r.traitName}, ${r.methodName})" } } def abort(msg: String) = c.abort(c.enclosingPosition, msg) @@ -209,7 +209,7 @@ object RouterMacro { val impl = $impl implRouter.orElse { method => - if (method.apiName == $traitPathPart) { + if (method.traitName == $traitPathPart) { method.methodName match { case ..$methodCases case _ => None diff --git a/sloth/src/main/scala-3/internal/Macros.scala b/sloth/src/main/scala-3/internal/Macros.scala index 0570f9ad..516ba89d 100644 --- a/sloth/src/main/scala-3/internal/Macros.scala +++ b/sloth/src/main/scala-3/internal/Macros.scala @@ -10,7 +10,7 @@ import scala.quoted.runtime.StopMacroExpansion private implicit val toExprMethod: ToExpr[Method] = new ToExpr[Method] { def apply(path: Method)(using Quotes): Expr[Method] = { import quotes.reflect._ - '{ Method(${Expr(path.apiName)}, ${Expr(path.methodName)}) } + '{ Method(${Expr(path.traitName)}, ${Expr(path.methodName)}) } } } @@ -318,7 +318,7 @@ object RouterMacro { '{ ${prefix}.orElse { method => - if (method.apiName == ${Expr(traitPathPart)}) { + if (method.traitName == ${Expr(traitPathPart)}) { ${Match( '{method.methodName}.asTerm, methodCases('{method}.asTerm) :+ CaseDef(Wildcard(), None, '{ None }.asTerm) diff --git a/sloth/src/main/scala/Request.scala b/sloth/src/main/scala/Request.scala index 5587ae95..57d65a75 100644 --- a/sloth/src/main/scala/Request.scala +++ b/sloth/src/main/scala/Request.scala @@ -1,17 +1,17 @@ package sloth -case class Method(apiName: String, methodName: String) +case class Method(traitName: String, methodName: String) case class Request[T](method: Method, payload: T) { @deprecated("Use .method instead", "0.8.0") - def path: List[String] = List(method.apiName, method.methodName) + def path: List[String] = List(method.traitName, method.methodName) } object Request { @deprecated("""Use Request(Method("Api", "method"), payload) instead""", "0.8.0") def apply[T](path: List[String], payload: T): Request[T] = Request(methodFromList(path), payload) private[sloth] def methodFromList(path: List[String]) = path match { - case List(traitName, methodName) => Method(apiName = traitName, methodName = methodName) + case List(traitName, methodName) => Method(traitName = traitName, methodName = methodName) case _ => Method("", "") } } From f7a14a1295d7d6c7d99b7347b4fb2b7b5f2241ed Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 19:54:19 +0200 Subject: [PATCH 10/14] rename get to getMethod --- .../src/main/scala/HttpRpcRoutes.scala | 4 ++-- sloth/src/main/scala/Router.scala | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/http4sServer/src/main/scala/HttpRpcRoutes.scala b/http4sServer/src/main/scala/HttpRpcRoutes.scala index 3dde0f6a..9184f960 100644 --- a/http4sServer/src/main/scala/HttpRpcRoutes.scala +++ b/http4sServer/src/main/scala/HttpRpcRoutes.scala @@ -25,7 +25,7 @@ object HttpRpcRoutes { request.pathInfo.segments match { case Vector(traitName, methodName) => val method = sloth.Method(traitName.decoded(), methodName.decoded()) - val result = router(request).get(method).traverse { f => + val result = router(request).getMethod(method).traverse { f => request.as[PickleType].flatMap { payload => f(payload) match { case Left(error) => serverFailureToResponse[F](dsl, onError)(error) @@ -56,7 +56,7 @@ object HttpRpcRoutes { request.pathInfo.segments match { case Vector(traitName, methodName) => val method = sloth.Method(traitName.decoded(), methodName.decoded()) - val result = router(request).get(method).traverse { f => + val result = router(request).getMethod(method).traverse { f => request.as[String].flatMap { payload => f(payload) match { case Left(error) => serverFailureToResponse[F](dsl, onError)(error) diff --git a/sloth/src/main/scala/Router.scala b/sloth/src/main/scala/Router.scala index d850d6c5..759fa977 100644 --- a/sloth/src/main/scala/Router.scala +++ b/sloth/src/main/scala/Router.scala @@ -7,36 +7,36 @@ import cats.implicits._ import cats.~> trait Router[PickleType, Result[_]] { - val get: Router.ApiMapping[PickleType, Result] + val getMethod: Router.ApiMapping[PickleType, Result] def apply(request: Request[PickleType]): Either[ServerFailure, Result[PickleType]] = - get(request.method) match { + getMethod(request.method) match { case Some(function) => function(request.payload) case None => Left(ServerFailure.MethodNotFound(request.method)) } @deprecated("Use get(method) instead", "0.8.0") - def getFunction(path: List[String]): Option[PickleType => Either[ServerFailure, Result[PickleType]]] = get(Request.methodFromList(path)) + def getFunction(path: List[String]): Option[PickleType => Either[ServerFailure, Result[PickleType]]] = getMethod(Request.methodFromList(path)) def orElse(collect: Router.ApiMapping[PickleType, Result]): Router[PickleType, Result] } -class RouterCo[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val get: Router.ApiMapping[PickleType, Result])(implicit +class RouterCo[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val getMethod: Router.ApiMapping[PickleType, Result])(implicit private[sloth] val functor: Functor[Result] ) extends Router[PickleType, Result] with PlatformSpecificRouterCo[PickleType, Result] { - def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterCo[PickleType, Result] = new RouterCo(logger, request => get(request).orElse(collect(request))) + def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterCo[PickleType, Result] = new RouterCo(logger, request => getMethod(request).orElse(collect(request))) - def mapResult[R[_]: Functor](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterCo(logger, request => get(request).map { case v => v.map(_.map(f.apply)) }) + def mapResult[R[_]: Functor](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterCo(logger, request => getMethod(request).map { case v => v.map(_.map(f.apply)) }) } -class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val get: Router.ApiMapping[PickleType, Result])(implicit +class RouterContra[PickleType, Result[_]](private[sloth] val logger: LogHandler[Result], val getMethod: Router.ApiMapping[PickleType, Result])(implicit private[sloth] val routerHandler: RouterContraHandler[Result] ) extends Router[PickleType, Result] with PlatformSpecificRouterContra[PickleType, Result] { - def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterContra[PickleType, Result] = new RouterContra(logger, request => get(request).orElse(collect(request))) + def orElse(collect: Router.ApiMapping[PickleType, Result]): RouterContra[PickleType, Result] = new RouterContra(logger, request => getMethod(request).orElse(collect(request))) - def mapResult[R[_]: RouterContraHandler](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterContra(logger, request => get(request).map { case v => v.map(_.map(f.apply)) }) + def mapResult[R[_]: RouterContraHandler](f: Result ~> R, logger: LogHandler[R] = LogHandler.empty[R]): Router[PickleType, R] = new RouterContra(logger, request => getMethod(request).map { case v => v.map(_.map(f.apply)) }) } object Router { From 35f5e0f6e4aee843beb6ce25a664ecab648d341b Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 20:01:34 +0200 Subject: [PATCH 11/14] Apply suggestions from code review --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6906c88d..b07f2a28 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ val router = Router[PickleType, ServerResult](MyLogHandler) ### Method overloading -When overloading methods with different parameter lists, sloth cannot have uniquely identify the method (because it is referenced with the trait name and the method name). Here you will need to provide a custom name: +When overloading methods with different parameter lists, sloth cannot uniquely identify the method (because it is referenced with the trait name and the method name). Here you will need to provide a custom name: ```scala trait Api { def fun(i: Int): F[Int] @@ -236,7 +236,7 @@ For each declared method in this trait (in this case `fun`): ### Server -When calling `router.route[Api](impl)`, a macro generates a function that maps a method method and the pickled arguments to a pickled result. This basically boils down to: +When calling `router.route[Api](impl)`, a macro generates a function that maps a method (trait-name + method-name) and the pickled arguments to a pickled result. This basically boils down to: ```scala { (method: sloth.Method) => From 4d651d1cbfac7a0f6c7d1ceb7200997b485af8c5 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 20:04:24 +0200 Subject: [PATCH 12/14] update --- sloth/src/main/scala-2/internal/Macros.scala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sloth/src/main/scala-2/internal/Macros.scala b/sloth/src/main/scala-2/internal/Macros.scala index 9d32329f..f7f3bb81 100644 --- a/sloth/src/main/scala-2/internal/Macros.scala +++ b/sloth/src/main/scala-2/internal/Macros.scala @@ -1,7 +1,5 @@ package sloth.internal -import sloth.Method - import scala.reflect.macros.blackbox.Context object Validator { @@ -19,8 +17,8 @@ class Translator[C <: Context](val c: C) { import Validator._ object implicits { - implicit val liftMethod: Liftable[Method] = - Liftable[Method]{ r => q"new _root_.sloth.Method(${r.traitName}, ${r.methodName})" } + implicit val liftMethod: Liftable[sloth.Method] = + Liftable[sloth.Method]{ r => q"new _root_.sloth.Method(${r.traitName}, ${r.methodName})" } } def abort(msg: String) = c.abort(c.enclosingPosition, msg) @@ -138,7 +136,7 @@ object TraitMacro { val traitPathPart = t.traitPathPart(traitTag.tpe) val methodImplList = validMethods.collect { case (symbol, method) => val methodPathPart = t.methodPathPart(symbol) - val path = Method(traitPathPart, methodPathPart) + val path = sloth.Method(traitPathPart, methodPathPart) val parameters = t.paramsAsValDefs(method) val paramsType = t.paramsType(method) val paramListValue = t.wrapAsParamsType(method) From 36b0e305a99b4c8a7e9dfc4e6a7e04d001da18b4 Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 20:12:54 +0200 Subject: [PATCH 13/14] Apply suggestions from code review --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b07f2a28..98a78dcf 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ trait Api { ``` For each declared method in this trait (in this case `fun`): -* Calculate method method: `Method("Api", "fun")` (`Name` annotations on the trait or method are taken into account). +* Calculate method name: `Method("Api", "fun")` (`Name` annotations on the trait or method are taken into account). * Serialize the method parameters as a tuple: `(a, b, c)`. ### Server @@ -259,7 +259,7 @@ When calling `client.wire[Api]`, a macro generates an instance of `Api` by imple new Api { def fun(a: Int, b: String)(c: Double): F[Int] = { // serialize arguments - // call RequestTransport transport with method method and arguments + // call RequestTransport transport with method and arguments // return deserialized response } } From 3422e53fc871316ef8359cb53d0ff3a85296bbdf Mon Sep 17 00:00:00 2001 From: johannes karoff Date: Tue, 25 Jun 2024 20:13:58 +0200 Subject: [PATCH 14/14] Update sloth/src/test/scala/SlothSpec.scala --- sloth/src/test/scala/SlothSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sloth/src/test/scala/SlothSpec.scala b/sloth/src/test/scala/SlothSpec.scala index b77c891a..e8b94b97 100644 --- a/sloth/src/test/scala/SlothSpec.scala +++ b/sloth/src/test/scala/SlothSpec.scala @@ -23,7 +23,7 @@ trait SingleApi { // def bum(a: Int): Option[Int] // should not compile because of generic parameter // def bom[T](a: T): Future[Int] - @Name("kanone") // does not compile without MethodName, because overloaded + @Name("kanone") // does not compile without Name annotation, because overloaded def foo: Future[Int] } object SingleApiImpl extends SingleApi {