Skip to content

Commit 2c8071e

Browse files
committed
add meta annotations to RequestPath
1 parent 34ef9c4 commit 2c8071e

File tree

4 files changed

+78
-29
lines changed

4 files changed

+78
-29
lines changed

sloth/src/main/scala-2/internal/Macros.scala

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ class Translator[C <: Context](val c: C) {
1919
import Validator._
2020

2121
object implicits {
22-
implicit val liftRequestPath: Liftable[RequestPath] =
23-
Liftable[RequestPath]{ r => q"new _root_.sloth.RequestPath(${r.apiName}, ${r.methodName})" }
22+
implicit val liftRequestPath: Liftable[RequestPath] = Liftable[RequestPath] { r =>
23+
q"new _root_.sloth.RequestPath(${r.apiName}, ${r.methodName})"
24+
}
2425
}
2526

2627
def abort(msg: String) = c.abort(c.enclosingPosition, msg)
@@ -38,7 +39,7 @@ class Translator[C <: Context](val c: C) {
3839

3940
//TODO rename overloaded methods to fun1, fun2, fun3 or append TypeSignature instead of number?
4041
private def validateAllMethods(methods: List[(MethodSymbol, Type)]): List[Either[String, (MethodSymbol, Type)]] =
41-
methods.groupBy(m => methodPathPart(m._1)).map {
42+
methods.groupBy(m => methodPathInfo(m._1)).map {
4243
case (_, x :: Nil) => Right(x)
4344
case (k, _) => Left(s"""method $k is overloaded (rename the method or add a @PathName("other-name"))""")
4445
}.toList
@@ -47,6 +48,11 @@ class Translator[C <: Context](val c: C) {
4748
case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.PathName] => name.toString
4849
}
4950

51+
private def findMeta(annotations: Seq[Annotation]) = annotations.map(_.tree).collect {
52+
case Apply(Select(New(annotation), _), _) if annotation.tpe <:< typeOf[sloth.Meta] => annotation.tpe.typeSymbol.name.toString
53+
case Apply(Select(New(annotation), _), Literal(Constant(name)) :: Nil) if annotation.tpe =:= typeOf[sloth.MetaName] => name.toString
54+
}.toVector
55+
5056
private def eitherSeq[A, B](list: List[Either[A, B]]): Either[List[A], List[B]] = list.partition(_.isLeft) match {
5157
case (Nil, rights) => Right(for (Right(i) <- rights) yield i)
5258
case (lefts, _) => Left(for (Left(s) <- lefts) yield s)
@@ -75,11 +81,17 @@ class Translator[C <: Context](val c: C) {
7581
}
7682

7783
//TODO what about fqn for trait to not have overlaps?
78-
def traitPathPart(tpe: Type): String =
79-
findPathName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString)
80-
81-
def methodPathPart(m: MethodSymbol): String =
82-
findPathName(m.annotations).getOrElse(m.name.toString)
84+
def traitPathInfo(tpe: Type): (String, Vector[String]) =
85+
(
86+
findPathName(tpe.typeSymbol.annotations).getOrElse(tpe.typeSymbol.name.toString),
87+
findMeta(tpe.typeSymbol.annotations)
88+
)
89+
90+
def methodPathInfo(m: MethodSymbol): (String, Vector[String]) =
91+
(
92+
findPathName(m.annotations).getOrElse(m.name.toString),
93+
findMeta(m.annotations)
94+
)
8395

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

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

137-
val traitPathPart = t.traitPathPart(traitTag.tpe)
149+
val (traitPathPart, traitMeta) = t.traitPathInfo(traitTag.tpe)
138150
val methodImplList = validMethods.collect { case (symbol, method) =>
139-
val methodPathPart = t.methodPathPart(symbol)
140-
val path = RequestPath(traitPathPart, methodPathPart)
151+
val (methodPathPart, methodMeta) = t.methodPathInfo(symbol)
152+
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
141153
val parameters = t.paramsAsValDefs(method)
142154
val paramsType = t.paramsType(method)
143155
val paramListValue = t.wrapAsParamsType(method)
@@ -189,10 +201,10 @@ object RouterMacro {
189201

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

192-
val traitPathPart = t.traitPathPart(traitTag.tpe)
204+
val (traitPathPart, traitMeta) = t.traitPathInfo(traitTag.tpe)
193205
val methodTuples = validMethods.map { case (symbol, method) =>
194-
val methodPathPart = t.methodPathPart(symbol)
195-
val path = RequestPath(traitPathPart, methodPathPart)
206+
val (methodPathPart, methodMeta) = t.methodPathInfo(symbol)
207+
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
196208
val paramsType = t.paramsType(method)
197209
val argParams = t.objectToParams(method, TermName("args"))
198210
val innerReturnType = t.getInnerTypeOutOfReturnType(resultTag.tpe, method.finalResultType)
@@ -242,10 +254,10 @@ object ChecksumMacro {
242254
case class ParamSignature(name: String, tpe: Type) {
243255
def checksum: Int = (name, typeChecksum(tpe)).hashCode
244256
}
245-
case class MethodSignature(name: String, params: List[ParamSignature], result: Type) {
246-
def checksum: Int = (name, params.map(_.checksum), typeChecksum(result)).hashCode
257+
case class MethodSignature(name: String, meta: Seq[String], params: List[ParamSignature], result: Type) {
258+
def checksum: Int = (name, meta, params.map(_.checksum), typeChecksum(result)).hashCode
247259
}
248-
case class ApiSignature(name: String, methods: Set[MethodSignature]) {
260+
case class ApiSignature(name: String, meta: Seq[String], methods: Set[MethodSignature]) {
249261
def checksum: Int = (name, methods.map(_.checksum)).hashCode
250262
}
251263

@@ -279,21 +291,21 @@ object ChecksumMacro {
279291
tpe.typeSymbol.fullName,
280292
caseAccessors.map(a => (a.name.toString, typeChecksum(a.typeSignatureIn(tpe).finalResultType))),
281293
directSubClasses.map(typeChecksum).toSet
282-
).hashCode
294+
).hashCode
283295
}
284296

285297
val definedMethods = t.definedMethodsInType(traitTag.tpe)
286298

287-
val dataMethods:Set[MethodSignature] = definedMethods.map { case (symbol, method) =>
288-
val name = t.methodPathPart(symbol)
299+
val dataMethods: Set[MethodSignature] = definedMethods.map { case (symbol, method) =>
300+
val (name, meta) = t.methodPathInfo(symbol)
289301
val resultType = method.finalResultType
290302
val params = paramsOfType(method)
291303

292-
MethodSignature(name, params, resultType)
304+
MethodSignature(name, meta, params, resultType)
293305
}.toSet
294306

295-
val name = t.traitPathPart(traitTag.tpe)
296-
val apiSignature = ApiSignature(name, dataMethods)
307+
val (name, meta) = t.traitPathInfo(traitTag.tpe)
308+
val apiSignature = ApiSignature(name, meta, dataMethods)
297309

298310
val checksum = apiSignature.checksum
299311

sloth/src/main/scala-3/internal/Macros.scala

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ import scala.annotation.meta.param
77
import scala.NonEmptyTuple
88
import scala.quoted.runtime.StopMacroExpansion
99

10+
private implicit val toExprVectorString: ToExpr[Vector[String]] = new ToExpr[Vector[String]] {
11+
def apply(vector: Vector[String])(using Quotes): Expr[Vector[String]] = {
12+
import quotes.reflect._
13+
'{Vector(${Varargs(vector.map(Expr(_)))}: _*)}
14+
}
15+
}
16+
1017
private implicit val toExprRequestPath: ToExpr[RequestPath] = new ToExpr[RequestPath] {
1118
def apply(path: RequestPath)(using Quotes): Expr[RequestPath] = {
1219
import quotes.reflect._
13-
'{ RequestPath(${Expr(path.apiName)}, ${Expr(path.methodName)}) }
20+
'{ RequestPath(${Expr(path.apiName)}, ${Expr(path.methodName)}, ${Expr(path.meta)}) }
1421
}
1522
}
1623

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

33+
private def getMeta(using Quotes)(symbol: quotes.reflect.Symbol): Vector[String] = {
34+
import quotes.reflect.*
35+
36+
symbol.annotations.collect {
37+
case Apply(Select(New(annotation), _), _) if annotation.tpe <:< TypeRepr.of[Meta] =>
38+
annotation.tpe.typeSymbol.name
39+
case Apply(Select(New(annotation), _), Literal(constant) :: Nil) if annotation.tpe =:= TypeRepr.of[MetaName] =>
40+
constant.value.asInstanceOf[String]
41+
}.toVector
42+
}
43+
2644
private def getTypeConstructor(using Quotes)(tpe: quotes.reflect.TypeRepr): quotes.reflect.TypeRepr = {
2745
import quotes.reflect.*
2846

@@ -87,8 +105,8 @@ def createTypeTreeTuple(using Quotes)(tupleTypesList: List[quotes.reflect.TypeRe
87105
private def checkMethodErrors[Trait: Type, Result[_]: Type](using q: Quotes)(methods: Seq[quotes.reflect.Symbol]): Unit = {
88106
import quotes.reflect.*
89107

90-
val duplicateErrors = methods.groupBy(getPathName).collect { case (name, symbols) if symbols.size > 1 =>
91-
val message = s"Method $name is overloaded, please rename one of the methods or use the PathName annotation to disambiguate"
108+
val duplicateErrors = methods.groupBy(m => (getPathName(m), getMeta(m))).collect { case ((name, meta), symbols) if symbols.size > 1 =>
109+
val message = s"Method $name (meta=$meta) is overloaded, please rename one of the methods or use the PathName or Meta annotation to disambiguate"
92110
(message, symbols.flatMap(_.pos).lastOption)
93111
}
94112

@@ -156,6 +174,7 @@ object TraitMacro {
156174
checkMethodErrors[Trait, Result](methods)
157175

158176
val traitPathPart = getPathName(TypeRepr.of[Trait].typeSymbol)
177+
val traitMeta = getMeta(TypeRepr.of[Trait].typeSymbol)
159178

160179
def decls(cls: Symbol): List[Symbol] = methods.map { method =>
161180
val methodType = TypeRepr.of[Trait].memberType(method)
@@ -168,7 +187,8 @@ object TraitMacro {
168187
val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef =>
169188
val body = (cls.declaredMethods.zip(methods)).map { case (method, origMethod) =>
170189
val methodPathPart = getPathName(origMethod)
171-
val path = RequestPath(traitPathPart, methodPathPart)
190+
val methodMeta = getMeta(origMethod)
191+
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
172192
val pathExpr = Expr(path)
173193

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

247267
val traitPathPart = getPathName(TypeRepr.of[Trait].typeSymbol)
268+
val traitMeta = getMeta(TypeRepr.of[Trait].typeSymbol)
248269

249270
type FunctionInput = PickleType
250271
type FunctionOutput = Either[ServerFailure, Result[PickleType]]
251272

252273
val result = ValDef.let(Symbol.spliceOwner, implInstance.asTerm) { implRef =>
253274
val methodDefinitions = methods.map { method =>
254275
val methodPathPart = getPathName(method)
255-
val path = RequestPath(traitPathPart, methodPathPart)
276+
val methodMeta = getMeta(method)
277+
val path = RequestPath(traitPathPart, methodPathPart, traitMeta ++ methodMeta)
256278
val pathExpr = Expr(path)
257279

258280
val returnType = getInnerTypeOutOfReturnType[Trait, Result](method)

sloth/src/main/scala/Annotations.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,18 @@ package sloth
33
import scala.annotation.StaticAnnotation
44

55
final class PathName(val name: String) extends StaticAnnotation
6+
7+
final class MetaName extends StaticAnnotation
8+
9+
trait Meta extends StaticAnnotation
10+
object Meta {
11+
object http {
12+
final class Get extends Meta
13+
final class Head extends Meta
14+
final class Post extends Meta
15+
final class Put extends Meta
16+
final class Delete extends Meta
17+
final class Options extends Meta
18+
final class Patch extends Meta
19+
}
20+
}

sloth/src/main/scala/Request.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package sloth
22

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

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

0 commit comments

Comments
 (0)