Skip to content

Commit

Permalink
[dsl] Use constrainted constructors for PathVar as per code review
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianhjr committed Nov 10, 2023
1 parent 9561c09 commit c3dd55c
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 17 deletions.
8 changes: 6 additions & 2 deletions docs/docs/dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,13 +424,17 @@ val usersService = HttpRoutes.of[IO] {
```

If you want to extract a variable of type `A`, you can provide a custom extractor
which implements `cast: String => Try[A]`, similar to the way in which `IntVar` does it.
via one of the following functions:

- `PathVar.of[A](cast: String => A)`
- `PathVar.fromPartialFunction[A](cast: PartialFunction[String, A])`
- `PathVar.fromTry[A](cast: String => Try[A])`

```scala mdoc:silent
import java.time.LocalDate
import scala.util.Try

val LocalDateVar = PathVar(str => Try(LocalDate.parse(str)))
val LocalDateVar = PathVar.fromTry(str => Try(LocalDate.parse(str)))

def getTemperatureForecast(date: LocalDate): IO[Double] = IO(42.23)

Expand Down
6 changes: 2 additions & 4 deletions dsl/src/main/scala/org/http4s/dsl/Http4sDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ package org.http4s.dsl
import cats.arrow.FunctionK
import org.http4s.Method
import org.http4s.Uri
import org.http4s.dsl.impl._

import scala.util.Try
import org.http4s.dsl.impl.*

trait Http4sDsl2[F[_], G[_]] extends RequestDsl with Statuses with Responses[F, G] {
val Path: Uri.Path.type = Uri.Path
Expand All @@ -46,7 +44,7 @@ trait Http4sDsl2[F[_], G[_]] extends RequestDsl with Statuses with Responses[F,
*/
val : impl.->.type = impl.->

def PathVar[A](cast: String => Try[A]): impl.PathVar[A] = new impl.PathVar[A](cast)
lazy val PathVar: impl.PathVar.type = impl.PathVar

val IntVar: impl.IntVar.type = impl.IntVar
val LongVar: impl.LongVar.type = impl.LongVar
Expand Down
23 changes: 17 additions & 6 deletions dsl/src/main/scala/org/http4s/dsl/impl/Path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,41 +171,52 @@ object /: {
* enum Color:
* case Red, Green, Blue
*
* val ColorPath = new PathVar(str => Try(Color.valueOf(str)))
* val ColorPath = PathVar.fromTry(str => Try(Color.valueOf(str)))
*
* Path("/Green") match {
* case Root / ColorPath(color) => ...
* }}}
*/
class PathVar[A](cast: String => Try[A]) {
class PathVar[A] private[impl] (cast: String => Option[A]) {
def unapply(str: String): Option[A] =
if (str.nonEmpty) cast(str).toOption
if (str.nonEmpty) cast(str)
else None
}

object PathVar {
def of[A](cast: String => A): PathVar[A] =
new PathVar(str => Option(cast(str)))

def fromPartialFunction[A](cast: PartialFunction[String, A]): PathVar[A] =
new PathVar(str => cast.lift(str))

def fromTry[A](cast: String => Try[A]): PathVar[A] =
new PathVar(str => cast(str).toOption)
}

/** Integer extractor of a path variable:
* {{{
* Path("/user/123") match {
* case Root / "user" / IntVar(userId) => ...
* }}}
*/
object IntVar extends PathVar(str => Try(str.toInt))
object IntVar extends PathVar(str => Try(str.toInt).toOption)

/** Long extractor of a path variable:
* {{{
* Path("/user/123") match {
* case Root / "user" / LongVar(userId) => ...
* }}}
*/
object LongVar extends PathVar(str => Try(str.toLong))
object LongVar extends PathVar(str => Try(str.toLong).toOption)

/** UUID extractor of a path variable:
* {{{
* Path("/user/13251d88-7a73-4fcf-b935-54dfae9f023e") match {
* case Root / "user" / UUIDVar(userId) => ...
* }}}
*/
object UUIDVar extends PathVar(str => Try(java.util.UUID.fromString(str)))
object UUIDVar extends PathVar(str => Try(java.util.UUID.fromString(str)).toOption)

/** Matrix path variable extractor
* For an example see [[https://www.w3.org/DesignIssues/MatrixURIs.html MatrixURIs]]
Expand Down
4 changes: 1 addition & 3 deletions dsl/src/main/scala/org/http4s/dsl/request.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ package org.http4s.dsl

import org.http4s.Uri

import scala.util.Try

object request extends RequestDslBinCompat {
val Path: Uri.Path.type = Uri.Path
val Root: Uri.Path.Root.type = Uri.Path.Root
Expand All @@ -30,7 +28,7 @@ object request extends RequestDslBinCompat {
val /: : impl./:.type = impl./:
val +& : impl.+&.type = impl.+&

def PathVar[A](cast: String => Try[A]): impl.PathVar[A] = new impl.PathVar[A](cast)
lazy val PathVar: impl.PathVar.type = impl.PathVar

val IntVar: impl.IntVar.type = impl.IntVar
val LongVar: impl.LongVar.type = impl.LongVar
Expand Down
22 changes: 20 additions & 2 deletions dsl/src/test/scala/org/http4s/dsl/PathSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,29 @@ class PathSuite extends Http4sSuite with AllSyntax {
case "Green" => Green
case "Blue" => Blue
}

def partialFunction: PartialFunction[String, Color] = {
case "Red" => Red
case "Green" => Green
case "Blue" => Blue
}
}

val ColorVar = PathVar(str => Try(Color.valueOf(str)))
val ColorVarPure = PathVar.of(Color.valueOf)
assert(path"/Green" match {
case Root / ColorVarPure(color) => color == Color.Green
case _ => false
})

val ColorVarPF = PathVar.fromPartialFunction(Color.partialFunction)
assert(path"/Green" match {
case Root / ColorVarPF(color) => color == Color.Green
case _ => false
})

val ColorVarTry = PathVar.fromTry(str => Try(Color.valueOf(str)))
assert(path"/Green" match {
case Root / ColorVar(color) => color == Color.Green
case Root / ColorVarTry(color) => color == Color.Green
case _ => false
})
}
Expand Down

0 comments on commit c3dd55c

Please sign in to comment.