2014-10-28 13:22:11 +01:00
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
|
|
{-# LANGUAGE FunctionalDependencies #-}
|
|
|
|
{-# LANGUAGE DataKinds #-}
|
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
2014-10-28 15:22:28 +01:00
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
{-# LANGUAGE TemplateHaskell #-}
|
2014-10-28 13:22:11 +01:00
|
|
|
module Servant.API.QQ where
|
|
|
|
|
|
|
|
import Control.Applicative
|
|
|
|
import Data.List.Split (splitOn)
|
2014-10-28 15:22:28 +01:00
|
|
|
import Data.Maybe (mapMaybe)
|
|
|
|
import Language.Haskell.TH.Quote
|
2014-10-28 13:22:11 +01:00
|
|
|
import Language.Haskell.TH
|
2014-10-29 13:10:04 +01:00
|
|
|
import Safe (headMay)
|
2014-10-28 13:22:11 +01:00
|
|
|
|
|
|
|
import Servant.API.Capture
|
|
|
|
import Servant.API.Get
|
|
|
|
import Servant.API.Post
|
|
|
|
import Servant.API.Put
|
|
|
|
import Servant.API.Delete
|
2014-10-29 13:10:04 +01:00
|
|
|
import Servant.API.QueryParam
|
|
|
|
import Servant.API.ReqBody
|
2014-10-28 13:22:11 +01:00
|
|
|
import Servant.API.Sub
|
2014-10-28 15:22:28 +01:00
|
|
|
import Servant.API.Union
|
2014-10-28 13:22:11 +01:00
|
|
|
|
|
|
|
class ExpSYM repr' repr | repr -> repr', repr' -> repr where
|
2014-10-28 15:22:28 +01:00
|
|
|
lit :: String -> repr' -> repr
|
|
|
|
capture :: String -> String -> repr -> repr
|
2014-10-29 13:10:04 +01:00
|
|
|
reqBody :: String -> repr -> repr
|
|
|
|
queryParam :: String -> String -> repr -> repr
|
2014-10-28 15:22:28 +01:00
|
|
|
conj :: repr' -> repr -> repr
|
|
|
|
get :: String -> repr
|
|
|
|
post :: String -> repr
|
|
|
|
put :: String -> repr
|
|
|
|
delete :: String -> repr
|
|
|
|
|
|
|
|
infixr 6 >:
|
|
|
|
|
|
|
|
(>:) :: Type -> Type -> Type
|
|
|
|
(>:) = conj
|
2014-10-28 13:22:11 +01:00
|
|
|
|
2014-10-28 17:32:22 +01:00
|
|
|
|
2014-10-28 13:22:11 +01:00
|
|
|
instance ExpSYM Type Type where
|
2014-10-29 14:54:13 +01:00
|
|
|
lit name r = LitT (StrTyLit name) >: r
|
|
|
|
capture name typ r = AppT (AppT (ConT ''Capture) (LitT (StrTyLit name)))
|
|
|
|
(ConT $ mkName typ) >: r
|
|
|
|
reqBody typ r = AppT (ConT ''ReqBody) (ConT $ mkName typ) >: r
|
|
|
|
queryParam name typ r = AppT (AppT (ConT ''QueryParam) (LitT (StrTyLit name)))
|
|
|
|
(ConT $ mkName typ) >: r
|
|
|
|
conj x = AppT (AppT (ConT ''(:>)) x)
|
2014-10-29 13:10:04 +01:00
|
|
|
get typ = AppT (ConT ''Get) (ConT $ mkName typ)
|
|
|
|
post typ = AppT (ConT ''Post) (ConT $ mkName typ)
|
|
|
|
put typ = AppT (ConT ''Put) (ConT $ mkName typ)
|
|
|
|
delete "()" = ConT ''Delete
|
|
|
|
delete _ = error "Delete does not return a request body"
|
2014-10-28 15:22:28 +01:00
|
|
|
|
|
|
|
readEntry :: ExpSYM r r => [String] -> Maybe r
|
2014-10-28 13:22:11 +01:00
|
|
|
readEntry [] = Nothing
|
2014-10-28 17:32:22 +01:00
|
|
|
readEntry (met:xs:typ) = case met of
|
|
|
|
"GET" -> rd get
|
|
|
|
"POST" -> rd post
|
|
|
|
"PUT" -> rd put
|
|
|
|
"DELETE" -> rd delete
|
2014-10-28 13:22:11 +01:00
|
|
|
x -> error $ "Unknown method: " ++ x
|
2014-10-28 17:32:22 +01:00
|
|
|
where typ' = splitOn "->" $ concat typ
|
|
|
|
rd m = case typ' of
|
|
|
|
[] -> readEntry' xs $ m "()"
|
|
|
|
[rsp] -> readEntry' xs $ m rsp
|
2014-10-29 13:10:04 +01:00
|
|
|
(rqbd:[rsp]) -> readEntry' xs $ reqBody rqbd $ m rsp
|
2014-10-28 17:32:22 +01:00
|
|
|
_ -> error "Only functions of one argument allowed!"
|
2014-10-28 13:22:11 +01:00
|
|
|
readEntry x = error $ "Wrong number of elems in line: " ++ show x
|
|
|
|
|
2014-10-28 17:32:22 +01:00
|
|
|
readEntry' :: ExpSYM r r => String -> r -> Maybe r
|
2014-10-28 15:22:28 +01:00
|
|
|
readEntry' [] _ = Nothing
|
2014-10-28 17:32:22 +01:00
|
|
|
readEntry' xs r = Just $ foldr1 (.) (tRepr <$> splitOn "/" xs) r
|
2014-10-28 15:22:28 +01:00
|
|
|
where
|
|
|
|
tRepr y | [x] <- splitOn ":" y = lit x
|
2014-10-29 13:10:04 +01:00
|
|
|
| a:[b] <- splitOn ":" y = case headMay a of
|
|
|
|
Just '?' -> queryParam (tail a) b
|
|
|
|
Just _ -> capture a b
|
|
|
|
Nothing -> error "Expecting something after '/'"
|
2014-10-28 13:22:11 +01:00
|
|
|
| otherwise = error "Only one ':' per section"
|
2014-10-28 15:22:28 +01:00
|
|
|
|
|
|
|
readAll :: String -> Type
|
|
|
|
readAll s = foldr1 union $ mapMaybe readEntry $ words <$> lines s
|
|
|
|
where union :: Type -> Type -> Type
|
2014-10-29 14:54:13 +01:00
|
|
|
union a = AppT (AppT (ConT ''(:<|>)) a)
|
2014-10-28 15:22:28 +01:00
|
|
|
|
|
|
|
sitemap :: QuasiQuoter
|
|
|
|
sitemap = QuasiQuoter { quoteExp = undefined
|
|
|
|
, quotePat = undefined
|
|
|
|
, quoteType = return . readAll
|
|
|
|
, quoteDec = undefined
|
|
|
|
}
|
2014-10-28 13:22:11 +01:00
|
|
|
|