
japgolly.scalajs.react.extra.internal.RouterMacros.scala Maven / Gradle / Ivy
package japgolly.scalajs.react.extra.internal
import japgolly.scalajs.react.internal.ValueOfCompat.ValueOf
import japgolly.microlibs.compiletime.*
import japgolly.microlibs.compiletime.MacroEnv.*
import japgolly.scalajs.react.extra.router.StaticDsl.{Route, RouteB, RouteCommon}
import scala.compiletime.*
import scala.deriving.Mirror
import scala.language.`3.0`
import scala.quoted.*
// This is here in .internal because users are expected to import router._ and it's
// better if this isn't visible and imported with the rest of the package.
object RouterMacros {
// ===================================================================================================================
// TODO: https://github.com/lampepfl/dotty/issues/12509
// trait ForRoute [A] extends Common[Route, A] { self: Route[A] => }
// trait ForRouteB[A] extends Common[RouteB, A] { self: RouteB[A] => }
// trait Common[R[x] <: RouteCommon[R,x], A] { self: RouteCommon[R, A] =>
// /** Maps the captures values of the route to a case class. */
// inline def caseClass[B <: Product](using m: Mirror.ProductOf[B]): R[B] =
// ${ caseCaseMacro[R, A, B]('m, 'self) }
// /** Same as [[caseClass]] except the code generated by the macro is printed to stdout. */
// inline def caseClassDebug[B <: scala.Product](using m: Mirror.ProductOf[B]): R[B] =
// InlineUtils.printCode(caseClass[B])
// }
// private def caseCaseMacro[R[x] <: RouteCommon[R,x], A, B <: Product]
// (mirror: Expr[Mirror.Of[B]], self: Expr[RouteCommon[R, A]])
// (using Quotes, Type[A], Type[B], Type[R]): Expr[R[B]] = {
// import quotes.reflect.*
// mirror match {
// blah blah blah
// }
// }
// ===================================================================================================================
trait ForRoute[A] { self: Route[A] =>
/** Maps the captures values of the route to a case class. */
inline def caseClass[B <: Product](using m: Mirror.ProductOf[B]): Route[B] =
${ caseCaseMacro[A, B]('m, 'self) }
/** Same as [[caseClass]] except the code generated by the macro is printed to stdout. */
inline def caseClassDebug[B <: scala.Product](using m: Mirror.ProductOf[B]): Route[B] =
InlineUtils.printCode(caseClass[B])
}
private def caseCaseMacro[A, B <: Product]
(mirror: Expr[Mirror.Of[B]], self: Expr[Route[A]])
(using Quotes, Type[A], Type[B]): Expr[Route[B]] = {
import quotes.reflect.*
mirror match {
case '{ $m: Mirror.ProductOf[B] { type MirroredElemTypes = EmptyTuple }} =>
val ev = Expr.summonOrError[ValueOf[A]]
'{ $self.const[B]($m.fromProduct(EmptyTuple))($ev) }
case '{ $m: Mirror.ProductOf[B] { type MirroredElemTypes = t *: EmptyTuple }} =>
Expr.summonOrError[A =:= t]
'{
$self.xmap[B](
a => $m.fromProduct(a.asInstanceOf[t] *: EmptyTuple))(
b => Tuple.fromProduct(b).asInstanceOf[A *: EmptyTuple].head)
}
case '{ type t <: Product; $m: Mirror.ProductOf[B] { type MirroredElemTypes = `t` }} =>
Expr.summonOrError[A =:= t]
'{
$self.xmap[B](
a => $m.fromProduct(a.asInstanceOf[t]))(
b => Tuple.fromProduct(b).asInstanceOf[A])
}
}
}
trait ForRouteB[A] { self: RouteB[A] =>
/** Maps the captures values of the route to a case class. */
inline def caseClass[B <: Product](using m: Mirror.ProductOf[B]): RouteB[B] =
${ caseCaseMacroB[A, B]('m, 'self) }
/** Same as [[caseClass]] except the code generated by the macro is printed to stdout. */
inline def caseClassDebug[B <: scala.Product](using m: Mirror.ProductOf[B]): RouteB[B] =
InlineUtils.printCode(caseClass[B])
}
private def caseCaseMacroB[A, B <: Product]
(mirror: Expr[Mirror.Of[B]], self: Expr[RouteB[A]])
(using Quotes, Type[A], Type[B]): Expr[RouteB[B]] = {
import quotes.reflect.*
mirror match {
case '{ $m: Mirror.ProductOf[B] { type MirroredElemTypes = EmptyTuple }} =>
val ev = Expr.summonOrError[ValueOf[A]]
'{ $self.const[B]($m.fromProduct(EmptyTuple))($ev) }
case '{ $m: Mirror.ProductOf[B] { type MirroredElemTypes = t *: EmptyTuple }} =>
Expr.summonOrError[A =:= t]
'{
$self.xmap[B](
a => $m.fromProduct(a.asInstanceOf[t] *: EmptyTuple))(
b => Tuple.fromProduct(b).asInstanceOf[A *: EmptyTuple].head)
}
case '{ type t <: Product; $m: Mirror.ProductOf[B] { type MirroredElemTypes = `t` }} =>
Expr.summonOrError[A =:= t]
'{
$self.xmap[B](
a => $m.fromProduct(a.asInstanceOf[t]))(
b => Tuple.fromProduct(b).asInstanceOf[A])
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy