Drop EitherT in favor of ExceptT

This commit is contained in:
Index Int 2015-09-12 15:11:24 +03:00
parent 961887cc95
commit f2f7b061d2
22 changed files with 158 additions and 174 deletions

View file

@ -13,8 +13,8 @@ type MyApi = "books" :> Get '[JSON] [Book] -- GET /books
myApi :: Proxy MyApi myApi :: Proxy MyApi
myApi = Proxy myApi = Proxy
getAllBooks :: EitherT String IO [Book] getAllBooks :: ExceptT String IO [Book]
postNewBook :: Book -> EitherT String IO Book postNewBook :: Book -> ExceptT String IO Book
-- 'client' allows you to produce operations to query an API from a client. -- 'client' allows you to produce operations to query an API from a client.
(getAllBooks :<|> postNewBook) = client myApi host (getAllBooks :<|> postNewBook) = client myApi host
where host = BaseUrl Http "localhost" 8080 where host = BaseUrl Http "localhost" 8080

View file

@ -33,7 +33,6 @@ library
, aeson , aeson
, attoparsec , attoparsec
, bytestring , bytestring
, either
, exceptions , exceptions
, http-client , http-client
, http-client-tls , http-client-tls
@ -61,10 +60,10 @@ test-suite spec
, Servant.Common.BaseUrlSpec , Servant.Common.BaseUrlSpec
build-depends: build-depends:
base == 4.* base == 4.*
, transformers
, aeson , aeson
, bytestring , bytestring
, deepseq , deepseq
, either
, hspec == 2.* , hspec == 2.*
, http-client , http-client
, http-media , http-media

View file

