{-|

This module provides a ready to use implementation of `web-routes` for Snap.

To get going, you'll need to add a few things to `Application.hs`.

> {-# LANGUAGE DeriveGeneric     #-}
> {-# LANGUAGE FlexibleInstances #-}
> {-# LANGUAGE TypeFamilies      #-}

DeriveGeneric is used to derive the `PathInfo` instance for your URL data type,
the rest are needed by `web-routes`.

> import Data.Text (Text)
> import Snap.Web.Routes

"Snap.Web.Routes" exports the data types needed to define your `PathInfo` and
`MonadRoute` instances below.

> data AppUrl
>     = Count Int
>     | Echo Text
>     | Paths [Text]
>       deriving (Generic)

Define your application's URL data type. Deriving a `Generic` instance gives
you a `PathInfo` instance for free.

> data App = App
>     { _routeFn :: AppUrl -> [(Text, Maybe Text)] -> Text
>     }

Extend your App type to include a routing function.

> instance PathInfo AppUrl

Get your free PathInfo instance. Alternatives are to use `web-routes-th` or
implement PathInfo yourself.

> instance MonadRoute (Handler App App) where
>    type URL (Handler App App) = AppUrl
>    askRouteFn = gets _routeFn

Define your MonadRoute instance. In particular, `type URL (Handler App App)`
must be set to your URL data type defined above and `askRouteFn` should point
to the routing function you added to your App type.

Moving on to `Site.hs`.

> import Snap.Web.Routes

Snap.Web.Routes provides a convenience router function you'll need.

> routes :: [(ByteString, Handler App App ())]
> routes = [ ("",          serveDirectory "static")
>          , ("",          routeWith routeAppUrl)
>          ]

Add your routes to the bottom of the routes list using routeWith.

> routeAppUrl :: AppUrl -> Handler App App ()
> routeAppUrl appUrl =
>     case appUrl of
>       (Count n)   -> writeText $ ("Count = " `T.append` (T.pack $ show n))
>       (Echo text) -> echo text
>       (Paths ps)  -> writeText $ T.intercalate " " ps

> echo :: T.Text -> Handler App App ()
> echo msg = heistLocal (bindString "message" msg) $ render "echo"

Define the handler for each data constructor in your URL data type.

> app :: SnapletInit App App
> app = makeSnaplet "app" "An example application with snap-web-routes." Nothing $ do
>     addRoutes routes
>     return $ App renderRoute

Lastly, add the routing function to your app. If you prefixed the routes in routeWith:

>          , ("/prefix",          routeWith routeAppUrl)

then use `renderRouteWithPrefix` instead:

>     return . App $ renderRouteWithPrefix "/prefix"

|-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies      #-}

module Snap.Web.Routes
  ( renderRoute
  , renderRouteWithPrefix
  , routeWith
  , heistUrl
  , gets
  , Generic
  , MonadRoute(..)
  , PathInfo(..)
  ) where

import Control.Monad.State (lift, gets)
import Data.Text (Text, append, pack)
import Heist (HeistT)
import Snap.Core
import Snap.Web.Routes.Heist
import Snap.Snaplet
import Web.Routes


instance (MonadRoute m) => MonadRoute (HeistT n m) where
    type URL (HeistT n m) = URL m
    askRouteFn = lift askRouteFn


routeWith :: (PathInfo url, MonadSnap m) => (url -> m ()) -> m ()
routeWith router =
    do rq <- getRequest
       case fromPathInfo $ rqPathInfo rq of
         (Left e) -> writeText (pack e)
         (Right url) -> router url


renderRoute :: PathInfo url => url -> [(Text, Maybe Text)] -> Text
renderRoute = renderRouteWithPrefix ""


renderRouteWithPrefix :: PathInfo url => Text -> url -> [(Text, Maybe Text)] -> Text
renderRouteWithPrefix prefix url params = prefix `append` toPathInfoParams url params