servant-elm-0.1.0.0: src/Servant/Elm/Client.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Servant.Elm.Client where
import Data.Proxy (Proxy (Proxy))
import qualified Data.Text as T
import Elm (ToElmType, toElmDecoderWithSources,
toElmEncoderWithSources,
toElmTypeWithSources)
import GHC.TypeLits (KnownSymbol, symbolVal)
import Servant.API ((:<|>), (:>), Capture, Get, Post,
QueryFlag, QueryParam, QueryParams,
ReqBody)
import Servant.Foreign (ArgType (..), QueryArg (..), Segment (..),
SegmentType (..))
import Servant.Elm.Request (Request (..), addArgName, addDecoderDefs,
addEncoderDefs, addFnName, addFnSignature,
addTypeDefs, addUrlQueryStr,
addUrlSegment, defRequest, setBodyEncoder,
setDecoder, setHttpMethod)
elmClient :: (HasElmClient layout)
=> Proxy layout -> [Request]
elmClient p = elmClientWithRoute p defRequest
class HasElmClient layout where
elmClientWithRoute :: Proxy layout -> Request -> [Request]
-- a :<|> b
instance (HasElmClient a, HasElmClient b) => HasElmClient (a :<|> b) where
elmClientWithRoute Proxy result =
elmClientWithRoute (Proxy :: Proxy a) result ++
elmClientWithRoute (Proxy :: Proxy b) result
-- path :> rest
instance (KnownSymbol path, HasElmClient sublayout) => HasElmClient (path :> sublayout) where
elmClientWithRoute Proxy result =
elmClientWithRoute (Proxy :: Proxy sublayout)
((addFnName p . addUrlSegment segment) result)
where p = symbolVal (Proxy :: Proxy path)
segment = Segment (Static (T.pack p))
-- Capture name ArgType :> rest
instance (KnownSymbol capture, ToElmType a, HasElmClient sublayout)
=> HasElmClient (Capture capture a :> sublayout) where
elmClientWithRoute Proxy result =
elmClientWithRoute (Proxy :: Proxy sublayout)
((addTypeDefs tDefs
. addFnSignature typeName
. addFnName "by"
. addArgName argName
. addUrlSegment (Segment (Cap (T.pack argName, T.pack typeName)))) result)
where argProxy = Proxy :: Proxy a
argName = symbolVal (Proxy :: Proxy capture)
(typeName, tDefs) = toElmTypeWithSources argProxy
-- QueryFlag name :> rest
instance (KnownSymbol sym, HasElmClient sublayout)
=> HasElmClient (QueryFlag sym :> sublayout) where
elmClientWithRoute Proxy result =
elmClientWithRoute (Proxy :: Proxy sublayout)
((addArgName argName
. addFnSignature typeName
. addUrlQueryStr (QueryArg (T.pack argName, T.pack typeName) Flag)) result)
where argName = symbolVal (Proxy :: Proxy sym)
(typeName, _) = toElmTypeWithSources (Proxy :: Proxy Bool)
-- QueryParams name ArgType :> rest
instance (KnownSymbol sym, ToElmType a, HasElmClient sublayout)
=> HasElmClient (QueryParams sym a :> sublayout) where
elmClientWithRoute Proxy result =
elmClientWithRoute (Proxy :: Proxy sublayout)
((addArgName argName
. addTypeDefs tDefs
. addFnSignature typeName
. addUrlQueryStr (QueryArg (T.pack argName, T.pack typeName) List)) result)
where argName = symbolVal (Proxy :: Proxy sym)
(typeName, tDefs) = toElmTypeWithSources (Proxy :: Proxy [a])
-- QueryParam name ArgType :> rest
instance (KnownSymbol sym, ToElmType a, HasElmClient sublayout)
=> HasElmClient (QueryParam sym a :> sublayout) where
elmClientWithRoute Proxy result =
elmClientWithRoute (Proxy :: Proxy sublayout)
((addArgName argName
. addTypeDefs tDefs
. addFnSignature typeName
. addUrlQueryStr (QueryArg (T.pack argName, T.pack typeName) Normal)) result)
where argName = symbolVal (Proxy :: Proxy sym)
(typeName, tDefs) = toElmTypeWithSources (Proxy :: Proxy (Maybe a))
-- ReqBody '[cts] BodyType :> rest
instance (ToElmType body, HasElmClient sublayout)
=> HasElmClient (ReqBody (ct ': cts) body :> sublayout) where
elmClientWithRoute Proxy request =
elmClientWithRoute (Proxy :: Proxy sublayout)
((addArgName "body"
. addTypeDefs typeDefs
. addFnSignature typeName
. setBodyEncoder bodyEncoder
. addEncoderDefs encoderDefs) request)
where (typeName, typeDefs) = toElmTypeWithSources (Proxy :: Proxy body)
(bodyEncoder, encoderDefs) = toElmEncoderWithSources (Proxy :: Proxy body)
-- Get '[cts] RequestType
instance {-# OVERLAPPABLE #-} (ToElmType apiRequest) => HasElmClient (Get (ct ': cts) apiRequest) where
elmClientWithRoute Proxy request =
[completeRequestWithType (Proxy :: Proxy apiRequest) "GET" request]
-- Get '[cts] ()
instance {-# OVERLAPPING #-} HasElmClient (Get (ct ': cts) ()) where
elmClientWithRoute Proxy request =
[completeRequest "GET" request]
-- Post '[cts] RequestType
instance {-# OVERLAPPABLE #-} (ToElmType apiRequest) => HasElmClient (Post (ct ': cts) apiRequest) where
elmClientWithRoute Proxy request =
[completeRequestWithType (Proxy :: Proxy apiRequest) "POST" request]
-- Post '[cts] ()
instance {-# OVERLAPPING #-} HasElmClient (Post (ct ': cts) ()) where
elmClientWithRoute Proxy request =
[completeRequest "POST" request]
completeRequestWithType :: ToElmType a => Proxy a -> String -> Request -> Request
completeRequestWithType proxy method =
setHttpMethod method
. addFnSignature typeName
. addTypeDefs typeDefs
. addDecoderDefs decDefs
. setDecoder dec
where
(dec, decDefs) = toElmDecoderWithSources proxy
(typeName, typeDefs) = toElmTypeWithSources proxy
completeRequest :: String -> Request -> Request
completeRequest method =
setHttpMethod method
. addFnSignature "()"
. setDecoder "(succeed ())"