@ -25,7 +25,7 @@ module Servant.Client
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
#endif #endif
import Control.Monad import Control.Monad
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
import Data.List import Data.List
import Data.Proxy import Data.Proxy
@ -50,8 +50,8 @@ import Servant.Common.Req
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getAllBooks :: EitherT String IO [Book] -- > getAllBooks :: ExceptT String IO [Book]
-- > postNewBook :: Book -> EitherT String IO Book -- > postNewBook :: Book -> ExceptT String IO Book
-- > (getAllBooks :<|> postNewBook) = client myApi host -- > (getAllBooks :<|> postNewBook) = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
client :: HasClient layout => Proxy layout -> BaseUrl -> Client layout client :: HasClient layout => Proxy layout -> BaseUrl -> Client layout
@ -76,8 +76,8 @@ class HasClient layout where
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getAllBooks :: EitherT String IO [Book] -- > getAllBooks :: ExceptT String IO [Book]
-- > postNewBook :: Book -> EitherT String IO Book -- > postNewBook :: Book -> ExceptT String IO Book
-- > (getAllBooks :<|> postNewBook) = client myApi host -- > (getAllBooks :<|> postNewBook) = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
instance (HasClient a, HasClient b) => HasClient (a :<|> b) where instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
@ -102,7 +102,7 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBook :: Text -> EitherT String IO Book -- > getBook :: Text -> ExceptT String IO Book
-- > getBook = client myApi host -- > getBook = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBook" to query that endpoint -- > -- then you can just use "getBook" to query that endpoint
@ -129,7 +129,7 @@ instance
#endif #endif
-- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances -- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances
(MimeUnrender ct a, cts' ~ (ct ': cts)) => HasClient (Delete cts' a) where (MimeUnrender ct a, cts' ~ (ct ': cts)) => HasClient (Delete cts' a) where
type Client (Delete cts' a) = EitherT ServantError IO a type Client (Delete cts' a) = ExceptT ServantError IO a
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
snd <$> performRequestCT (Proxy :: Proxy ct) H.methodDelete req [200, 202] baseurl snd <$> performRequestCT (Proxy :: Proxy ct) H.methodDelete req [200, 202] baseurl
@ -140,7 +140,7 @@ instance
{-# OVERLAPPING #-} {-# OVERLAPPING #-}
#endif #endif
HasClient (Delete cts ()) where HasClient (Delete cts ()) where
type Client (Delete cts ()) = EitherT ServantError IO () type Client (Delete cts ()) = ExceptT ServantError IO ()
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
void $ performRequestNoBody H.methodDelete req [204] baseurl void $ performRequestNoBody H.methodDelete req [204] baseurl
@ -153,7 +153,7 @@ instance
-- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances -- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances
( MimeUnrender ct a, BuildHeadersTo ls, cts' ~ (ct ': cts) ( MimeUnrender ct a, BuildHeadersTo ls, cts' ~ (ct ': cts)
) => HasClient (Delete cts' (Headers ls a)) where ) => HasClient (Delete cts' (Headers ls a)) where
type Client (Delete cts' (Headers ls a)) = EitherT ServantError IO (Headers ls a) type Client (Delete cts' (Headers ls a)) = ExceptT ServantError IO (Headers ls a)
clientWithRoute Proxy req baseurl = do clientWithRoute Proxy req baseurl = do
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodDelete req [200, 202] baseurl (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodDelete req [200, 202] baseurl
return $ Headers { getResponse = resp return $ Headers { getResponse = resp
@ -169,7 +169,7 @@ instance
{-# OVERLAPPABLE #-} {-# OVERLAPPABLE #-}
#endif #endif
(MimeUnrender ct result) => HasClient (Get (ct ': cts) result) where (MimeUnrender ct result) => HasClient (Get (ct ': cts) result) where
type Client (Get (ct ': cts) result) = EitherT ServantError IO result type Client (Get (ct ': cts) result) = ExceptT ServantError IO result
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
snd <$> performRequestCT (Proxy :: Proxy ct) H.methodGet req [200, 203] baseurl snd <$> performRequestCT (Proxy :: Proxy ct) H.methodGet req [200, 203] baseurl
@ -180,7 +180,7 @@ instance
{-# OVERLAPPING #-} {-# OVERLAPPING #-}
#endif #endif
HasClient (Get (ct ': cts) ()) where HasClient (Get (ct ': cts) ()) where
type Client (Get (ct ': cts) ()) = EitherT ServantError IO () type Client (Get (ct ': cts) ()) = ExceptT ServantError IO ()
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
performRequestNoBody H.methodGet req [204] baseurl performRequestNoBody H.methodGet req [204] baseurl
@ -192,7 +192,7 @@ instance
#endif #endif
( MimeUnrender ct a, BuildHeadersTo ls ( MimeUnrender ct a, BuildHeadersTo ls
) => HasClient (Get (ct ': cts) (Headers ls a)) where ) => HasClient (Get (ct ': cts) (Headers ls a)) where
type Client (Get (ct ': cts) (Headers ls a)) = EitherT ServantError IO (Headers ls a) type Client (Get (ct ': cts) (Headers ls a)) = ExceptT ServantError IO (Headers ls a)
clientWithRoute Proxy req baseurl = do clientWithRoute Proxy req baseurl = do
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodGet req [200, 203, 204] baseurl (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodGet req [200, 203, 204] baseurl
return $ Headers { getResponse = resp return $ Headers { getResponse = resp
@ -220,7 +220,7 @@ instance
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > viewReferer :: Maybe Referer -> EitherT String IO Book -- > viewReferer :: Maybe Referer -> ExceptT String IO Book
-- > viewReferer = client myApi host -- > viewReferer = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "viewRefer" to query that endpoint -- > -- then you can just use "viewRefer" to query that endpoint
@ -250,7 +250,7 @@ instance
{-# OVERLAPPABLE #-} {-# OVERLAPPABLE #-}
#endif #endif
(MimeUnrender ct a) => HasClient (Post (ct ': cts) a) where (MimeUnrender ct a) => HasClient (Post (ct ': cts) a) where
type Client (Post (ct ': cts) a) = EitherT ServantError IO a type Client (Post (ct ': cts) a) = ExceptT ServantError IO a
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPost req [200,201] baseurl snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPost req [200,201] baseurl
@ -261,7 +261,7 @@ instance
{-# OVERLAPPING #-} {-# OVERLAPPING #-}
#endif #endif
HasClient (Post (ct ': cts) ()) where HasClient (Post (ct ': cts) ()) where
type Client (Post (ct ': cts) ()) = EitherT ServantError IO () type Client (Post (ct ': cts) ()) = ExceptT ServantError IO ()
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
void $ performRequestNoBody H.methodPost req [204] baseurl void $ performRequestNoBody H.methodPost req [204] baseurl
@ -273,7 +273,7 @@ instance
#endif #endif
( MimeUnrender ct a, BuildHeadersTo ls ( MimeUnrender ct a, BuildHeadersTo ls
) => HasClient (Post (ct ': cts) (Headers ls a)) where ) => HasClient (Post (ct ': cts) (Headers ls a)) where
type Client (Post (ct ': cts) (Headers ls a)) = EitherT ServantError IO (Headers ls a) type Client (Post (ct ': cts) (Headers ls a)) = ExceptT ServantError IO (Headers ls a)
clientWithRoute Proxy req baseurl = do clientWithRoute Proxy req baseurl = do
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPost req [200, 201] baseurl (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPost req [200, 201] baseurl
return $ Headers { getResponse = resp return $ Headers { getResponse = resp
@ -289,7 +289,7 @@ instance
{-# OVERLAPPABLE #-} {-# OVERLAPPABLE #-}
#endif #endif
(MimeUnrender ct a) => HasClient (Put (ct ': cts) a) where (MimeUnrender ct a) => HasClient (Put (ct ': cts) a) where
type Client (Put (ct ': cts) a) = EitherT ServantError IO a type Client (Put (ct ': cts) a) = ExceptT ServantError IO a
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPut req [200,201] baseurl snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPut req [200,201] baseurl
@ -300,7 +300,7 @@ instance
{-# OVERLAPPING #-} {-# OVERLAPPING #-}
#endif #endif
HasClient (Put (ct ': cts) ()) where HasClient (Put (ct ': cts) ()) where
type Client (Put (ct ': cts) ()) = EitherT ServantError IO () type Client (Put (ct ': cts) ()) = ExceptT ServantError IO ()
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
void $ performRequestNoBody H.methodPut req [204] baseurl void $ performRequestNoBody H.methodPut req [204] baseurl
@ -312,7 +312,7 @@ instance
#endif #endif
( MimeUnrender ct a, BuildHeadersTo ls ( MimeUnrender ct a, BuildHeadersTo ls
) => HasClient (Put (ct ': cts) (Headers ls a)) where ) => HasClient (Put (ct ': cts) (Headers ls a)) where
type Client (Put (ct ': cts) (Headers ls a)) = EitherT ServantError IO (Headers ls a) type Client (Put (ct ': cts) (Headers ls a)) = ExceptT ServantError IO (Headers ls a)
clientWithRoute Proxy req baseurl = do clientWithRoute Proxy req baseurl = do
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPut req [200, 201] baseurl (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPut req [200, 201] baseurl
return $ Headers { getResponse = resp return $ Headers { getResponse = resp
@ -328,7 +328,7 @@ instance
{-# OVERLAPPABLE #-} {-# OVERLAPPABLE #-}
#endif #endif
(MimeUnrender ct a) => HasClient (Patch (ct ': cts) a) where (MimeUnrender ct a) => HasClient (Patch (ct ': cts) a) where
type Client (Patch (ct ': cts) a) = EitherT ServantError IO a type Client (Patch (ct ': cts) a) = ExceptT ServantError IO a
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPatch req [200,201] baseurl snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPatch req [200,201] baseurl
@ -339,7 +339,7 @@ instance
{-# OVERLAPPING #-} {-# OVERLAPPING #-}
#endif #endif
HasClient (Patch (ct ': cts) ()) where HasClient (Patch (ct ': cts) ()) where
type Client (Patch (ct ': cts) ()) = EitherT ServantError IO () type Client (Patch (ct ': cts) ()) = ExceptT ServantError IO ()
clientWithRoute Proxy req baseurl = clientWithRoute Proxy req baseurl =
void $ performRequestNoBody H.methodPatch req [204] baseurl void $ performRequestNoBody H.methodPatch req [204] baseurl
@ -351,7 +351,7 @@ instance
#endif #endif
( MimeUnrender ct a, BuildHeadersTo ls ( MimeUnrender ct a, BuildHeadersTo ls
) => HasClient (Patch (ct ': cts) (Headers ls a)) where ) => HasClient (Patch (ct ': cts) (Headers ls a)) where
type Client (Patch (ct ': cts) (Headers ls a)) = EitherT ServantError IO (Headers ls a) type Client (Patch (ct ': cts) (Headers ls a)) = ExceptT ServantError IO (Headers ls a)
clientWithRoute Proxy req baseurl = do clientWithRoute Proxy req baseurl = do
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPatch req [200, 201, 204] baseurl (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPatch req [200, 201, 204] baseurl
return $ Headers { getResponse = resp return $ Headers { getResponse = resp
@ -378,7 +378,7 @@ instance
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBooksBy :: Maybe Text -> EitherT String IO [Book] -- > getBooksBy :: Maybe Text -> ExceptT String IO [Book]
-- > getBooksBy = client myApi host -- > getBooksBy = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBooksBy" to query that endpoint. -- > -- then you can just use "getBooksBy" to query that endpoint.
@ -424,7 +424,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBooksBy :: [Text] -> EitherT String IO [Book] -- > getBooksBy :: [Text] -> ExceptT String IO [Book]
-- > getBooksBy = client myApi host -- > getBooksBy = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBooksBy" to query that endpoint. -- > -- then you can just use "getBooksBy" to query that endpoint.
@ -465,7 +465,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBooks :: Bool -> EitherT String IO [Book] -- > getBooks :: Bool -> ExceptT String IO [Book]
-- > getBooks = client myApi host -- > getBooks = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBooks" to query that endpoint. -- > -- then you can just use "getBooks" to query that endpoint.
@ -507,7 +507,7 @@ instance (KnownSymbol sym, HasClient sublayout)
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBooksBy :: Maybe Text -> EitherT String IO [Book] -- > getBooksBy :: Maybe Text -> ExceptT String IO [Book]
-- > getBooksBy = client myApi host -- > getBooksBy = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBooksBy" to query that endpoint. -- > -- then you can just use "getBooksBy" to query that endpoint.
@ -552,7 +552,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBooksBy :: [Text] -> EitherT String IO [Book] -- > getBooksBy :: [Text] -> ExceptT String IO [Book]
-- > getBooksBy = client myApi host -- > getBooksBy = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBooksBy" to query that endpoint. -- > -- then you can just use "getBooksBy" to query that endpoint.
@ -593,7 +593,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > getBooks :: Bool -> EitherT String IO [Book] -- > getBooks :: Bool -> ExceptT String IO [Book]
-- > getBooks = client myApi host -- > getBooks = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "getBooks" to query that endpoint. -- > -- then you can just use "getBooks" to query that endpoint.
@ -618,7 +618,7 @@ instance (KnownSymbol sym, HasClient sublayout)
-- | Pick a 'Method' and specify where the server you want to query is. You get -- | Pick a 'Method' and specify where the server you want to query is. You get
-- back the full `Response`. -- back the full `Response`.
instance HasClient Raw where instance HasClient Raw where
type Client Raw = H.Method -> EitherT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], Response ByteString) type Client Raw = H.Method -> ExceptT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
clientWithRoute :: Proxy Raw -> Req -> BaseUrl -> Client Raw clientWithRoute :: Proxy Raw -> Req -> BaseUrl -> Client Raw
clientWithRoute Proxy req baseurl httpMethod = do clientWithRoute Proxy req baseurl httpMethod = do
@ -639,7 +639,7 @@ instance HasClient Raw where
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > addBook :: Book -> EitherT String IO Book -- > addBook :: Book -> ExceptT String IO Book
-- > addBook = client myApi host -- > addBook = client myApi host
-- > where host = BaseUrl Http "localhost" 8080 -- > where host = BaseUrl Http "localhost" 8080
-- > -- then you can just use "addBook" to query that endpoint -- > -- then you can just use "addBook" to query that endpoint

View file

@ -11,7 +11,7 @@ import Control.Exception
import Control.Monad import Control.Monad
import Control.Monad.Catch (MonadThrow) import Control.Monad.Catch (MonadThrow)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.ByteString.Lazy hiding (pack, filter, map, null, elem) import Data.ByteString.Lazy hiding (pack, filter, map, null, elem)
import Data.IORef import Data.IORef
import Data.String import Data.String
@ -142,7 +142,7 @@ displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
performRequest :: Method -> Req -> (Int -> Bool) -> BaseUrl performRequest :: Method -> Req -> (Int -> Bool) -> BaseUrl
-> EitherT ServantError IO ( Int, ByteString, MediaType -> ExceptT ServantError IO ( Int, ByteString, MediaType
, [HTTP.Header], Response ByteString) , [HTTP.Header], Response ByteString)
performRequest reqMethod req isWantedStatus reqHost = do performRequest reqMethod req isWantedStatus reqHost = do
partialRequest <- liftIO $ reqToRequest req reqHost partialRequest <- liftIO $ reqToRequest req reqHost
@ -156,7 +156,7 @@ performRequest reqMethod req isWantedStatus reqHost = do
Client.httpLbs request manager Client.httpLbs request manager
case eResponse of case eResponse of
Left err -> Left err ->
left . ConnectionError $ SomeException err throwE . ConnectionError $ SomeException err
Right response -> do Right response -> do
let status = Client.responseStatus response let status = Client.responseStatus response
@ -166,25 +166,25 @@ performRequest reqMethod req isWantedStatus reqHost = do
ct <- case lookup "Content-Type" $ Client.responseHeaders response of ct <- case lookup "Content-Type" $ Client.responseHeaders response of
Nothing -> pure $ "application"//"octet-stream" Nothing -> pure $ "application"//"octet-stream"
Just t -> case parseAccept t of Just t -> case parseAccept t of
Nothing -> left $ InvalidContentTypeHeader (cs t) body Nothing -> throwE $ InvalidContentTypeHeader (cs t) body
Just t' -> pure t' Just t' -> pure t'
unless (isWantedStatus status_code) $ unless (isWantedStatus status_code) $
left $ FailureResponse status ct body throwE $ FailureResponse status ct body
return (status_code, body, ct, hrds, response) return (status_code, body, ct, hrds, response)
performRequestCT :: MimeUnrender ct result => performRequestCT :: MimeUnrender ct result =>
Proxy ct -> Method -> Req -> [Int] -> BaseUrl -> EitherT ServantError IO ([HTTP.Header], result) Proxy ct -> Method -> Req -> [Int] -> BaseUrl -> ExceptT ServantError IO ([HTTP.Header], result)
performRequestCT ct reqMethod req wantedStatus reqHost = do performRequestCT ct reqMethod req wantedStatus reqHost = do
let acceptCT = contentType ct let acceptCT = contentType ct
(_status, respBody, respCT, hrds, _response) <- (_status, respBody, respCT, hrds, _response) <-
performRequest reqMethod (req { reqAccept = [acceptCT] }) (`elem` wantedStatus) reqHost performRequest reqMethod (req { reqAccept = [acceptCT] }) (`elem` wantedStatus) reqHost
unless (matches respCT (acceptCT)) $ left $ UnsupportedContentType respCT respBody unless (matches respCT (acceptCT)) $ throwE $ UnsupportedContentType respCT respBody
case mimeUnrender ct respBody of case mimeUnrender ct respBody of
Left err -> left $ DecodeFailure err respCT respBody Left err -> throwE $ DecodeFailure err respCT respBody
Right val -> return (hrds, val) Right val -> return (hrds, val)
performRequestNoBody :: Method -> Req -> [Int] -> BaseUrl -> EitherT ServantError IO () performRequestNoBody :: Method -> Req -> [Int] -> BaseUrl -> ExceptT ServantError IO ()
performRequestNoBody reqMethod req wantedStatus reqHost = do performRequestNoBody reqMethod req wantedStatus reqHost = do
_ <- performRequest reqMethod req (`elem` wantedStatus) reqHost _ <- performRequest reqMethod req (`elem` wantedStatus) reqHost
return () return ()

View file

@ -15,10 +15,10 @@ module Servant.ClientSpec where
#if !MIN_VERSION_base(4,8,0) #if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
#endif #endif
import qualified Control.Arrow as Arrow import Control.Arrow (left)
import Control.Concurrent import Control.Concurrent
import Control.Exception import Control.Exception
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.Aeson import Data.Aeson
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
import Data.Char import Data.Char
@ -105,14 +105,14 @@ server = serve api (
:<|> return :<|> return
:<|> (\ name -> case name of :<|> (\ name -> case name of
Just "alice" -> return alice Just "alice" -> return alice
Just name -> left $ ServantErr 400 (name ++ " not found") "" [] Just name -> throwE $ ServantErr 400 (name ++ " not found") "" []
Nothing -> left $ ServantErr 400 "missing parameter" "" []) Nothing -> throwE $ ServantErr 400 "missing parameter" "" [])
:<|> (\ names -> return (zipWith Person names [0..])) :<|> (\ names -> return (zipWith Person names [0..]))
:<|> return :<|> return
:<|> (\ name -> case name of :<|> (\ name -> case name of
Just "alice" -> return alice Just "alice" -> return alice
Just name -> left $ ServantErr 400 (name ++ " not found") "" [] Just name -> throwE $ ServantErr 400 (name ++ " not found") "" []
Nothing -> left $ ServantErr 400 "missing parameter" "" []) Nothing -> throwE $ ServantErr 400 "missing parameter" "" [])
:<|> (\ names -> return (zipWith Person names [0..])) :<|> (\ names -> return (zipWith Person names [0..]))
:<|> return :<|> return
:<|> (\ _request respond -> respond $ responseLBS ok200 [] "rawSuccess") :<|> (\ _request respond -> respond $ responseLBS ok200 [] "rawSuccess")
@ -144,21 +144,21 @@ withFailServer action = withWaiDaemon (return failServer) action
spec :: IO () spec :: IO ()
spec = withServer $ \ baseUrl -> do spec = withServer $ \ baseUrl -> do
let getGet :: EitherT ServantError IO Person let getGet :: ExceptT ServantError IO Person
getDeleteEmpty :: EitherT ServantError IO () getDeleteEmpty :: ExceptT ServantError IO ()
getCapture :: String -> EitherT ServantError IO Person getCapture :: String -> ExceptT ServantError IO Person
getBody :: Person -> EitherT ServantError IO Person getBody :: Person -> ExceptT ServantError IO Person
getQueryParam :: Maybe String -> EitherT ServantError IO Person getQueryParam :: Maybe String -> ExceptT ServantError IO Person
getQueryParams :: [String] -> EitherT ServantError IO [Person] getQueryParams :: [String] -> ExceptT ServantError IO [Person]
getQueryFlag :: Bool -> EitherT ServantError IO Bool getQueryFlag :: Bool -> ExceptT ServantError IO Bool
getMatrixParam :: Maybe String -> EitherT ServantError IO Person getMatrixParam :: Maybe String -> ExceptT ServantError IO Person
getMatrixParams :: [String] -> EitherT ServantError IO [Person] getMatrixParams :: [String] -> ExceptT ServantError IO [Person]
getMatrixFlag :: Bool -> EitherT ServantError IO Bool getMatrixFlag :: Bool -> ExceptT ServantError IO Bool
getRawSuccess :: Method -> EitherT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], C.Response ByteString) getRawSuccess :: Method -> ExceptT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], C.Response ByteString)
getRawFailure :: Method -> EitherT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], C.Response ByteString) getRawFailure :: Method -> ExceptT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], C.Response ByteString)
getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] -> EitherT ServantError IO (String, Maybe Int, Bool, [(String, [Rational])]) getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] -> ExceptT ServantError IO (String, Maybe Int, Bool, [(String, [Rational])])
getRespHeaders :: EitherT ServantError IO (Headers TestHeaders Bool) getRespHeaders :: ExceptT ServantError IO (Headers TestHeaders Bool)
getDeleteContentType :: EitherT ServantError IO () getDeleteContentType :: ExceptT ServantError IO ()
( getGet ( getGet
:<|> getDeleteEmpty :<|> getDeleteEmpty
:<|> getCapture :<|> getCapture
@ -178,54 +178,54 @@ spec = withServer $ \ baseUrl -> do
hspec $ do hspec $ do
it "Servant.API.Get" $ do it "Servant.API.Get" $ do
(Arrow.left show <$> runEitherT getGet) `shouldReturn` Right alice (left show <$> runExceptT getGet) `shouldReturn` Right alice
describe "Servant.API.Delete" $ do describe "Servant.API.Delete" $ do
it "allows empty content type" $ do it "allows empty content type" $ do
(Arrow.left show <$> runEitherT getDeleteEmpty) `shouldReturn` Right () (left show <$> runExceptT getDeleteEmpty) `shouldReturn` Right ()
it "allows content type" $ do it "allows content type" $ do
(Arrow.left show <$> runEitherT getDeleteContentType) `shouldReturn` Right () (left show <$> runExceptT getDeleteContentType) `shouldReturn` Right ()
it "Servant.API.Capture" $ do it "Servant.API.Capture" $ do
(Arrow.left show <$> runEitherT (getCapture "Paula")) `shouldReturn` Right (Person "Paula" 0) (left show <$> runExceptT (getCapture "Paula")) `shouldReturn` Right (Person "Paula" 0)
it "Servant.API.ReqBody" $ do it "Servant.API.ReqBody" $ do
let p = Person "Clara" 42 let p = Person "Clara" 42
(Arrow.left show <$> runEitherT (getBody p)) `shouldReturn` Right p (left show <$> runExceptT (getBody p)) `shouldReturn` Right p
it "Servant.API.QueryParam" $ do it "Servant.API.QueryParam" $ do
Arrow.left show <$> runEitherT (getQueryParam (Just "alice")) `shouldReturn` Right alice left show <$> runExceptT (getQueryParam (Just "alice")) `shouldReturn` Right alice
Left FailureResponse{..} <- runEitherT (getQueryParam (Just "bob")) Left FailureResponse{..} <- runExceptT (getQueryParam (Just "bob"))
responseStatus `shouldBe` Status 400 "bob not found" responseStatus `shouldBe` Status 400 "bob not found"
it "Servant.API.QueryParam.QueryParams" $ do it "Servant.API.QueryParam.QueryParams" $ do
(Arrow.left show <$> runEitherT (getQueryParams [])) `shouldReturn` Right [] (left show <$> runExceptT (getQueryParams [])) `shouldReturn` Right []
(Arrow.left show <$> runEitherT (getQueryParams ["alice", "bob"])) (left show <$> runExceptT (getQueryParams ["alice", "bob"]))
`shouldReturn` Right [Person "alice" 0, Person "bob" 1] `shouldReturn` Right [Person "alice" 0, Person "bob" 1]
context "Servant.API.QueryParam.QueryFlag" $ context "Servant.API.QueryParam.QueryFlag" $
forM_ [False, True] $ \ flag -> forM_ [False, True] $ \ flag ->
it (show flag) $ do it (show flag) $ do
(Arrow.left show <$> runEitherT (getQueryFlag flag)) `shouldReturn` Right flag (left show <$> runExceptT (getQueryFlag flag)) `shouldReturn` Right flag
it "Servant.API.MatrixParam" $ do it "Servant.API.MatrixParam" $ do
Arrow.left show <$> runEitherT (getMatrixParam (Just "alice")) `shouldReturn` Right alice left show <$> runExceptT (getMatrixParam (Just "alice")) `shouldReturn` Right alice
Left FailureResponse{..} <- runEitherT (getMatrixParam (Just "bob")) Left FailureResponse{..} <- runExceptT (getMatrixParam (Just "bob"))
responseStatus `shouldBe` Status 400 "bob not found" responseStatus `shouldBe` Status 400 "bob not found"
it "Servant.API.MatrixParam.MatrixParams" $ do it "Servant.API.MatrixParam.MatrixParams" $ do
Arrow.left show <$> runEitherT (getMatrixParams []) `shouldReturn` Right [] left show <$> runExceptT (getMatrixParams []) `shouldReturn` Right []
Arrow.left show <$> runEitherT (getMatrixParams ["alice", "bob"]) left show <$> runExceptT (getMatrixParams ["alice", "bob"])
`shouldReturn` Right [Person "alice" 0, Person "bob" 1] `shouldReturn` Right [Person "alice" 0, Person "bob" 1]
context "Servant.API.MatrixParam.MatrixFlag" $ context "Servant.API.MatrixParam.MatrixFlag" $
forM_ [False, True] $ \ flag -> forM_ [False, True] $ \ flag ->
it (show flag) $ do it (show flag) $ do
Arrow.left show <$> runEitherT (getMatrixFlag flag) `shouldReturn` Right flag left show <$> runExceptT (getMatrixFlag flag) `shouldReturn` Right flag
it "Servant.API.Raw on success" $ do it "Servant.API.Raw on success" $ do
res <- runEitherT (getRawSuccess methodGet) res <- runExceptT (getRawSuccess methodGet)
case res of case res of
Left e -> assertFailure $ show e Left e -> assertFailure $ show e
Right (code, body, ct, _, response) -> do Right (code, body, ct, _, response) -> do
@ -234,7 +234,7 @@ spec = withServer $ \ baseUrl -> do
C.responseStatus response `shouldBe` ok200 C.responseStatus response `shouldBe` ok200
it "Servant.API.Raw on failure" $ do it "Servant.API.Raw on failure" $ do
res <- runEitherT (getRawFailure methodGet) res <- runExceptT (getRawFailure methodGet)
case res of case res of
Left e -> assertFailure $ show e Left e -> assertFailure $ show e
Right (code, body, ct, _, response) -> do Right (code, body, ct, _, response) -> do
@ -243,7 +243,7 @@ spec = withServer $ \ baseUrl -> do
C.responseStatus response `shouldBe` badRequest400 C.responseStatus response `shouldBe` badRequest400
it "Returns headers appropriately" $ withServer $ \ _ -> do it "Returns headers appropriately" $ withServer $ \ _ -> do
res <- runEitherT getRespHeaders res <- runExceptT getRespHeaders
case res of case res of
Left e -> assertFailure $ show e Left e -> assertFailure $ show e
Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")] Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")]
@ -252,7 +252,7 @@ spec = withServer $ \ baseUrl -> do
it "works for a combination of Capture, QueryParam, QueryFlag and ReqBody" $ it "works for a combination of Capture, QueryParam, QueryFlag and ReqBody" $
property $ forAllShrink pathGen shrink $ \(NonEmpty cap) num flag body -> property $ forAllShrink pathGen shrink $ \(NonEmpty cap) num flag body ->
ioProperty $ do ioProperty $ do
result <- Arrow.left show <$> runEitherT (getMultiple cap num flag body) result <- left show <$> runExceptT (getMultiple cap num flag body)
return $ return $
result === Right (cap, num, flag, body) result === Right (cap, num, flag, body)
@ -261,11 +261,11 @@ spec = withServer $ \ baseUrl -> do
let test :: (WrappedApi, String) -> Spec let test :: (WrappedApi, String) -> Spec
test (WrappedApi api, desc) = test (WrappedApi api, desc) =
it desc $ it desc $
withWaiDaemon (return (serve api (left $ ServantErr 500 "error message" "" []))) $ withWaiDaemon (return (serve api (throwE $ ServantErr 500 "error message" "" []))) $
\ host -> do \ host -> do
let getResponse :: EitherT ServantError IO () let getResponse :: ExceptT ServantError IO ()
getResponse = client api host getResponse = client api host
Left FailureResponse{..} <- runEitherT getResponse Left FailureResponse{..} <- runExceptT getResponse
responseStatus `shouldBe` (Status 500 "error message") responseStatus `shouldBe` (Status 500 "error message")
mapM_ test $ mapM_ test $
(WrappedApi (Proxy :: Proxy (Delete '[JSON] ())), "Delete") : (WrappedApi (Proxy :: Proxy (Delete '[JSON] ())), "Delete") :
@ -276,54 +276,54 @@ spec = withServer $ \ baseUrl -> do
failSpec :: IO () failSpec :: IO ()
failSpec = withFailServer $ \ baseUrl -> do failSpec = withFailServer $ \ baseUrl -> do
let getGet :: EitherT ServantError IO Person let getGet :: ExceptT ServantError IO Person
getDeleteEmpty :: EitherT ServantError IO () getDeleteEmpty :: ExceptT ServantError IO ()
getCapture :: String -> EitherT ServantError IO Person getCapture :: String -> ExceptT ServantError IO Person
getBody :: Person -> EitherT ServantError IO Person getBody :: Person -> ExceptT ServantError IO Person
( getGet ( getGet
:<|> getDeleteEmpty :<|> getDeleteEmpty
:<|> getCapture :<|> getCapture
:<|> getBody :<|> getBody
:<|> _ ) :<|> _ )
= client api baseUrl = client api baseUrl
getGetWrongHost :: EitherT ServantError IO Person getGetWrongHost :: ExceptT ServantError IO Person
(getGetWrongHost :<|> _) = client api (BaseUrl Http "127.0.0.1" 19872 "") (getGetWrongHost :<|> _) = client api (BaseUrl Http "127.0.0.1" 19872 "")
hspec $ do hspec $ do
context "client returns errors appropriately" $ do context "client returns errors appropriately" $ do
it "reports FailureResponse" $ do it "reports FailureResponse" $ do
Left res <- runEitherT getDeleteEmpty Left res <- runExceptT getDeleteEmpty
case res of case res of
FailureResponse (Status 404 "Not Found") _ _ -> return () FailureResponse (Status 404 "Not Found") _ _ -> return ()
_ -> fail $ "expected 404 response, but got " <> show res _ -> fail $ "expected 404 response, but got " <> show res
it "reports DecodeFailure" $ do it "reports DecodeFailure" $ do
Left res <- runEitherT (getCapture "foo") Left res <- runExceptT (getCapture "foo")
case res of case res of
DecodeFailure _ ("application/json") _ -> return () DecodeFailure _ ("application/json") _ -> return ()
_ -> fail $ "expected DecodeFailure, but got " <> show res _ -> fail $ "expected DecodeFailure, but got " <> show res
it "reports ConnectionError" $ do it "reports ConnectionError" $ do
Left res <- runEitherT getGetWrongHost Left res <- runExceptT getGetWrongHost
case res of case res of
ConnectionError _ -> return () ConnectionError _ -> return ()
_ -> fail $ "expected ConnectionError, but got " <> show res _ -> fail $ "expected ConnectionError, but got " <> show res
it "reports UnsupportedContentType" $ do it "reports UnsupportedContentType" $ do
Left res <- runEitherT getGet Left res <- runExceptT getGet
case res of case res of
UnsupportedContentType ("application/octet-stream") _ -> return () UnsupportedContentType ("application/octet-stream") _ -> return ()
_ -> fail $ "expected UnsupportedContentType, but got " <> show res _ -> fail $ "expected UnsupportedContentType, but got " <> show res
it "reports InvalidContentTypeHeader" $ do it "reports InvalidContentTypeHeader" $ do
Left res <- runEitherT (getBody alice) Left res <- runExceptT (getBody alice)
case res of case res of
InvalidContentTypeHeader "fooooo" _ -> return () InvalidContentTypeHeader "fooooo" _ -> return ()
_ -> fail $ "expected InvalidContentTypeHeader, but got " <> show res _ -> fail $ "expected InvalidContentTypeHeader, but got " <> show res
data WrappedApi where data WrappedApi where
WrappedApi :: (HasServer api, Server api ~ EitherT ServantErr IO a, WrappedApi :: (HasServer api, Server api ~ ExceptT ServantErr IO a,
HasClient api, Client api ~ EitherT ServantError IO ()) => HasClient api, Client api ~ ExceptT ServantError IO ()) =>
Proxy api -> WrappedApi Proxy api -> WrappedApi

View file

@ -5,7 +5,7 @@
import Control.Applicative import Control.Applicative
import Control.Monad import Control.Monad
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.Aeson import Data.Aeson
import Data.Monoid import Data.Monoid
import Data.Proxy import Data.Proxy
@ -54,16 +54,16 @@ instance FromJSON Package
hackageAPI :: Proxy HackageAPI hackageAPI :: Proxy HackageAPI
hackageAPI = Proxy hackageAPI = Proxy
getUsers :: EitherT ServantError IO [UserSummary] getUsers :: ExceptT ServantError IO [UserSummary]
getUser :: Username -> EitherT ServantError IO UserDetailed getUser :: Username -> ExceptT ServantError IO UserDetailed
getPackages :: EitherT ServantError IO [Package] getPackages :: ExceptT ServantError IO [Package]
getUsers :<|> getUser :<|> getPackages = client hackageAPI $ BaseUrl Http "hackage.haskell.org" 80 "" getUsers :<|> getUser :<|> getPackages = client hackageAPI $ BaseUrl Http "hackage.haskell.org" 80 ""
main :: IO () main :: IO ()
main = print =<< uselessNumbers main = print =<< uselessNumbers
uselessNumbers :: IO (Either ServantError ()) uselessNumbers :: IO (Either ServantError ())
uselessNumbers = runEitherT $ do uselessNumbers = runExceptT $ do
users <- getUsers users <- getUsers
liftIO . putStrLn $ show (length users) ++ " users" liftIO . putStrLn $ show (length users) ++ " users"

View file

@ -25,7 +25,6 @@ executable tutorial
, base >= 4.7 && < 5 , base >= 4.7 && < 5
, bytestring , bytestring
, directory , directory
, either
, http-types , http-types
, js-jquery , js-jquery
, lucid , lucid
@ -50,10 +49,10 @@ executable t8-main
build-depends: build-depends:
aeson aeson
, base >= 4.7 && < 5 , base >= 4.7 && < 5
, either
, servant == 0.5.* , servant == 0.5.*
, servant-client == 0.5.* , servant-client == 0.5.*
, servant-server == 0.5.* , servant-server == 0.5.*
, transformers
, wai , wai
executable hackage executable hackage
@ -61,7 +60,6 @@ executable hackage
build-depends: build-depends:
aeson >= 0.8 aeson >= 0.8
, base >=4.7 && < 5 , base >=4.7 && < 5
, either
, servant == 0.5.* , servant == 0.5.*
, servant-client == 0.5.* , servant-client == 0.5.*
, text , text

View file

@ -4,7 +4,7 @@
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
module T3 where module T3 where
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.Aeson import Data.Aeson
import Data.List import Data.List
import GHC.Generics import GHC.Generics
@ -69,15 +69,15 @@ server = position
:<|> hello :<|> hello
:<|> marketing :<|> marketing
where position :: Int -> Int -> EitherT ServantErr IO Position where position :: Int -> Int -> ExceptT ServantErr IO Position
position x y = return (Position x y) position x y = return (Position x y)
hello :: Maybe String -> EitherT ServantErr IO HelloMessage hello :: Maybe String -> ExceptT ServantErr IO HelloMessage
hello mname = return . HelloMessage $ case mname of hello mname = return . HelloMessage $ case mname of
Nothing -> "Hello, anonymous coward" Nothing -> "Hello, anonymous coward"
Just n -> "Hello, " ++ n Just n -> "Hello, " ++ n
marketing :: ClientInfo -> EitherT ServantErr IO Email marketing :: ClientInfo -> ExceptT ServantErr IO Email
marketing clientinfo = return (emailForClient clientinfo) marketing clientinfo = return (emailForClient clientinfo)
app :: Application app :: Application

View file

@ -6,7 +6,7 @@
module T5 where module T5 where
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.Aeson import Data.Aeson
import GHC.Generics import GHC.Generics
import Network.Wai import Network.Wai
@ -29,7 +29,7 @@ server = do
exists <- liftIO (doesFileExist "myfile.txt") exists <- liftIO (doesFileExist "myfile.txt")
if exists if exists
then liftIO (readFile "myfile.txt") >>= return . FileContent then liftIO (readFile "myfile.txt") >>= return . FileContent
else left custom404Err else throwE custom404Err
where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." } where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }

View file

@ -3,7 +3,7 @@
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
module T7 where module T7 where
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader import Control.Monad.Trans.Reader
import Network.Wai import Network.Wai
import Servant import Servant
@ -26,7 +26,7 @@ readerServerT = a :<|> b
readerServer :: Server ReaderAPI readerServer :: Server ReaderAPI
readerServer = enter readerToEither readerServerT readerServer = enter readerToEither readerServerT
where readerToEither :: Reader String :~> EitherT ServantErr IO where readerToEither :: Reader String :~> ExceptT ServantErr IO
readerToEither = Nat $ \r -> return (runReader r "hi") readerToEither = Nat $ \r -> return (runReader r "hi")
app :: Application app :: Application

View file

@ -3,7 +3,7 @@
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
module T8 where module T8 where
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.Aeson import Data.Aeson
import Servant import Servant
import Servant.Client import Servant.Client
@ -12,20 +12,20 @@ import T3
position :: Int -- ^ value for "x" position :: Int -- ^ value for "x"
-> Int -- ^ value for "y" -> Int -- ^ value for "y"
-> EitherT ServantError IO Position -> ExceptT ServantError IO Position
hello :: Maybe String -- ^ an optional value for "name" hello :: Maybe String -- ^ an optional value for "name"
-> EitherT ServantError IO HelloMessage -> ExceptT ServantError IO HelloMessage
marketing :: ClientInfo -- ^ value for the request body marketing :: ClientInfo -- ^ value for the request body
-> EitherT ServantError IO Email -> ExceptT ServantError IO Email
position :<|> hello :<|> marketing = client api baseUrl position :<|> hello :<|> marketing = client api baseUrl
baseUrl :: BaseUrl baseUrl :: BaseUrl
baseUrl = BaseUrl Http "localhost" 8081 "" baseUrl = BaseUrl Http "localhost" 8081 ""
queries :: EitherT ServantError IO (Position, HelloMessage, Email) queries :: ExceptT ServantError IO (Position, HelloMessage, Email)
queries = do queries = do
pos <- position 10 10 pos <- position 10 10
msg <- hello (Just "servant") msg <- hello (Just "servant")
@ -34,7 +34,7 @@ queries = do
run :: IO () run :: IO ()
run = do run = do
res <- runEitherT queries res <- runExceptT queries
case res of case res of
Left err -> putStrLn $ "Error: " ++ show err Left err -> putStrLn $ "Error: " ++ show err
Right (pos, msg, em) -> do Right (pos, msg, em) -> do

View file

@ -91,8 +91,8 @@ class HasServer api => HasMock api where
-- actually "means" 2 request handlers, of the following types: -- actually "means" 2 request handlers, of the following types:
-- --
-- @ -- @
-- getUser :: EitherT ServantErr IO User -- getUser :: ExceptT ServantErr IO User
-- getBook :: EitherT ServantErr IO Book -- getBook :: ExceptT ServantErr IO Book
-- @ -- @
-- --
-- So under the hood, 'mock' uses the 'IO' bit to generate -- So under the hood, 'mock' uses the 'IO' bit to generate

View file

@ -2,6 +2,7 @@ HEAD
---- ----
* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators * Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
* Drop `EitherT` in favor of `ExceptT`
0.4.1 0.4.1
----- -----

View file

@ -44,7 +44,7 @@ testApi = Proxy
-- There's one handler per endpoint, which, just like in the type -- There's one handler per endpoint, which, just like in the type
-- that represents the API, are glued together using :<|>. -- that represents the API, are glued together using :<|>.
-- --
-- Each handler runs in the 'EitherT ServantErr IO' monad. -- Each handler runs in the 'ExceptT ServantErr IO' monad.
server :: Server TestApi server :: Server TestApi
server = helloH :<|> postGreetH :<|> deleteGreetH server = helloH :<|> postGreetH :<|> deleteGreetH

View file

@ -48,7 +48,6 @@ library
, attoparsec >= 0.12 && < 0.14 , attoparsec >= 0.12 && < 0.14
, bytestring >= 0.10 && < 0.11 , bytestring >= 0.10 && < 0.11
, containers >= 0.5 && < 0.6 , containers >= 0.5 && < 0.6
, either >= 4.3 && < 4.5
, http-types >= 0.8 && < 0.9 , http-types >= 0.8 && < 0.9
, network-uri >= 2.6 && < 2.7 , network-uri >= 2.6 && < 2.7
, mtl >= 2 && < 3 , mtl >= 2 && < 3
@ -97,7 +96,6 @@ test-suite spec
, bytestring , bytestring
, bytestring-conversion , bytestring-conversion
, directory , directory
, either
, exceptions , exceptions
, hspec == 2.* , hspec == 2.*
, hspec-wai , hspec-wai

View file

@ -1,4 +1,3 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
@ -29,9 +28,6 @@ module Servant.Server
, evalStateTSNat , evalStateTSNat
, logWriterTLNat , logWriterTLNat
, logWriterTSNat , logWriterTSNat
#if MIN_VERSION_mtl(2,2,1)
, fromExceptT
#endif
-- ** Functions based on <https://hackage.haskell.org/package/mmorph mmorph> -- ** Functions based on <https://hackage.haskell.org/package/mmorph mmorph>
, hoistNat , hoistNat
, embedNat , embedNat
@ -113,7 +109,7 @@ serve p server = toApplication (runRouter (route p (return (RR (Right server))))
-- Documentation -- Documentation
-- $enterDoc -- $enterDoc
-- Sometimes our cherished `EitherT` monad isn't quite the type you'd like for -- Sometimes our cherished `ExceptT` monad isn't quite the type you'd like for
-- your handlers. Maybe you want to thread some configuration in a @Reader@ -- your handlers. Maybe you want to thread some configuration in a @Reader@
-- monad. Or have your types ensure that your handlers don't do any IO. Enter -- monad. Or have your types ensure that your handlers don't do any IO. Enter
-- `enter`. -- `enter`.

View file

@ -23,7 +23,7 @@ module Servant.Server.Internal
#if !MIN_VERSION_base(4,8,0) #if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
#endif #endif
import Control.Monad.Trans.Either (EitherT) import Control.Monad.Trans.Except (ExceptT)
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
import qualified Data.Map as M import qualified Data.Map as M
@ -65,7 +65,7 @@ class HasServer layout where
route :: Proxy layout -> IO (RouteResult (Server layout)) -> Router route :: Proxy layout -> IO (RouteResult (Server layout)) -> Router
type Server layout = ServerT layout (EitherT ServantErr IO) type Server layout = ServerT layout (ExceptT ServantErr IO)
-- * Instances -- * Instances
@ -107,7 +107,7 @@ captured _ = fromText
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBook -- > server = getBook
-- > where getBook :: Text -> EitherT ServantErr IO Book -- > where getBook :: Text -> ExceptT ServantErr IO Book
-- > getBook isbn = ... -- > getBook isbn = ...
instance (KnownSymbol capture, FromText a, HasServer sublayout) instance (KnownSymbol capture, FromText a, HasServer sublayout)
=> HasServer (Capture capture a :> sublayout) where => HasServer (Capture capture a :> sublayout) where
@ -142,7 +142,7 @@ processMethodRouter handleA status method headers request = case handleA of
methodRouter :: (AllCTRender ctypes a) methodRouter :: (AllCTRender ctypes a)
=> Method -> Proxy ctypes -> Status => Method -> Proxy ctypes -> Status
-> IO (RouteResult (EitherT ServantErr IO a)) -> IO (RouteResult (ExceptT ServantErr IO a))
-> Router -> Router
methodRouter method proxy status action = LeafRouter route' methodRouter method proxy status action = LeafRouter route'
where where
@ -158,7 +158,7 @@ methodRouter method proxy status action = LeafRouter route'
methodRouterHeaders :: (GetHeaders (Headers h v), AllCTRender ctypes v) methodRouterHeaders :: (GetHeaders (Headers h v), AllCTRender ctypes v)
=> Method -> Proxy ctypes -> Status => Method -> Proxy ctypes -> Status
-> IO (RouteResult (EitherT ServantErr IO (Headers h v))) -> IO (RouteResult (ExceptT ServantErr IO (Headers h v)))
-> Router -> Router
methodRouterHeaders method proxy status action = LeafRouter route' methodRouterHeaders method proxy status action = LeafRouter route'
where where
@ -174,7 +174,7 @@ methodRouterHeaders method proxy status action = LeafRouter route'
| otherwise = respond $ failWith NotFound | otherwise = respond $ failWith NotFound
methodRouterEmpty :: Method methodRouterEmpty :: Method
-> IO (RouteResult (EitherT ServantErr IO ())) -> IO (RouteResult (ExceptT ServantErr IO ()))
-> Router -> Router
methodRouterEmpty method action = LeafRouter route' methodRouterEmpty method action = LeafRouter route'
where where
@ -192,9 +192,9 @@ methodRouterEmpty method action = LeafRouter route'
-- --
-- The code of the handler will, just like -- The code of the handler will, just like
-- for 'Servant.API.Get.Get', 'Servant.API.Post.Post' and -- for 'Servant.API.Get.Get', 'Servant.API.Post.Post' and
-- 'Servant.API.Put.Put', run in @EitherT ServantErr IO ()@. -- 'Servant.API.Put.Put', run in @ExceptT ServantErr IO ()@.
-- The 'Int' represents the status code and the 'String' a message -- The 'Int' represents the status code and the 'String' a message
-- to be returned. You can use 'Control.Monad.Trans.Either.left' to -- to be returned. You can use 'Control.Monad.Trans.Except.throwE' to
-- painlessly error out if the conditions for a successful deletion -- painlessly error out if the conditions for a successful deletion
-- are not met. -- are not met.
instance instance
@ -233,9 +233,9 @@ instance
-- | When implementing the handler for a 'Get' endpoint, -- | When implementing the handler for a 'Get' endpoint,
-- just like for 'Servant.API.Delete.Delete', 'Servant.API.Post.Post' -- just like for 'Servant.API.Delete.Delete', 'Servant.API.Post.Post'
-- and 'Servant.API.Put.Put', the handler code runs in the -- and 'Servant.API.Put.Put', the handler code runs in the
-- @EitherT ServantErr IO@ monad, where the 'Int' represents -- @ExceptT ServantErr IO@ monad, where the 'Int' represents
-- the status code and the 'String' a message, returned in case of -- the status code and the 'String' a message, returned in case of
-- failure. You can quite handily use 'Control.Monad.Trans.EitherT.left' -- failure. You can quite handily use 'Control.Monad.Trans.Except.throwE'
-- to quickly fail if some conditions are not met. -- to quickly fail if some conditions are not met.
-- --
-- If successfully returning a value, we use the type-level list, combined -- If successfully returning a value, we use the type-level list, combined
@ -294,7 +294,7 @@ instance
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = viewReferer -- > server = viewReferer
-- > where viewReferer :: Referer -> EitherT ServantErr IO referer -- > where viewReferer :: Referer -> ExceptT ServantErr IO referer
-- > viewReferer referer = return referer -- > viewReferer referer = return referer
instance (KnownSymbol sym, FromText a, HasServer sublayout) instance (KnownSymbol sym, FromText a, HasServer sublayout)
=> HasServer (Header sym a :> sublayout) where => HasServer (Header sym a :> sublayout) where
@ -310,9 +310,9 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
-- | When implementing the handler for a 'Post' endpoint, -- | When implementing the handler for a 'Post' endpoint,
-- just like for 'Servant.API.Delete.Delete', 'Servant.API.Get.Get' -- just like for 'Servant.API.Delete.Delete', 'Servant.API.Get.Get'
-- and 'Servant.API.Put.Put', the handler code runs in the -- and 'Servant.API.Put.Put', the handler code runs in the
-- @EitherT ServantErr IO@ monad, where the 'Int' represents -- @ExceptT ServantErr IO@ monad, where the 'Int' represents
-- the status code and the 'String' a message, returned in case of -- the status code and the 'String' a message, returned in case of
-- failure. You can quite handily use 'Control.Monad.Trans.EitherT.left' -- failure. You can quite handily use 'Control.Monad.Trans.Except.throwE'
-- to quickly fail if some conditions are not met. -- to quickly fail if some conditions are not met.
-- --
-- If successfully returning a value, we use the type-level list, combined -- If successfully returning a value, we use the type-level list, combined
@ -356,9 +356,9 @@ instance
-- | When implementing the handler for a 'Put' endpoint, -- | When implementing the handler for a 'Put' endpoint,
-- just like for 'Servant.API.Delete.Delete', 'Servant.API.Get.Get' -- just like for 'Servant.API.Delete.Delete', 'Servant.API.Get.Get'
-- and 'Servant.API.Post.Post', the handler code runs in the -- and 'Servant.API.Post.Post', the handler code runs in the
-- @EitherT ServantErr IO@ monad, where the 'Int' represents -- @ExceptT ServantErr IO@ monad, where the 'Int' represents
-- the status code and the 'String' a message, returned in case of -- the status code and the 'String' a message, returned in case of
-- failure. You can quite handily use 'Control.Monad.Trans.EitherT.left' -- failure. You can quite handily use 'Control.Monad.Trans.Except.throwE'
-- to quickly fail if some conditions are not met. -- to quickly fail if some conditions are not met.
-- --
-- If successfully returning a value, we use the type-level list, combined -- If successfully returning a value, we use the type-level list, combined
@ -401,9 +401,9 @@ instance
-- | When implementing the handler for a 'Patch' endpoint, -- | When implementing the handler for a 'Patch' endpoint,
-- just like for 'Servant.API.Delete.Delete', 'Servant.API.Get.Get' -- just like for 'Servant.API.Delete.Delete', 'Servant.API.Get.Get'
-- and 'Servant.API.Put.Put', the handler code runs in the -- and 'Servant.API.Put.Put', the handler code runs in the
-- @EitherT ServantErr IO@ monad, where the 'Int' represents -- @ExceptT ServantErr IO@ monad, where the 'Int' represents
-- the status code and the 'String' a message, returned in case of -- the status code and the 'String' a message, returned in case of
-- failure. You can quite handily use 'Control.Monad.Trans.EitherT.left' -- failure. You can quite handily use 'Control.Monad.Trans.Except.throwE'
-- to quickly fail if some conditions are not met. -- to quickly fail if some conditions are not met.
-- --
-- If successfully returning a value, we just require that its type has -- If successfully returning a value, we just require that its type has
@ -459,7 +459,7 @@ instance
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBooksBy -- > server = getBooksBy
-- > where getBooksBy :: Maybe Text -> EitherT ServantErr IO [Book] -- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
-- > getBooksBy Nothing = ...return all books... -- > getBooksBy Nothing = ...return all books...
-- > getBooksBy (Just author) = ...return books by the given author... -- > getBooksBy (Just author) = ...return books by the given author...
instance (KnownSymbol sym, FromText a, HasServer sublayout) instance (KnownSymbol sym, FromText a, HasServer sublayout)
@ -496,7 +496,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBooksBy -- > server = getBooksBy
-- > where getBooksBy :: [Text] -> EitherT ServantErr IO [Book] -- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
-- > getBooksBy authors = ...return all books by these authors... -- > getBooksBy authors = ...return all books by these authors...
instance (KnownSymbol sym, FromText a, HasServer sublayout) instance (KnownSymbol sym, FromText a, HasServer sublayout)
=> HasServer (QueryParams sym a :> sublayout) where => HasServer (QueryParams sym a :> sublayout) where
@ -527,7 +527,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBooks -- > server = getBooks
-- > where getBooks :: Bool -> EitherT ServantErr IO [Book] -- > where getBooks :: Bool -> ExceptT ServantErr IO [Book]
-- > getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument... -- > getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument...
instance (KnownSymbol sym, HasServer sublayout) instance (KnownSymbol sym, HasServer sublayout)
=> HasServer (QueryFlag sym :> sublayout) where => HasServer (QueryFlag sym :> sublayout) where
@ -567,7 +567,7 @@ parseMatrixText = parseQueryText
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBooksBy -- > server = getBooksBy
-- > where getBooksBy :: Maybe Text -> EitherT ServantErr IO [Book] -- > where getBooksBy :: Maybe Text -> ExceptT ServantErr IO [Book]
-- > getBooksBy Nothing = ...return all books... -- > getBooksBy Nothing = ...return all books...
-- > getBooksBy (Just author) = ...return books by the given author... -- > getBooksBy (Just author) = ...return books by the given author...
instance (KnownSymbol sym, FromText a, HasServer sublayout) instance (KnownSymbol sym, FromText a, HasServer sublayout)
@ -607,7 +607,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBooksBy -- > server = getBooksBy
-- > where getBooksBy :: [Text] -> EitherT ServantErr IO [Book] -- > where getBooksBy :: [Text] -> ExceptT ServantErr IO [Book]
-- > getBooksBy authors = ...return all books by these authors... -- > getBooksBy authors = ...return all books by these authors...
instance (KnownSymbol sym, FromText a, HasServer sublayout) instance (KnownSymbol sym, FromText a, HasServer sublayout)
=> HasServer (MatrixParams sym a :> sublayout) where => HasServer (MatrixParams sym a :> sublayout) where
@ -641,7 +641,7 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = getBooks -- > server = getBooks
-- > where getBooks :: Bool -> EitherT ServantErr IO [Book] -- > where getBooks :: Bool -> ExceptT ServantErr IO [Book]
-- > getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument... -- > getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument...
instance (KnownSymbol sym, HasServer sublayout) instance (KnownSymbol sym, HasServer sublayout)
=> HasServer (MatrixFlag sym :> sublayout) where => HasServer (MatrixFlag sym :> sublayout) where
@ -699,7 +699,7 @@ instance HasServer Raw where
-- > -- >
-- > server :: Server MyApi -- > server :: Server MyApi
-- > server = postBook -- > server = postBook
-- > where postBook :: Book -> EitherT ServantErr IO Book -- > where postBook :: Book -> ExceptT ServantErr IO Book
-- > postBook book = ...insert into your db... -- > postBook book = ...insert into your db...
instance ( AllCTUnrender list a, HasServer sublayout instance ( AllCTUnrender list a, HasServer sublayout
) => HasServer (ReqBody list a :> sublayout) where ) => HasServer (ReqBody list a :> sublayout) where

View file

@ -22,9 +22,6 @@ import Control.Monad.Morph
import Control.Monad.Reader import Control.Monad.Reader
import qualified Control.Monad.State.Lazy as LState import qualified Control.Monad.State.Lazy as LState
import qualified Control.Monad.State.Strict as SState import qualified Control.Monad.State.Strict as SState
#if MIN_VERSION_mtl(2,2,1)
import Control.Monad.Trans.Either
#endif
import qualified Control.Monad.Writer.Lazy as LWriter import qualified Control.Monad.Writer.Lazy as LWriter
import qualified Control.Monad.Writer.Strict as SWriter import qualified Control.Monad.Writer.Strict as SWriter
import Data.Typeable import Data.Typeable
@ -83,11 +80,6 @@ logWriterTLNat logger = Nat $ \x -> do
liftIO $ logger w liftIO $ logger w
return a return a
#if MIN_VERSION_mtl(2,2,1)
fromExceptT :: ExceptT e m :~> EitherT e m
fromExceptT = Nat $ \x -> EitherT $ runExceptT x
#endif
-- | Like @mmorph@'s `hoist`. -- | Like @mmorph@'s `hoist`.
hoistNat :: (MFunctor t, Monad m) => (m :~> n) -> (t m :~> t n) hoistNat :: (MFunctor t, Monad m) => (m :~> n) -> (t m :~> t n)
hoistNat (Nat n) = Nat $ hoist n hoistNat (Nat n) = Nat $ hoist n

View file

@ -8,7 +8,7 @@ module Servant.Server.Internal.RoutingApplication where
import Control.Applicative (Applicative, (<$>)) import Control.Applicative (Applicative, (<$>))
import Data.Monoid (Monoid, mappend, mempty) import Data.Monoid (Monoid, mappend, mempty)
#endif #endif
import Control.Monad.Trans.Either (EitherT, runEitherT) import Control.Monad.Trans.Except (ExceptT, runExceptT)
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
import Data.IORef (newIORef, readIORef, import Data.IORef (newIORef, readIORef,
@ -107,7 +107,7 @@ toApplication ra request respond = do
routingRespond (Right response) = routingRespond (Right response) =
respond response respond response
runAction :: IO (RouteResult (EitherT ServantErr IO a)) runAction :: IO (RouteResult (ExceptT ServantErr IO a))
-> (RouteResult Response -> IO r) -> (RouteResult Response -> IO r)
-> (a -> RouteResult Response) -> (a -> RouteResult Response)
-> IO r -> IO r
@ -116,7 +116,7 @@ runAction action respond k = do
go r go r
where where
go (RR (Right a)) = do go (RR (Right a)) = do
e <- runEitherT a e <- runExceptT a
respond $ case e of respond $ case e of
Right x -> k x Right x -> k x
Left err -> succeedWith $ responseServantErr err Left err -> succeedWith $ responseServantErr err

View file

@ -5,7 +5,7 @@ module Servant.Server.Internal.EnterSpec where
import qualified Control.Category as C import qualified Control.Category as C
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.Either import Control.Monad.Trans.Except
import Data.Proxy import Data.Proxy
import Servant.API import Servant.API
import Servant.Server import Servant.Server
@ -34,7 +34,7 @@ combinedAPI = Proxy
readerServer' :: ServerT ReaderAPI (Reader String) readerServer' :: ServerT ReaderAPI (Reader String)
readerServer' = return 1797 :<|> ask readerServer' = return 1797 :<|> ask
fReader :: Reader String :~> EitherT ServantErr IO fReader :: Reader String :~> ExceptT ServantErr IO
fReader = generalizeNat C.. (runReaderTNat "hi") fReader = generalizeNat C.. (runReaderTNat "hi")
readerServer :: Server ReaderAPI readerServer :: Server ReaderAPI

View file

@ -10,7 +10,7 @@ module Servant.ServerSpec where
import Control.Monad (forM_, when) import Control.Monad (forM_, when)
import Control.Monad.Trans.Either (EitherT, left) import Control.Monad.Trans.Except (ExceptT, throwE)
import Data.Aeson (FromJSON, ToJSON, decode', encode) import Data.Aeson (FromJSON, ToJSON, decode', encode)
import Data.ByteString.Conversion () import Data.ByteString.Conversion ()
import Data.Char (toUpper) import Data.Char (toUpper)
@ -99,11 +99,11 @@ spec = do
type CaptureApi = Capture "legs" Integer :> Get '[JSON] Animal type CaptureApi = Capture "legs" Integer :> Get '[JSON] Animal
captureApi :: Proxy CaptureApi captureApi :: Proxy CaptureApi
captureApi = Proxy captureApi = Proxy
captureServer :: Integer -> EitherT ServantErr IO Animal captureServer :: Integer -> ExceptT ServantErr IO Animal
captureServer legs = case legs of captureServer legs = case legs of
4 -> return jerry 4 -> return jerry
2 -> return tweety 2 -> return tweety
_ -> left err404 _ -> throwE err404
captureSpec :: Spec captureSpec :: Spec
captureSpec = do captureSpec = do
@ -481,11 +481,11 @@ headerApi = Proxy
headerSpec :: Spec headerSpec :: Spec
headerSpec = describe "Servant.API.Header" $ do headerSpec = describe "Servant.API.Header" $ do
let expectsInt :: Maybe Int -> EitherT ServantErr IO () let expectsInt :: Maybe Int -> ExceptT ServantErr IO ()
expectsInt (Just x) = when (x /= 5) $ error "Expected 5" expectsInt (Just x) = when (x /= 5) $ error "Expected 5"
expectsInt Nothing = error "Expected an int" expectsInt Nothing = error "Expected an int"
let expectsString :: Maybe String -> EitherT ServantErr IO () let expectsString :: Maybe String -> ExceptT ServantErr IO ()
expectsString (Just x) = when (x /= "more from you") $ error "Expected more from you" expectsString (Just x) = when (x /= "more from you") $ error "Expected more from you"
expectsString Nothing = error "Expected a string" expectsString Nothing = error "Expected a string"

View file

@ -14,4 +14,4 @@ packages:
- servant-server/ - servant-server/
extra-deps: extra-deps:
- engine-io-wai-1.0.2 - engine-io-wai-1.0.2
resolver: nightly-2015-09-05 resolver: nightly-2015-09-10