Skip to content

Commit

Permalink
add meta annotations to RequestPath
Browse files Browse the repository at this point in the history
  • Loading branch information
cornerman committed Jan 24, 2024
1 parent 34ef9c4 commit c7b9cfb
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 27 deletions.
53 changes: 32 additions & 21 deletions sloth/src/main/scala-2/internal/Macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Translator[C <: Context](val c: C) {

//TODO rename overloaded methods to fun1, fun2, fun3 or append TypeSignature instead of number?
private def validateAllMethods(methods: List[(MethodSymbol, Type)]): List[Either[String, (MethodSymbol, Type)]] =
methods.groupBy(m => methodPathPart(m._1)).map {
methods.groupBy(m => methodPathInfo(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"))""")
}.toList
Expand All @@ -47,6 +47,11 @@ class Translator[C <: Context](val c: C) {
case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.PathName] => name.toString
}

private def findMeta(annotations: Seq[Annotation]) = annotations.map(_.tree).collect {
case Apply(Select(New(annotation), _), _) if annotation.tpe <:< typeOf[sloth.Meta] => annotation.tpe.typeSymbol.name.toString
case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.MetaName] => name.toString
}.toVector

private def eitherSeq[A, B](list: List[Either[A, B]]): Either[List[A], List[B]] = list.partition(_.isLeft) match {
case (Nil, rights) => Right(for (Right(i) <- rights) yield i)
case (lefts, _) => Left(for (Left(s) <- lefts) yield s)
Expand Down Expand Up @@ -75,11 +80,17 @@ 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)

def methodPathPart(m: MethodSymbol): String =
findPathName(m.annotations).getOrElse(m.name.toString)
def traitPathInfo(tpe: Type): (String, Vector[String]) =
(
findPathName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString),
findMeta(tpe.typeSymbol.annotations)
)

def methodPathInfo(m: MethodSymbol): (String, Vector[String]) =
(
findPathName(m.annotations).getOrElse(m.name.toString),
findMeta(m.annotations)
)

def paramAsValDef(p: Symbol): ValDef = q"val ${p.name.toTermName}: ${p.typeSignature}"
def paramsAsValDefs(m: Type): List[List[ValDef]] = m.paramLists.map(_.map(paramAsValDef))
Expand Down Expand Up @@ -134,10 +145,10 @@ object TraitMacro {

val validMethods = t.supportedMethodsInType(traitTag.tpe, resultTag.tpe)

val traitPathPart = t.traitPathPart(traitTag.tpe)
val (traitPathPart, traitMeta) = t.traitPathInfo(traitTag.tpe)
val methodImplList = validMethods.collect { case (symbol, method) =>
val methodPathPart = t.methodPathPart(symbol)
val path = RequestPath(traitPathPart, methodPathPart)
val (methodPathPart, methodMeta) = t.methodPathInfo(symbol)
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
val parameters = t.paramsAsValDefs(method)
val paramsType = t.paramsType(method)
val paramListValue = t.wrapAsParamsType(method)
Expand Down Expand Up @@ -189,10 +200,10 @@ object RouterMacro {

val validMethods = t.supportedMethodsInType(traitTag.tpe, resultTag.tpe)

val traitPathPart = t.traitPathPart(traitTag.tpe)
val (traitPathPart, traitMeta) = t.traitPathInfo(traitTag.tpe)
val methodTuples = validMethods.map { case (symbol, method) =>
val methodPathPart = t.methodPathPart(symbol)
val path = RequestPath(traitPathPart, methodPathPart)
val (methodPathPart, methodMeta) = t.methodPathInfo(symbol)
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
val paramsType = t.paramsType(method)
val argParams = t.objectToParams(method, TermName("args"))
val innerReturnType = t.getInnerTypeOutOfReturnType(resultTag.tpe, method.finalResultType)
Expand Down Expand Up @@ -242,10 +253,10 @@ object ChecksumMacro {
case class ParamSignature(name: String, tpe: Type) {
def checksum: Int = (name, typeChecksum(tpe)).hashCode
}
case class MethodSignature(name: String, params: List[ParamSignature], result: Type) {
def checksum: Int = (name, params.map(_.checksum), typeChecksum(result)).hashCode
case class MethodSignature(name: String, meta: Seq[String], params: List[ParamSignature], result: Type) {
def checksum: Int = (name, meta, params.map(_.checksum), typeChecksum(result)).hashCode
}
case class ApiSignature(name: String, methods: Set[MethodSignature]) {
case class ApiSignature(name: String, meta: Seq[String], methods: Set[MethodSignature]) {
def checksum: Int = (name, methods.map(_.checksum)).hashCode
}

Expand Down Expand Up @@ -279,21 +290,21 @@ object ChecksumMacro {
tpe.typeSymbol.fullName,
caseAccessors.map(a => (a.name.toString, typeChecksum(a.typeSignatureIn(tpe).finalResultType))),
directSubClasses.map(typeChecksum).toSet
).hashCode
).hashCode
}

val definedMethods = t.definedMethodsInType(traitTag.tpe)

val dataMethods:Set[MethodSignature] = definedMethods.map { case (symbol, method) =>
val name = t.methodPathPart(symbol)
val dataMethods: Set[MethodSignature] = definedMethods.map { case (symbol, method) =>
val (name, meta) = t.methodPathInfo(symbol)
val resultType = method.finalResultType
val params = paramsOfType(method)

MethodSignature(name, params, resultType)
MethodSignature(name, meta, params, resultType)
}.toSet

val name = t.traitPathPart(traitTag.tpe)
val apiSignature = ApiSignature(name, dataMethods)
val (name, meta) = t.traitPathInfo(traitTag.tpe)
val apiSignature = ApiSignature(name, meta, dataMethods)

val checksum = apiSignature.checksum

Expand Down
32 changes: 27 additions & 5 deletions sloth/src/main/scala-3/internal/Macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import scala.annotation.meta.param
import scala.NonEmptyTuple
import scala.quoted.runtime.StopMacroExpansion

private implicit val toExprVectorString: ToExpr[Vector[String]] = new ToExpr[Vector[String]] {
def apply(vector: Vector[String])(using Quotes): Expr[Vector[String]] = {
import quotes.reflect._
'{Vector(${Varargs(vector.map(Expr(_)))}: _*)}
}
}

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)}) }
'{ RequestPath(${Expr(path.apiName)}, ${Expr(path.methodName)}, ${Expr(path.meta)}) }
}
}

