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

io.frontroute.PathMatcher.scala Maven / Gradle / Ivy

package io.frontroute

import scala.util.Failure
import scala.util.Success
import scala.util.Try
import scala.util.matching.Regex
import scala.util.matching.Regex.Match
import app.tulz.tuplez.Composition

abstract class PathMatcher[T](val description: String) {
  self =>

  def apply(path: List[String]): Either[(String, List[String]), (T, List[String])]

  def map[V](f: T => V): PathMatcher[V] = new PathMatcher[V](self.description) {
    override def apply(in: List[String]): Either[(String, List[String]), (V, List[String])] =
      self(in).map { case (t, out) =>
        f(t) -> out
      }
  }

  def flatMap[V](description: String)(f: T => PathMatcher[V]): PathMatcher[V] = new PathMatcher[V](description) {
    override def apply(path: List[String]): Either[(String, List[String]), (V, List[String])] =
      self(path).flatMap { case (t, out) =>
        f(t).apply(out)
      }
  }

  def filter(description: String)(f: T => Boolean): PathMatcher[T] = this.flatMap(description) { t =>
    if (f(t)) {
      PathMatcher.provide(t)
    } else {
      PathMatcher.fail("filter failed")
    }
  }

  def collect[V](description: String)(f: PartialFunction[T, V]): PathMatcher[V] = this.flatMap(description) { t =>
    if (f.isDefinedAt(t)) {
      PathMatcher.provide(f(t))
    } else {
      PathMatcher.fail("collect failed")
    }
  }

  def withFilter(description: String)(f: T => Boolean): PathMatcher[T] = this.filter(description)(f)

  def /[V](other: PathMatcher[V])(implicit compose: Composition[T, V]): PathMatcher[compose.Composed] =
    self.flatMap(s"${self.description}/${other.description}") { t1 =>
      other.map { v =>
        compose.compose(t1, v)
      }
    }

  def as[O](f: T => O): PathMatcher[O] = self.map(f)

  def void: PathMatcher[Unit] = this.map(_ => ())

  def unary_! : PathMatcher[Unit] = new PathMatcher[Unit](s"!${self.description}") {
    override def apply(path: List[String]): Either[(String, List[String]), (Unit, List[String])] =
      self(path) match {
        case Right((_, rest)) => Left("not !matched" -> rest)
        case Left((_, rest))  => Right(() -> rest)
      }
  }

  override def toString: String = description

}

object PathMatcher {

  val unit: PathMatcher[Unit] = new PathMatcher[Unit]("unit") {
    override def apply(path: List[String]): Either[(String, List[String]), (Unit, List[String])] =
      Right(() -> path)
  }

  def provide[V](v: V): PathMatcher[V] = unit.map(_ => v)

  def fail[T](msg: String): PathMatcher[T] = new PathMatcher[T]("fail") {
    override def apply(path: List[String]): Either[(String, List[String]), (T, List[String])] =
      Left(msg -> path)
  }

}

trait PathMatchers {

  def segment: PathMatcher[String] = new PathMatcher[String]("segment") {

    override def apply(path: List[String]): Either[(String, List[String]), (String, List[String])] =
      path match {
        case head :: tail => Right(head -> tail)
        case Nil          => Left(s"unexpected end of path" -> Nil)
      }

  }

  def segment(s: String): PathMatcher0 = segment.filter(s)(_ == s).void

  def regex(r: Regex): PathMatcher[Match] =
    segment
      .map(s => r.findFirstMatchIn(s))
      .collect(s"regex($r)") { case Some(m) => m }

  def fromTry[V](t: Try[V]): PathMatcher[V] = new PathMatcher[V]("fromTry") {
    override def apply(path: List[String]): Either[(String, List[String]), (V, List[String])] =
      t match {
        case Success(value) =>
          Right(value -> path)
        case Failure(exception) =>
          Left(exception.getMessage -> path)
      }
  }

  def tryParse[V](t: => V): PathMatcher[V] = fromTry(Try(t))

  def long: PathMatcher[Long] = segment.flatMap("long") { matched =>
    tryParse(matched.toLong)
  }

  def double: PathMatcher[Double] = segment.flatMap("double") { matched =>
    tryParse(matched.toDouble)
  }

  implicit def stringToSegment(s: String): PathMatcher[Unit] = segment(s)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy