This code is from a class that extends Directives
. So all methods of Directives
are in scope.
PathMatcher
There is no method /
in String
, so an implicit conversion is used to convert String
to PathMatcher0
(PathMatcher[HNil]
) with method /
.
Method /
takes a PathMatcher
and returns a PathMatcher
.
Segment
is a PathMatcher1[String]
(PathMatcher[String :: HNil]
).
Method /
of PathMatcher[HNil]
with PathMatcher[String :: HNil]
parameter returns a PathMatcher[String :: HNil]
.
Method /
of PathMatcher[String :: HNil]
with PathMatcher[String :: HNil]
parameter returns a PathMatcher[String :: String :: HNil]
. It's black magic from shapeless
. See heterogenous lists concatenation; it is worth reading.
Directive
So you are calling method path
with PathMatcher[String :: String :: HNil]
as a parameter. It returns a Directive[String :: String :: HNil]
.
Then you are calling method apply
on Directive
with Function2[?, ?, ?]
((a, b) => ..
) as a parameter. There is an appropriate implicit conversion (see black magic) for every Directive[A :: B :: C ...]
that creates an object with method apply((a: A, b: B, c: C ...) => Route)
.
Parsing
PathMatcher
contains rules for path parsing. It returns its result as an HList
.
The "foo" matcher matches a String and ignores it (returns HNil
).
The A / B
matcher combines 2 matchers (A
and B
) separated by a "/" string. It concatenates the results of A
and B
using HList
concatenation.
The Segment
matcher matches a path segment and returns it as a String :: HNil
.
So "foo" / Segment / Segment
matches a path of 3 segments, ignores the first one and returns the remaining segments as String :: String :: HNil
.
Then black magic allows you to use Function2[String, String, Route]
((String, String) => Route
) to process String :: String :: HNil
. Without such magic you would have to use the method like this: {case a :: b :: HNil => ...}
.
Black magic
As @AlexIv noted:
There is an implicit conversion pimpApply
for every Directive[A :: B :: C ...]
that creates an object with method apply((a: A, b: B, c: C ...) => Route)
.
It accepts ApplyConverter
implicitly. Type member In
of ApplyConverter
represents an appropriate function (A, B, C ...) => Route
for every Directive[A :: B :: C ...]
.
There is no way to create such implicit values without macros or boilerplate code. So sbt-boilerplate
is used for ApplyConverter
generation. See ApplyConverterInstances.scala
.