Expand All @@ -23,6 +30,17 @@ private def getPathName(using Quotes)(symbol: quotes.reflect.Symbol): String = {
}.getOrElse(symbol.name)
}

private def getMeta(using Quotes)(symbol: quotes.reflect.Symbol): Vector[String] = {
import quotes.reflect.*

symbol.annotations.collect {
case Apply(Select(New(annotation), _), _) if annotation.tpe <:< TypeRepr.of[Meta] =>
annotation.tpe.typeSymbol.name
case Apply(Select(New(annotation), _), Literal(constant) :: Nil) if annotation.tpe =:= TypeRepr.of[MetaName] =>
constant.value.asInstanceOf[String]
}.toVector
}

private def getTypeConstructor(using Quotes)(tpe: quotes.reflect.TypeRepr): quotes.reflect.TypeRepr = {
import quotes.reflect.*

Expand Down Expand Up @@ -87,8 +105,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(m => (getPathName(m), getMeta(m))).collect { case ((name, meta), symbols) if symbols.size > 1 =>
val message = s"Method $name (meta=$meta) is overloaded, please rename one of the methods or use the PathName or Meta annotation to disambiguate"
(message, symbols.flatMap(_.pos).lastOption)
}

Expand Down Expand Up @@ -156,6 +174,7 @@ object TraitMacro {
checkMethodErrors[Trait, Result](methods)

val traitPathPart = getPathName(TypeRepr.of[Trait].typeSymbol)
val traitMeta = getMeta(TypeRepr.of[Trait].typeSymbol)

def decls(cls: Symbol): List[Symbol] = methods.map { method =>
val methodType = TypeRepr.of[Trait].memberType(method)
Expand All @@ -168,7 +187,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 = RequestPath(traitPathPart, methodPathPart)
val methodMeta = getMeta(origMethod)
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
val pathExpr = Expr(path)

DefDef(method, { argss =>
Expand Down Expand Up @@ -245,14 +265,16 @@ object RouterMacro {
checkMethodErrors[Trait, Result](methods)

val traitPathPart = getPathName(TypeRepr.of[Trait].typeSymbol)
val traitMeta = getMeta(TypeRepr.of[Trait].typeSymbol)

type FunctionInput = PickleType
type FunctionOutput = Either[ServerFailure, Result[PickleType]]

val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef =>
val methodDefinitions = methods.map { method =>
val methodPathPart = getPathName(method)
val path = RequestPath(traitPathPart, methodPathPart)
val methodMeta = getMeta(method)
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
val pathExpr = Expr(path)

val returnType = getInnerTypeOutOfReturnType[Trait, Result](method)
Expand Down
15 changes: 15 additions & 0 deletions sloth/src/main/scala/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,18 @@ package sloth
import scala.annotation.StaticAnnotation

final class PathName(val name: String) extends StaticAnnotation

final class MetaName extends StaticAnnotation

trait Meta extends StaticAnnotation
object Meta {
object http {
final class Get extends Meta
final class Head extends Meta
final class Post extends Meta
final class Put extends Meta
final class Delete extends Meta
final class Options extends Meta
final class Patch extends Meta
}
}
2 changes: 1 addition & 1 deletion sloth/src/main/scala/Request.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package sloth

case class RequestPath(apiName: String, methodName: String, meta: Set[String] = Set.empty)
case class RequestPath(apiName: String, methodName: String, meta: Vector[String] = Vector.empty)

case class Request[T](path: RequestPath, payload: T)

0 comments on commit c7b9cfb

Please sign in to comment.