servant/src/Servant/API/QQ.hs

96 lines
3.5 KiB
Haskell
Raw Normal View History

2014-10-28 13:22:11 +01:00
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# 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)
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
import Servant.API.Union
2014-10-28 13:22:11 +01:00
class ExpSYM repr' repr | repr -> repr', repr' -> repr where
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
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"
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
readEntry' [] _ = Nothing
2014-10-28 17:32:22 +01:00
readEntry' xs r = Just $ foldr1 (.) (tRepr <$> splitOn "/" xs) r
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"
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)
sitemap :: QuasiQuoter
sitemap = QuasiQuoter { quoteExp = undefined
, quotePat = undefined
, quoteType = return . readAll
, quoteDec = undefined
}
2014-10-28 13:22:11 +01:00