summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2013-02-14 17:39:46 -0600
committerJesse Luehrs <doy@tozt.net>2013-02-14 17:44:28 -0600
commit6af0bb7aa231c41213b455e1ae44a50a5b295292 (patch)
tree4549593b7599ae37c1e336bfc60819159fe395be
parent84000c335f5cdd2bb3994e7062f30836f2275b4d (diff)
downloadscala-path-router-6af0bb7aa231c41213b455e1ae44a50a5b295292.tar.gz
scala-path-router-6af0bb7aa231c41213b455e1ae44a50a5b295292.zip
implement uriFor
-rw-r--r--src/main/scala/router.scala51
-rw-r--r--src/test/scala/basic.scala1
2 files changed, 50 insertions, 2 deletions
diff --git a/src/main/scala/router.scala b/src/main/scala/router.scala
index a9b2ec1..56e0342 100644
--- a/src/main/scala/router.scala
+++ b/src/main/scala/router.scala
@@ -42,8 +42,55 @@ class Router[T] {
_route(path.split("/"), routes.toList)
}
- def uriFor (mapping: Map[String, String]): String = {
- throw new Error("unimplemented")
+ private class AmbiguousRouteMapping(
+ mapping: Map[String, String],
+ paths: Seq[String]
+ ) extends RuntimeException {
+ override def getMessage (): String = {
+ "Ambiguous path descriptor (specified keys " +
+ mapping.keys.mkString(", ") +
+ "): could match paths " +
+ paths.mkString(", ")
+ }
+ }
+
+ def uriFor (mapping: Map[String, String]): Option[String] = {
+ // first remove all routes that can't possibly match
+ // - if the route requires a variable component that doesn't exist in the
+ // mapping, then it can't match
+ // - if the route contains a value for a variable component that doesn't
+ // pass the validation for that component, it can't match
+ // - if the route contains a default value, and that component also exists
+ // in the mapping, then the values must match
+ val possible = routes.flatMap(r => {
+ r.pathWithMapping(mapping) match {
+ case Some(path) => Some(r -> path)
+ case None => None
+ }
+ })
+
+ possible.toList match {
+ case Nil => None
+ case (r, path) :: Nil => Some(path)
+ case rs => {
+ // then try to disambiguate the remaining possibilities
+ // - we want the route with the fewest number of "extra" items in the
+ // mapping, after removing defaults and variable path components
+ val possibleByRemainder = possible.groupBy { case (r, path) => {
+ (mapping.keys.toSet --
+ r.defaults.keys.toSet --
+ r.requiredVariableComponents --
+ r.optionalVariableComponents).size
+ } }
+ val found = possibleByRemainder(possibleByRemainder.keys.min)
+ found.toList match {
+ case Nil => None
+ case (r, path) :: Nil => Some(path)
+ case rs =>
+ throw new AmbiguousRouteMapping(mapping, rs.map(_._1.path))
+ }
+ }
+ }
}
}
diff --git a/src/test/scala/basic.scala b/src/test/scala/basic.scala
index 070ace8..ef9ea22 100644
--- a/src/test/scala/basic.scala
+++ b/src/test/scala/basic.scala
@@ -57,6 +57,7 @@ class Basic extends FunSuite {
)
def testRoute (router: Router[Boolean], path: String, mapping: Map[String, String]) {
+ assert(path === router.uriFor(mapping).get)
val om = router.route(path)
assert(om.isDefined)
val m = om.get