Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PathVar map, emap #7310

Open
wants to merge 5 commits into
base: series/0.23
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dsl/src/main/scala/org/http4s/dsl/Http4sDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ trait Http4sDsl2[F[_], G[_]] extends RequestDsl with Statuses with Responses[F,
val IntVar: impl.IntVar.type = impl.IntVar
val LongVar: impl.LongVar.type = impl.LongVar
val UUIDVar: impl.UUIDVar.type = impl.UUIDVar
override lazy val StringVar: impl.StringVar.type = impl.StringVar
}

trait Http4sDsl[F[_]] extends Http4sDsl2[F, F] {
Expand Down
1 change: 1 addition & 0 deletions dsl/src/main/scala/org/http4s/dsl/RequestDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ trait RequestDsl extends Methods with Auth {
val IntVar: impl.IntVar.type
val LongVar: impl.LongVar.type
val UUIDVar: impl.UUIDVar.type
lazy val StringVar: impl.StringVar.type = impl.StringVar

type QueryParamDecoderMatcher[T] = impl.QueryParamDecoderMatcher[T]
type QueryParamMatcher[T] = impl.QueryParamMatcher[T]
Expand Down
17 changes: 17 additions & 0 deletions dsl/src/main/scala/org/http4s/dsl/impl/Path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import org.http4s.Uri.Path._
import org.http4s._
import org.http4s.headers.Allow

import scala.util.Success
import scala.util.Try
import scala.util.control.NoStackTrace

object :? {
def unapply[F[_]](req: Request[F]): Some[(Request[F], Map[String, collection.Seq[String]])] =
Expand Down Expand Up @@ -172,6 +174,13 @@ protected class PathVar[A](cast: String => Try[A]) {
cast(str).toOption
else
None

def map[B](f: A => B): PathVar[B] =
new PathVar[B](str => cast(str).map(f))

def emap[B](f: A => Option[B]): PathVar[B] =
new PathVar[B](str => cast(str).flatMap(a => f(a).toRight(new NoStackTrace {}).toTry))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little bit of bikeshedding, but this feels like too many allocations for the simple instantiation... Considering the PathVar nature (something that's going to be used for every request on a given path), could we tweak this?


}

/** Integer extractor of a path variable:
Expand All @@ -198,6 +207,14 @@ object LongVar extends PathVar(str => Try(str.toLong))
*/
object UUIDVar extends PathVar(str => Try(java.util.UUID.fromString(str)))

/** StringVar extractor of a path variable, used mostly for composition:
* {{{
* Path("/user/John") match {
* case Root / "user" / StringVar(userName) => ...
* }}}
*/
object StringVar extends PathVar(str => Success(str))

/** Matrix path variable extractor
* For an example see [[https://www.w3.org/DesignIssues/MatrixURIs.html MatrixURIs]]
* This is useful for representing a resource that may be addressed in multiple dimensions where order is unimportant
Expand Down
1 change: 1 addition & 0 deletions dsl/src/main/scala/org/http4s/dsl/request.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ object request extends RequestDslBinCompat {
val IntVar: impl.IntVar.type = impl.IntVar
val LongVar: impl.LongVar.type = impl.LongVar
val UUIDVar: impl.UUIDVar.type = impl.UUIDVar
override lazy val StringVar: impl.StringVar.type = impl.StringVar
}
52 changes: 52 additions & 0 deletions dsl/src/test/scala/org/http4s/dsl/PathSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import org.http4s.syntax.AllSyntax
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Gen

import scala.util.Try

class PathSuite extends Http4sSuite with AllSyntax {
implicit val arbitraryPath: Gen[Path] =
arbitrary[List[String]]
Expand Down Expand Up @@ -205,6 +207,56 @@ class PathSuite extends Http4sSuite with AllSyntax {
}))
}

test("String extractor success") {
assert(
path"/a/123" match {
case Root / "a" / StringVar(s) => s == "123"
case _ => false
}
)
}

test("Custom extractor with map success") {
val AbsIntVar = IntVar.map(math.abs)
assert(
path"/a/-123" match {
case Root / "a" / AbsIntVar(n) => n == 123
case _ => false
}
)
}

test("Custom extractor with map failure") {
val AbsIntVar = IntVar.map(math.abs)
assert(
path"/a/abab" match {
case Root / "a" / AbsIntVar(_) => false
case _ => true
}
)
}

test("Custom extractor with emap success") {
val NewIntVar = StringVar.emap(s => Try(s.toInt).toOption)
assert(
path"/a/-123" match {
case Root / "a" / NewIntVar(n) => n == -123
case _ => false
}
)
}

test("Custom extractor with emap failure") {
val NewIntVar = StringVar.emap(s => Try(s.toInt).toOption)

assert(
path"/a/abab" match {
case Root / "a" / NewIntVar(_) => false
case _ => true
}
)
}

object BoardExtractor extends impl.MatrixVar("square", List("x", "y"))

object EmptyNameExtractor extends impl.MatrixVar("", List("x", "y"))
Expand Down
Loading