{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
import Trasa.Core
import Trasa.Server
import Data.Vinyl
import Data.Functor.Identity
import Data.Kind (Type)
import Text.Read (readMaybe)
import Network.Wai.Handler.Warp (withApplication)
import qualified Data.Text as T
import qualified Data.ByteString.Lazy.Char8 as LBSC
import qualified Network.HTTP.Client as HC
main :: IO ()
main = do
putStrLn "\nStarting trasa server test suite"
let app = serveWith
(mapQuery captureCodecToCaptureDecoding . metaQuery . meta)
(mapRequestBody (Many . pure . bodyCodecToBodyDecoding) . metaRequestBody . meta)
(mapResponseBody (Many . pure . bodyCodecToBodyEncoding) . metaResponseBody . meta)
handle
router
withApplication (return app) $ \port -> do
manager <- HC.newManager HC.defaultManagerSettings
attempt manager ("GET http://127.0.0.1:" ++ show port ++ "/") $ \x -> x
{ HC.requestHeaders = [("Accept","text/plain"),("ContentType","text/plain")]
}
attempt manager ("GET http://127.0.0.1:" ++ show port ++ "/hello") $ \x -> x
{ HC.requestHeaders = [("Accept","text/plain"),("ContentType","text/plain")]
}
return ()
attempt :: HC.Manager -> String -> (HC.Request -> HC.Request) -> IO ()
attempt mngr url modify = do
req <- HC.parseUrlThrow url
let req' = modify req
_ <- HC.httpLbs req' mngr
return ()
handle :: Route caps qrys req resp -> Rec Identity caps -> Rec Parameter qrys -> RequestBody Identity req -> TrasaT IO resp
handle r = case r of
EmptyR -> \_ _ _ -> return (55 :: Int)
HelloR -> \_ _ _ -> return (67 :: Int)
data Route :: [Type] -> [Param] -> Bodiedness -> Type -> Type where
EmptyR :: Route '[] '[] Bodyless Int
HelloR :: Route '[] '[] Bodyless Int
AdditionR :: Route '[Int,Int] '[Optional Int] Bodyless Int
IdentityR :: Route '[String] '[] Bodyless String
LeftPadR :: Route '[Int] '[] (Body String) String
TrickyOneR :: Route '[Int] '[] Bodyless String
TrickyTwoR :: Route '[Int,Int] '[] Bodyless String
prepare :: Route cs qs rq rp -> Arguments cs qs rq (Prepared Route rp)
prepare = prepareWith (metaPath . meta) (metaQuery . meta) (metaRequestBody . meta)
link :: Prepared Route rp -> Url
link = linkWith
(mapPath (CaptureEncoding . captureCodecEncode) . metaPath . meta)
(mapQuery captureCodecToCaptureEncoding . metaQuery . meta)
parse :: T.Text -> Maybe Content -> Either TrasaErr (Concealed Route)
parse url = parseWith
(mapQuery captureCodecToCaptureDecoding . metaQuery . meta)
(mapRequestBody (Many . pure . bodyCodecToBodyDecoding) . metaRequestBody . meta)
router
"GET"
(decodeUrl url)
allRoutes :: [Constructed Route]
allRoutes =
[ Constructed HelloR
, Constructed AdditionR
, Constructed IdentityR
, Constructed LeftPadR
, Constructed TrickyOneR
, Constructed TrickyTwoR
, Constructed EmptyR
]
router :: Router Route
router = routerWith
(metaMethod . meta)
(mapPath (CaptureDecoding . captureCodecDecode) . metaPath . meta)
allRoutes
data Meta ps qs rq rp = Meta
{ metaPath :: Path CaptureCodec ps
, metaQuery :: Rec (Query CaptureCodec) qs
, metaRequestBody :: RequestBody BodyCodec rq
, metaResponseBody :: ResponseBody BodyCodec rp
, metaMethod :: T.Text
}
meta :: Route ps qs rq rp -> Meta ps qs rq rp
meta x = case x of
EmptyR -> Meta
end
qend
bodyless (resp bodyInt) "GET"
HelloR -> Meta
(match "hello" ./ end)
qend
bodyless (resp bodyInt) "GET"
AdditionR -> Meta
(match "add" ./ capture int ./ capture int ./ end)
(optional "more" int .& qend)
bodyless (resp bodyInt) "GET"
IdentityR -> Meta
(match "identity" ./ capture string ./ end)
qend
bodyless (resp bodyString) "GET"
LeftPadR -> Meta
(match "pad" ./ match "left" ./ capture int ./ end)
qend
(body bodyString) (resp bodyString) "GET"
TrickyOneR -> Meta
(match "tricky" ./ capture int ./ match "one" ./ end)
qend
bodyless (resp bodyString) "GET"
TrickyTwoR -> Meta
(capture int ./ capture int ./ match "two" ./ end)
qend
bodyless (resp bodyString) "GET"
int :: CaptureCodec Int
int = CaptureCodec (T.pack . show) (readMaybe . T.unpack)
string :: CaptureCodec String
string = CaptureCodec T.pack (Just . T.unpack)
bodyString :: BodyCodec String
bodyString = BodyCodec (pure "text/plain") LBSC.pack (Right . LBSC.unpack)
bodyUnit :: BodyCodec ()
bodyUnit = BodyCodec (pure "text/plain") (const "") (const (Right ()))
note :: e -> Maybe a -> Either e a
note e Nothing = Left e
note _ (Just x) = Right x
bodyInt :: BodyCodec Int
bodyInt = BodyCodec (pure "text/plain") (LBSC.pack . show)
(note "Could not decode int" . readMaybe . LBSC.unpack)