Safe Haskell | None |
---|---|
Language | Haskell2010 |
Synopsis
- data Bodiedness
- data Content = Content {}
- data Payload = Payload {
- payloadUrl :: !Url
- payloadContent :: !(Maybe Content)
- payloadAccepts :: !(NonEmpty MediaType)
- data Router route
- data Prepared :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type -> Type where
- data Concealed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type where
- data Constructed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type where
- Constructed :: !(route captures querys request response) -> Constructed route
- conceal :: Prepared route response -> Concealed route
- concealedToPrepared :: forall route a. Concealed route -> (forall resp. Prepared route resp -> a) -> a
- mapConstructed :: (forall caps qrys req resp. sub caps qrys req resp -> route caps qrys req resp) -> Constructed sub -> Constructed route
- data Method
- encodeMethod :: Method -> Text
- decodeMethod :: Text -> Method
- newtype QueryString = QueryString {}
- encodeQuery :: QueryString -> Query
- decodeQuery :: Query -> QueryString
- data Url = Url {
- urlPath :: ![Text]
- urlQueryString :: !QueryString
- encodeUrl :: Url -> Text
- decodeUrl :: Text -> Url
- data TrasaErr = TrasaErr {}
- status :: Status -> TrasaErr
- prepareWith :: (forall caps qrys req resp. route caps qrys req resp -> Meta capCodec qryCodec reqCodec respCodec caps qrys req resp) -> route captures query request response -> Arguments captures query request (Prepared route response)
- linkWith :: forall route response reqCodec respCodec. (forall caps qrys req resp. route caps qrys req resp -> Meta CaptureEncoding CaptureEncoding reqCodec respCodec caps qrys req resp) -> Prepared route response -> Url
- dispatchWith :: forall route m. Applicative m => (forall caps qrys req resp. route caps qrys req resp -> MetaServer caps qrys req resp) -> (forall caps qrys req resp. route caps qrys req resp -> Rec Identity caps -> Rec Parameter qrys -> RequestBody Identity req -> m resp) -> Router route -> Method -> [MediaType] -> Url -> Maybe Content -> m (Either TrasaErr Content)
- parseWith :: forall route capCodec respCodec. (forall caps qrys req resp. route caps qrys req resp -> Meta capCodec CaptureDecoding (Many BodyDecoding) respCodec caps qrys req resp) -> Router route -> Method -> Url -> Maybe Content -> Either TrasaErr (Concealed route)
- payloadWith :: forall route response. (forall caps qrys req resp. route caps qrys req resp -> MetaClient caps qrys req resp) -> Prepared route response -> Payload
- requestWith :: Functor m => (forall caps qrys req resp. route caps qrys req resp -> MetaClient caps qrys req resp) -> (Method -> Url -> Maybe Content -> NonEmpty MediaType -> m (Either TrasaErr Content)) -> Prepared route response -> m (Either TrasaErr response)
- routerWith :: forall route qryCodec reqCodec respCodec. (forall caps qrys req resp. route caps qrys req resp -> Meta CaptureDecoding qryCodec reqCodec respCodec caps qrys req resp) -> [Constructed route] -> Router route
- data Path :: (Type -> Type) -> [Type] -> Type where
- PathNil :: Path cap '[]
- PathConsCapture :: !(cap a) -> !(Path cap as) -> Path cap (a ': as)
- PathConsMatch :: !Text -> !(Path cap as) -> Path cap as
- match :: Text -> Path cpf caps -> Path cpf caps
- capture :: cpf cap -> Path cpf caps -> Path cpf (cap ': caps)
- end :: Path cpf '[]
- (./) :: (a -> b) -> a -> b
- mapPath :: (forall x. cf x -> cf' x) -> Path cf ps -> Path cf' ps
- appendPath :: Path f as -> Path f bs -> Path f (as ++ bs)
- data Param
- data Query :: (Type -> Type) -> Param -> Type where
- data Parameter :: Param -> Type where
- ParameterFlag :: !Bool -> Parameter Flag
- ParameterOptional :: !(Maybe a) -> Parameter (Optional a)
- ParameterList :: ![a] -> Parameter (List a)
- data Rec (a :: k -> Type) (b :: [k]) :: forall k. (k -> Type) -> [k] -> Type where
- demoteParameter :: Parameter param -> ParamBase param
- flag :: Text -> Query cpf Flag
- optional :: Text -> cpf query -> Query cpf (Optional query)
- list :: Text -> cpf query -> Query cpf (List query)
- qend :: Rec (Query qpf) '[]
- (.&) :: Query qpf q -> Rec (Query qpf) qs -> Rec (Query qpf) (q ': qs)
- mapQuery :: (forall x. f x -> g x) -> Rec (Query f) qs -> Rec (Query g) qs
- data RequestBody :: (Type -> Type) -> Bodiedness -> Type where
- RequestBodyPresent :: !(f a) -> RequestBody f (Body a)
- RequestBodyAbsent :: RequestBody f Bodyless
- body :: rqf req -> RequestBody rqf (Body req)
- bodyless :: RequestBody rqf Bodyless
- encodeRequestBody :: RequestBody (Many BodyEncoding) request -> RequestBody Identity request -> Maybe Content
- decodeRequestBody :: RequestBody (Many BodyDecoding) req -> Maybe Content -> Either TrasaErr (RequestBody Identity req)
- mapRequestBody :: (forall x. rqf x -> rqf' x) -> RequestBody rqf request -> RequestBody rqf' request
- newtype ResponseBody rpf response = ResponseBody {
- getResponseBody :: rpf response
- resp :: rpf resp -> ResponseBody rpf resp
- encodeResponseBody :: forall response. [MediaType] -> ResponseBody (Many BodyEncoding) response -> response -> Either TrasaErr Content
- decodeResponseBody :: ResponseBody (Many BodyDecoding) response -> Content -> Either TrasaErr response
- mapResponseBody :: (forall x. rpf x -> rpf' x) -> ResponseBody rpf request -> ResponseBody rpf' request
- newtype Many f a = Many {}
- one :: f a -> Many f a
- mapMany :: (forall x. f x -> g x) -> Many f a -> Many g a
- data Meta capCodec qryCodec reqCodec respCodec caps qrys req resp = Meta {
- metaPath :: !(Path capCodec caps)
- metaQuery :: !(Rec (Query qryCodec) qrys)
- metaRequestBody :: !(RequestBody reqCodec req)
- metaResponseBody :: !(ResponseBody respCodec resp)
- metaMethod :: !Method
- type MetaBuilder = Meta CaptureCodec CaptureCodec BodyCodec BodyCodec
- metaBuilderToMetaCodec :: Meta capCodec qryCodec reqCodec respCodec caps qrys req resp -> Meta capCodec qryCodec (Many reqCodec) (Many respCodec) caps qrys req resp
- type MetaCodec = Meta CaptureCodec CaptureCodec (Many BodyCodec) (Many BodyCodec)
- type MetaClient = Meta CaptureEncoding CaptureEncoding (Many BodyEncoding) (Many BodyDecoding)
- metaCodecToMetaClient :: MetaCodec caps qrys req resp -> MetaClient caps qrys req resp
- type MetaServer = Meta CaptureDecoding CaptureDecoding (Many BodyDecoding) (Many BodyEncoding)
- metaCodecToMetaServer :: MetaCodec caps qrys req resp -> MetaServer caps qrys req resp
- mapMetaPath :: (forall x. cf x -> cg x) -> Meta cf qryCodec reqCodec respCodec caps qrys req resp -> Meta cg qryCodec reqCodec respCodec caps qrys req resp
- mapMetaQuery :: (forall x. qf x -> qg x) -> Meta capCodec qf reqCodec respCodec caps qrys req resp -> Meta capCodec qg reqCodec respCodec caps qrys req resp
- mapMetaRequestBody :: (forall x. rf x -> rg x) -> Meta capCodec qryCodec rf respCodec caps qrys req resp -> Meta capCodec qryCodec rg respCodec caps qrys req resp
- mapMetaResponseBody :: (forall x. rf x -> rg x) -> Meta capCodec qryCodec reqCodec rf caps qrys req resp -> Meta capCodec qryCodec reqCodec rg caps qrys req resp
- mapMeta :: (forall x. capCodec1 x -> capCodec2 x) -> (forall x. qryCodec1 x -> qryCodec2 x) -> (forall x. reqCodec1 x -> reqCodec2 x) -> (forall x. respCodec1 x -> respCodec2 x) -> Meta capCodec1 qryCodec1 reqCodec1 respCodec1 caps qrys req resp -> Meta capCodec2 qryCodec2 reqCodec2 respCodec2 caps qrys req resp
- newtype CaptureEncoding a = CaptureEncoding {
- appCaptureEncoding :: a -> Text
- class HasCaptureEncoding capStrategy where
- captureEncoding :: capStrategy a -> CaptureEncoding a
- newtype CaptureDecoding a = CaptureDecoding {
- appCaptureDecoding :: Text -> Maybe a
- class HasCaptureDecoding capStrategy where
- captureDecoding :: capStrategy a -> CaptureDecoding a
- data CaptureCodec a = CaptureCodec {
- captureCodecEncode :: a -> Text
- captureCodecDecode :: Text -> Maybe a
- class HasCaptureCodec capStrategy where
- captureCodec :: capStrategy a -> CaptureCodec a
- data BodyEncoding a = BodyEncoding {}
- class HasBodyEncoding bodyStrategy where
- bodyEncoding :: bodyStrategy a -> BodyEncoding a
- data BodyDecoding a = BodyDecoding {}
- class HasBodyDecoding bodyStrategy where
- bodyDecoding :: bodyStrategy a -> BodyDecoding a
- data BodyCodec a = BodyCodec {
- bodyCodecNames :: NonEmpty MediaType
- bodyCodecEncode :: a -> ByteString
- bodyCodecDecode :: ByteString -> Either Text a
- class HasBodyCodec bodyStrategy where
- captureCodecToCaptureEncoding :: CaptureCodec a -> CaptureEncoding a
- captureCodecToCaptureDecoding :: CaptureCodec a -> CaptureDecoding a
- bodyCodecToBodyEncoding :: BodyCodec a -> BodyEncoding a
- bodyCodecToBodyDecoding :: BodyCodec a -> BodyDecoding a
- showReadCaptureCodec :: (Show a, Read a) => CaptureCodec a
- showReadBodyCodec :: (Show a, Read a) => BodyCodec a
- type family ParamBase (param :: Param) :: Type where ...
- type family Arguments (pieces :: [Type]) (querys :: [Param]) (body :: Bodiedness) (result :: Type) :: Type where ...
- handler :: forall captures querys request x. Rec Identity captures -> Rec Parameter querys -> RequestBody Identity request -> Arguments captures querys request x -> x
- prettyRouter :: Router route -> String
Types
data Bodiedness Source #
the type of the HTTP message body (json, text, etc) https://en.wikipedia.org/wiki/HTTP_message_body
The HTTP content type and body.
Content | |
|
Payload | |
|
Existential
data Prepared :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type -> Type where Source #
Includes the route, path, query parameters, and request body.
data Concealed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type where Source #
Only needed to implement parseWith
. Most users do not need this.
If you need to create a route hierarchy to provide breadcrumbs,
then you will need this.
data Constructed :: ([Type] -> [Param] -> Bodiedness -> Type -> Type) -> Type where Source #
A route with all types hidden: the captures, the request body, and the response body. This is needed so that users can enumerate over all the routes.
Constructed :: !(route captures querys request response) -> Constructed route |
concealedToPrepared :: forall route a. Concealed route -> (forall resp. Prepared route resp -> a) -> a Source #
mapConstructed :: (forall caps qrys req resp. sub caps qrys req resp -> route caps qrys req resp) -> Constructed sub -> Constructed route Source #
Request Types
Method
encodeMethod :: Method -> Text Source #
decodeMethod :: Text -> Method Source #
Queries
newtype QueryString Source #
Instances
Eq QueryString Source # | |
Defined in Trasa.Url (==) :: QueryString -> QueryString -> Bool # (/=) :: QueryString -> QueryString -> Bool # |
encodeQuery :: QueryString -> Query Source #
decodeQuery :: Query -> QueryString Source #
Url
Url | |
|
Errors
Instances
Eq TrasaErr Source # | |
Ord TrasaErr Source # | |
Defined in Trasa.Error | |
Show TrasaErr Source # | |
Exception TrasaErr Source # | |
Defined in Trasa.Error toException :: TrasaErr -> SomeException # fromException :: SomeException -> Maybe TrasaErr # displayException :: TrasaErr -> String # |
Using Routes
:: (forall caps qrys req resp. route caps qrys req resp -> Meta capCodec qryCodec reqCodec respCodec caps qrys req resp) | |
-> route captures query request response | The route to prepare |
-> Arguments captures query request (Prepared route response) |
Used my users to define a function called prepare, see tutorial
:: (forall caps qrys req resp. route caps qrys req resp -> Meta CaptureEncoding CaptureEncoding reqCodec respCodec caps qrys req resp) | |
-> Prepared route response | The route to encode |
-> Url |
Generate a Url
for use in hyperlinks.
:: Applicative m | |
=> (forall caps qrys req resp. route caps qrys req resp -> MetaServer caps qrys req resp) | |
-> (forall caps qrys req resp. route caps qrys req resp -> Rec Identity caps -> Rec Parameter qrys -> RequestBody Identity req -> m resp) | |
-> Router route | Router |
-> Method | Method |
-> [MediaType] | Accept headers |
-> Url | Everything after the authority |
-> Maybe Content | Content type and request body |
-> m (Either TrasaErr Content) | Encoded response |
Only useful to implement packages like 'trasa-server'
:: (forall caps qrys req resp. route caps qrys req resp -> Meta capCodec CaptureDecoding (Many BodyDecoding) respCodec caps qrys req resp) | |
-> Router route | Router |
-> Method | Request Method |
-> Url | Everything after the authority |
-> Maybe Content | Request content type and body |
-> Either TrasaErr (Concealed route) |
Parses the path, the querystring, and the request body.
:: (forall caps qrys req resp. route caps qrys req resp -> MetaClient caps qrys req resp) | |
-> Prepared route response | The route to be payload encoded |
-> Payload |
Only useful for library authors
routerWith :: forall route qryCodec reqCodec respCodec. (forall caps qrys req resp. route caps qrys req resp -> Meta CaptureDecoding qryCodec reqCodec respCodec caps qrys req resp) -> [Constructed route] -> Router route Source #
Build a router from all the possible routes, and methods to turn routes into needed metadata
Defining Routes
Path
data Path :: (Type -> Type) -> [Type] -> Type where Source #
PathNil :: Path cap '[] | |
PathConsCapture :: !(cap a) -> !(Path cap as) -> Path cap (a ': as) | |
PathConsMatch :: !Text -> !(Path cap as) -> Path cap as |
(./) :: (a -> b) -> a -> b infixr 7 Source #
flipped ($), useful for constructing routes. e.g. > match "add" . capture int . capture int ./ end
Query
data Parameter :: Param -> Type where Source #
ParameterFlag :: !Bool -> Parameter Flag | |
ParameterOptional :: !(Maybe a) -> Parameter (Optional a) | |
ParameterList :: ![a] -> Parameter (List a) |
data Rec (a :: k -> Type) (b :: [k]) :: forall k. (k -> Type) -> [k] -> Type where #
RecNil :: forall k (a :: k -> Type) (b :: [k]). Rec a ([] :: [k]) | |
RecCons :: forall k (a :: k -> Type) (b :: [k]) (r :: k) (rs :: [k]). a r -> Rec a rs -> Rec a (r ': rs) |
Instances
demoteParameter :: Parameter param -> ParamBase param Source #
Request Body
data RequestBody :: (Type -> Type) -> Bodiedness -> Type where Source #
RequestBodyPresent :: !(f a) -> RequestBody f (Body a) | |
RequestBodyAbsent :: RequestBody f Bodyless |
body :: rqf req -> RequestBody rqf (Body req) Source #
bodyless :: RequestBody rqf Bodyless Source #
encodeRequestBody :: RequestBody (Many BodyEncoding) request -> RequestBody Identity request -> Maybe Content Source #
decodeRequestBody :: RequestBody (Many BodyDecoding) req -> Maybe Content -> Either TrasaErr (RequestBody Identity req) Source #
mapRequestBody :: (forall x. rqf x -> rqf' x) -> RequestBody rqf request -> RequestBody rqf' request Source #
Response Body
newtype ResponseBody rpf response Source #
ResponseBody | |
|
resp :: rpf resp -> ResponseBody rpf resp Source #
encodeResponseBody :: forall response. [MediaType] -> ResponseBody (Many BodyEncoding) response -> response -> Either TrasaErr Content Source #
decodeResponseBody :: ResponseBody (Many BodyDecoding) response -> Content -> Either TrasaErr response Source #
mapResponseBody :: (forall x. rpf x -> rpf' x) -> ResponseBody rpf request -> ResponseBody rpf' request Source #
Many
Instances
Functor f => Functor (Many f) Source # | |
Applicative f => Applicative (Many f) Source # | |
Meta
data Meta capCodec qryCodec reqCodec respCodec caps qrys req resp Source #
Meta | |
|
metaBuilderToMetaCodec :: Meta capCodec qryCodec reqCodec respCodec caps qrys req resp -> Meta capCodec qryCodec (Many reqCodec) (Many respCodec) caps qrys req resp Source #
This function is a more general way to transform MetaBuilder
into MetaCodec
.
It wraps the req and resp codecs in Many.
type MetaCodec = Meta CaptureCodec CaptureCodec (Many BodyCodec) (Many BodyCodec) Source #
type MetaClient = Meta CaptureEncoding CaptureEncoding (Many BodyEncoding) (Many BodyDecoding) Source #
metaCodecToMetaClient :: MetaCodec caps qrys req resp -> MetaClient caps qrys req resp Source #
type MetaServer = Meta CaptureDecoding CaptureDecoding (Many BodyDecoding) (Many BodyEncoding) Source #
metaCodecToMetaServer :: MetaCodec caps qrys req resp -> MetaServer caps qrys req resp Source #
mapMetaPath :: (forall x. cf x -> cg x) -> Meta cf qryCodec reqCodec respCodec caps qrys req resp -> Meta cg qryCodec reqCodec respCodec caps qrys req resp Source #
mapMetaQuery :: (forall x. qf x -> qg x) -> Meta capCodec qf reqCodec respCodec caps qrys req resp -> Meta capCodec qg reqCodec respCodec caps qrys req resp Source #
mapMetaRequestBody :: (forall x. rf x -> rg x) -> Meta capCodec qryCodec rf respCodec caps qrys req resp -> Meta capCodec qryCodec rg respCodec caps qrys req resp Source #
mapMetaResponseBody :: (forall x. rf x -> rg x) -> Meta capCodec qryCodec reqCodec rf caps qrys req resp -> Meta capCodec qryCodec reqCodec rg caps qrys req resp Source #
mapMeta :: (forall x. capCodec1 x -> capCodec2 x) -> (forall x. qryCodec1 x -> qryCodec2 x) -> (forall x. reqCodec1 x -> reqCodec2 x) -> (forall x. respCodec1 x -> respCodec2 x) -> Meta capCodec1 qryCodec1 reqCodec1 respCodec1 caps qrys req resp -> Meta capCodec2 qryCodec2 reqCodec2 respCodec2 caps qrys req resp Source #
Codecs
newtype CaptureEncoding a Source #
CaptureEncoding | |
|
Instances
HasCaptureEncoding CaptureEncoding Source # | |
Defined in Trasa.Codec captureEncoding :: CaptureEncoding a -> CaptureEncoding a Source # |
class HasCaptureEncoding capStrategy where Source #
captureEncoding :: capStrategy a -> CaptureEncoding a Source #
Instances
HasCaptureEncoding CaptureCodec Source # | |
Defined in Trasa.Codec captureEncoding :: CaptureCodec a -> CaptureEncoding a Source # | |
HasCaptureEncoding CaptureEncoding Source # | |
Defined in Trasa.Codec captureEncoding :: CaptureEncoding a -> CaptureEncoding a Source # |
newtype CaptureDecoding a Source #
CaptureDecoding | |
|
Instances
HasCaptureDecoding CaptureDecoding Source # | |
Defined in Trasa.Codec captureDecoding :: CaptureDecoding a -> CaptureDecoding a Source # |
class HasCaptureDecoding capStrategy where Source #
captureDecoding :: capStrategy a -> CaptureDecoding a Source #
Instances
HasCaptureDecoding CaptureCodec Source # | |
Defined in Trasa.Codec captureDecoding :: CaptureCodec a -> CaptureDecoding a Source # | |
HasCaptureDecoding CaptureDecoding Source # | |
Defined in Trasa.Codec captureDecoding :: CaptureDecoding a -> CaptureDecoding a Source # |
data CaptureCodec a Source #
CaptureCodec | |
|
Instances
HasCaptureCodec CaptureCodec Source # | |
Defined in Trasa.Codec captureCodec :: CaptureCodec a -> CaptureCodec a Source # | |
HasCaptureDecoding CaptureCodec Source # | |
Defined in Trasa.Codec captureDecoding :: CaptureCodec a -> CaptureDecoding a Source # | |
HasCaptureEncoding CaptureCodec Source # | |
Defined in Trasa.Codec captureEncoding :: CaptureCodec a -> CaptureEncoding a Source # |
class HasCaptureCodec capStrategy where Source #
captureCodec :: capStrategy a -> CaptureCodec a Source #
Instances
HasCaptureCodec CaptureCodec Source # | |
Defined in Trasa.Codec captureCodec :: CaptureCodec a -> CaptureCodec a Source # |
data BodyEncoding a Source #
Instances
HasBodyEncoding BodyEncoding Source # | |
Defined in Trasa.Codec bodyEncoding :: BodyEncoding a -> BodyEncoding a Source # |
class HasBodyEncoding bodyStrategy where Source #
bodyEncoding :: bodyStrategy a -> BodyEncoding a Source #
Instances
HasBodyEncoding BodyCodec Source # | |
Defined in Trasa.Codec bodyEncoding :: BodyCodec a -> BodyEncoding a Source # | |
HasBodyEncoding BodyEncoding Source # | |
Defined in Trasa.Codec bodyEncoding :: BodyEncoding a -> BodyEncoding a Source # |
data BodyDecoding a Source #
Instances
HasBodyDecoding BodyDecoding Source # | |
Defined in Trasa.Codec bodyDecoding :: BodyDecoding a -> BodyDecoding a Source # |
class HasBodyDecoding bodyStrategy where Source #
bodyDecoding :: bodyStrategy a -> BodyDecoding a Source #
Instances
HasBodyDecoding BodyCodec Source # | |
Defined in Trasa.Codec bodyDecoding :: BodyCodec a -> BodyDecoding a Source # | |
HasBodyDecoding BodyDecoding Source # | |
Defined in Trasa.Codec bodyDecoding :: BodyDecoding a -> BodyDecoding a Source # |
BodyCodec | |
|
Instances
HasBodyCodec BodyCodec Source # | |
HasBodyDecoding BodyCodec Source # | |
Defined in Trasa.Codec bodyDecoding :: BodyCodec a -> BodyDecoding a Source # | |
HasBodyEncoding BodyCodec Source # | |
Defined in Trasa.Codec bodyEncoding :: BodyCodec a -> BodyEncoding a Source # |
class HasBodyCodec bodyStrategy where Source #
Instances
HasBodyCodec BodyCodec Source # | |
Converting Codecs
bodyCodecToBodyEncoding :: BodyCodec a -> BodyEncoding a Source #
bodyCodecToBodyDecoding :: BodyCodec a -> BodyDecoding a Source #
Type Class based Codecs
showReadCaptureCodec :: (Show a, Read a) => CaptureCodec a Source #
Argument Currying
type family Arguments (pieces :: [Type]) (querys :: [Param]) (body :: Bodiedness) (result :: Type) :: Type where ... Source #
A closed, total type family provided as a convenience to end users.
Other function is this library take advantage of Arguments
to allow
end users use normal function application. Without this, users would
need to write out Record
and RequestBody
values by hand, which
is tedious.
>>>
:kind! Arguments '[Int,Bool] '[Flag,Optional Double,List Int] 'Bodyless Double
Arguments '[Int,Bool] '[Flag,Optional Double,List Int] 'Bodyless Double :: * = Int -> Bool -> Bool -> Maybe Double -> [Int] -> Double
handler :: forall captures querys request x. Rec Identity captures -> Rec Parameter querys -> RequestBody Identity request -> Arguments captures querys request x -> x Source #
Uncurry the arguments type family
Helpers
prettyRouter :: Router route -> String Source #
Pretty prints a router, using indentation to show nesting of routes under a common prefix. This also shows the request methods that each route accepts. If there are any trivially overlapped routes, the appends are asterisk to the method name for which the routes are overlapped.