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

kreuzberg.extras.Route.scala Maven / Gradle / Ivy

The newest version!
package kreuzberg.extras

import kreuzberg._
import language.implicitConversions

/** A Route within [[SimpleRouter]] */
trait Route {

  /** The state of which the route depends of. */
  type State

  /** The path codec to map state and path */
  def pathCodec: PathCodec[State]

  /** Directly encode a state into a path. */
  def url(state: State): UrlResource = pathCodec.encode(state)

  /** Returns true if the route can handle a given path. */
  def canHandle(resource: UrlResource): Boolean

  /** Title displayed while loading. */
  def preTitle(resource: UrlResource)(using AssemblerContext): String

  /** Execute the route, can load lazy. */
  def target(resource: UrlResource)(using AssemblerContext): Effect[RoutingTarget]
}

case class RoutingTarget(
    title: String,
    component: Component
)

/** Marks something as being routed. */
trait Routed[T] {
  def route: Route.Aux[T]

  /** Accessing the URL directly from a state. */
  inline def url(state: T): UrlResource = route.url(state)

  /** Accessing the URL directly if there is no state */
  inline def url(implicit ev: Unit => T): UrlResource = url(ev(()))
}

/** Mark some component as simple routed without further state */
trait SimpleRouted extends Routed[Unit] {
  self: Component =>

  /** The routing path */
  def path: String

  /** The page title */
  def title: String

  override val route: Route.Aux[Unit] = Route.SimpleRoute(path, title, this)
}

object Route {

  type Aux[T] = Route {
    type State = T
  }

  /** Simplification for building routing tables. */
  implicit def routedToRoute[T](routed: Routed[T]): Route.Aux[T] = routed.route

  /** A Route which directly translates a path into a component */
  trait EagerRoute extends Route {
    val pathCodec: PathCodec[State]

    override def canHandle(resource: UrlResource): Boolean = pathCodec.handles(resource)

    override def target(resource: UrlResource)(using AssemblerContext): Effect[RoutingTarget] = Effect.const {
      eagerTarget(resource)
    }

    def eagerTarget(resource: UrlResource)(using AssemblerContext): RoutingTarget = {
      val state = pathCodec.decode(resource).getOrElse {
        throw new IllegalStateException(s"Unmatched path ${resource}")
      }
      RoutingTarget(title(state), component(state))
    }

    /** Returns a title for that path. */
    def title(state: State): String

    override def preTitle(resource: UrlResource)(using AssemblerContext): String = eagerTarget(resource).title

    /** Assembles a component for a given path. */
    def component(state: State)(using AssemblerContext): Component
  }

  /** Simple Route without parameters. */
  case class SimpleRoute(
      path: String,
      title: String,
      component: Component
  ) extends EagerRoute {
    override type State = Unit

    val pathCodec: PathCodec[Unit] = PathCodec.const(path)

    override def title(state: Unit): String = title

    override def component(state: Unit)(using AssemblerContext): Component = component
  }

  /** A Route whose target depends on a value. */
  case class DependentRoute[S](
      pathCodec: PathCodec[S],
      fn: S => AssemblerContext ?=> Component,
      titleFn: S => String
  ) extends EagerRoute {

    override type State = S

    override def title(state: S): String = titleFn(state)

    override def component(state: S)(using AssemblerContext): Component = fn(state)
  }

  /** A Lazy route which fetches data. */
  case class LazyRoute[S](
      pathCodec: PathCodec[S],
      eagerTitle: S => String,
      routingTarget: S => AssemblerContext ?=> Effect[RoutingTarget]
  ) extends Route {
    override type State = S

    override def canHandle(resource: UrlResource): Boolean = pathCodec.handles(resource)

    override def target(resource: UrlResource)(using AssemblerContext): Effect[RoutingTarget] = {
      routingTarget(pathCodec.forceDecode(resource))
    }

    override def preTitle(resource: UrlResource)(using AssemblerContext): String = {
      eagerTitle(pathCodec.forceDecode(resource))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy