Skip to content
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
23 changes: 23 additions & 0 deletions core/src/main/scala/io/finch/Endpoint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ trait Endpoint[A] { self =>
*/
def apply(input: Input): Endpoint.Result[A]

def meta: Endpoint.Meta

/**
* Maps this endpoint to the given function `A => B`.
*/
Expand All @@ -91,6 +93,7 @@ trait Endpoint[A] { self =>

final override def item = self.item
final override def toString: String = self.toString
final override def meta: Endpoint.Meta = self.meta
}

/**
Expand All @@ -114,6 +117,7 @@ trait Endpoint[A] { self =>

override def item = self.item
final override def toString: String = self.toString
final override def meta: Endpoint.Meta = self.meta
}

/**
Expand Down Expand Up @@ -141,6 +145,7 @@ trait Endpoint[A] { self =>

override def item = self.item
final override def toString: String = self.toString
final override def meta: Endpoint.Meta = self.meta
}

/**
Expand Down Expand Up @@ -187,6 +192,7 @@ trait Endpoint[A] { self =>

override def item = self.item
final override def toString: String = self.toString
final override def meta: Endpoint.Meta = self.meta
}

/**
Expand All @@ -202,6 +208,7 @@ trait Endpoint[A] { self =>

override def item = items.MultipleItems
final override def toString: String = s"${other.toString} :: ${self.toString}"
final override def meta: Endpoint.Meta = EndpointMetadata.Product(other.meta, self.meta)
}

/**
Expand All @@ -220,6 +227,7 @@ trait Endpoint[A] { self =>

override def item = items.MultipleItems
final override def toString: String = s"(${self.toString} :+: ${other.toString})"
final override def meta: Endpoint.Meta = EndpointMetadata.Coproduct(other.meta, self.meta)
}

/**
Expand Down Expand Up @@ -349,6 +357,7 @@ trait Endpoint[A] { self =>

override def item = self.item
override final def toString: String = self.toString
override final def meta: Endpoint.Meta = self.meta
}

/**
Expand All @@ -357,6 +366,7 @@ trait Endpoint[A] { self =>
final def withToString(ts: => String): Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Endpoint.Result[A] = self(input)
final override def toString: String = ts
final override def meta: Endpoint.Meta = self.meta
}

private[this] def withOutput[B](fn: Output[A] => Output[B]): Endpoint[B] =
Expand All @@ -370,11 +380,14 @@ object Endpoint {

type Result[A] = EndpointResult[A]

type Meta = EndpointMetadata

/**
* Creates an empty [[Endpoint]] (an endpoint that never matches) for a given type.
*/
def empty[A]: Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Result[A] = EndpointResult.Skipped
final def meta: Endpoint.Meta = EndpointMetadata.Empty
}

/**
Expand All @@ -383,6 +396,8 @@ object Endpoint {
def const[A](a: A): Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Result[A] =
EndpointResult.Matched(input, Rerunnable.const(Output.payload(a)))

final def meta: Endpoint.Meta = EndpointMetadata.Const
}

/**
Expand All @@ -400,6 +415,8 @@ object Endpoint {
def lift[A](a: => A): Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Result[A] =
EndpointResult.Matched(input, Rerunnable(Output.payload(a)))

final def meta: Endpoint.Meta = EndpointMetadata.Const
}

/**
Expand All @@ -408,6 +425,8 @@ object Endpoint {
def liftAsync[A](fa: => Future[A]): Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Result[A] =
EndpointResult.Matched(input, Rerunnable.fromFuture(fa).map(a => Output.payload(a)))

final def meta: Endpoint.Meta = EndpointMetadata.Const
}

/**
Expand All @@ -422,6 +441,8 @@ object Endpoint {
def liftOutput[A](oa: => Output[A]): Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Result[A] =
EndpointResult.Matched(input, Rerunnable(oa))

final def meta: Endpoint.Meta = EndpointMetadata.Const
}

/**
Expand All @@ -431,6 +452,8 @@ object Endpoint {
def liftOutputAsync[A](foa: => Future[Output[A]]): Endpoint[A] = new Endpoint[A] {
final def apply(input: Input): Result[A] =
EndpointResult.Matched(input, Rerunnable.fromFuture(foa))

final def meta: Endpoint.Meta = EndpointMetadata.Const
}

/**
Expand Down
43 changes: 43 additions & 0 deletions core/src/main/scala/io/finch/EndpointMetadata.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.finch

sealed trait Segment

object Segment {

case class Part(name: String) extends Segment

case object Wildcard extends Segment

case object Empty extends Segment

}

sealed trait EndpointMetadata

object EndpointMetadata {

case class Method(method: com.twitter.finagle.http.Method, a: EndpointMetadata) extends EndpointMetadata

case class Path(segment: io.finch.Segment) extends EndpointMetadata

case class Parameter(name: String) extends EndpointMetadata

case class Parameters(name: String) extends EndpointMetadata

case class Header(name: String) extends EndpointMetadata

case class Cookie(name: String) extends EndpointMetadata

case object Body extends EndpointMetadata

case class Multipart(name: String) extends EndpointMetadata

case class Coproduct(a: EndpointMetadata, b: EndpointMetadata) extends EndpointMetadata

case class Product(a: EndpointMetadata, b: EndpointMetadata) extends EndpointMetadata

case object Const extends EndpointMetadata

case object Empty extends EndpointMetadata

}
6 changes: 6 additions & 0 deletions core/src/main/scala/io/finch/Endpoints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ trait Endpoints extends Bodies
EndpointResult.Matched(input.copy(route = Nil), Rs.OutputHNil)

final override def toString: String = "*"

final def meta: Endpoint.Meta = EndpointMetadata.Path(Segment.Wildcard)
}

/**
Expand All @@ -33,6 +35,8 @@ trait Endpoints extends Bodies
EndpointResult.Matched(input, Rs.OutputHNil)

final override def toString: String = ""

final def meta: Endpoint.Meta = EndpointMetadata.Path(Segment.Empty)
}

/**
Expand All @@ -43,5 +47,7 @@ trait Endpoints extends Bodies
EndpointResult.Matched(input, Rs.payload(input.request))

final override def toString: String = "root"

final def meta: Endpoint.Meta = EndpointMetadata.Const
}
}
3 changes: 3 additions & 0 deletions core/src/main/scala/io/finch/endpoint/body.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ private abstract class FullBody[A] extends Endpoint[A] {
}

final override def item: RequestItem = items.BodyItem

final def meta: Endpoint.Meta = EndpointMetadata.Body
}

private object FullBody {
Expand Down Expand Up @@ -153,5 +155,6 @@ private[finch] trait Bodies {

final override def item: RequestItem = items.BodyItem
final override def toString: String = "asyncBody"
final def meta: Endpoint.Meta = EndpointMetadata.Body
}
}
1 change: 1 addition & 0 deletions core/src/main/scala/io/finch/endpoint/cookie.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ private abstract class Cookie[A](name: String) extends Endpoint[A] {

final override def item: items.RequestItem = items.CookieItem(name)
final override def toString: String = s"cookie($name)"
final def meta: Endpoint.Meta = EndpointMetadata.Cookie(name)
}

private object Cookie {
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/io/finch/endpoint/header.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ private abstract class Header[F[_], A](name: String, d: DecodeEntity[A], tag: Cl

final override def item: RequestItem = items.HeaderItem(name)
final override def toString: String = s"header($name)"
final def meta: Endpoint.Meta = EndpointMetadata.Header(name)
}

private object Header {
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/io/finch/endpoint/multipart.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ private abstract class Multipart[A, B](name: String) extends Endpoint[B] {

final override def item: RequestItem = ParamItem(name)
final override def toString: String = name
final def meta: Endpoint.Meta = EndpointMetadata.Multipart(name)
}

private object Multipart {
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/scala/io/finch/endpoint/param.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private abstract class Param[F[_], A](name: String, d: DecodeEntity[A], tag: Cla

final override def item: items.RequestItem = items.ParamItem(name)
final override def toString: String = s"param($name)"

final def meta: Endpoint.Meta = EndpointMetadata.Parameter(name)

}

Expand Down Expand Up @@ -75,6 +75,7 @@ private abstract class Params[F[_], A](name: String, d: DecodeEntity[A], tag: Cl
}
final override def item: items.RequestItem = items.ParamItem(name)
final override def toString: String = s"params($name)"
final def meta: Endpoint.Meta = EndpointMetadata.Parameters(name)
}

private object Params {
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala/io/finch/endpoint/path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private class MatchPath(s: String) extends Endpoint[HNil] {
}

final override def toString: String = s
final def meta: Endpoint.Meta = EndpointMetadata.Path(Segment.Part(s))
}

private class ExtractPath[A](implicit d: DecodePath[A], ct: ClassTag[A]) extends Endpoint[A] {
Expand All @@ -28,6 +29,9 @@ private class ExtractPath[A](implicit d: DecodePath[A], ct: ClassTag[A]) extends
}

final override def toString: String = s":${ct.runtimeClass.getSimpleName.toLowerCase}"

final def meta: Endpoint.Meta =
EndpointMetadata.Path(Segment.Part(ct.runtimeClass.getSimpleName.toLowerCase))
}

private class ExtractPaths[A](implicit d: DecodePath[A], ct: ClassTag[A]) extends Endpoint[Seq[A]] {
Expand All @@ -38,6 +42,9 @@ private class ExtractPaths[A](implicit d: DecodePath[A], ct: ClassTag[A]) extend
)

final override def toString: String = s":${ct.runtimeClass.getSimpleName.toLowerCase}*"

final def meta: Endpoint.Meta =
EndpointMetadata.Path(Segment.Part(s"${ct.runtimeClass.getSimpleName.toLowerCase}*"))
}

private[finch] trait Paths {
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/io/finch/syntax/EndpointMapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ class EndpointMapper[A](m: Method, e: Endpoint[A]) extends Endpoint[A] { self =>
else EndpointResult.Skipped

final override def toString: String = s"${ m.toString.toUpperCase } /${ e.toString }"

final def meta: Endpoint.Meta = EndpointMetadata.Method(m, e.meta)
}
33 changes: 33 additions & 0 deletions core/src/test/scala/io/finch/EndpointMetadataSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.finch

import io.finch.syntax.EndpointMapper

class EndpointMetadataSpec extends FinchSpec {

behavior of "EndpointMetadata"

private def interpreter(ms: EndpointMetadata): Endpoint[_] = ms match {
case EndpointMetadata.Method(m, meta) => new EndpointMapper(m, interpreter(meta))
case EndpointMetadata.Path(s) => s match {
case Segment.Part(part) => path(part)
case Segment.Wildcard => *
case Segment.Empty => /
}
case EndpointMetadata.Multipart(name) => multipartAttribute(name)
case EndpointMetadata.Cookie(name) => cookie(name)
case EndpointMetadata.Parameter(name) => param(name)
case EndpointMetadata.Parameters(name) => params(name)
case EndpointMetadata.Header(name) => header(name)
case EndpointMetadata.Body => stringBody
case EndpointMetadata.Empty => Endpoint.empty[String]
case EndpointMetadata.Const => Endpoint.const("foo")
case EndpointMetadata.Coproduct(a, b) => interpreter(b) :+: interpreter(a)
case EndpointMetadata.Product(a, b) => interpreter(a) :: interpreter(b)
}

it should "do a round-trip" in {
check { l: EndpointMetadata =>
interpreter(l).meta === l
}
}
}
Loading