{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ScopedTypeVariables #-} module Servant.API.Alternative where import Data.Proxy import Servant.Client import Servant.Docs import Servant.Server -- | Union of two APIs, first takes precedence in case of overlap. -- -- Example: -- -- > type MyApi = "books" :> Get [Book] -- GET /books -- > :<|> "books" :> ReqBody Book :> Post Book -- POST /books data a :<|> b = a :<|> b infixr 8 :<|> -- | A server for @a ':<|>' b@ first tries to match the request again the route -- represented by @a@ and if it fails tries @b@. You must provide a request -- handler for each route. -- -- > type MyApi = "books" :> Get [Book] -- GET /books -- > :<|> "books" :> ReqBody Book :> Post Book -- POST /books -- > -- > server :: Server MyApi -- > server = listAllBooks :<|> postBook -- > where listAllBooks = ... -- > postBook book = ... instance (HasServer a, HasServer b) => HasServer (a :<|> b) where type Server (a :<|> b) = Server a :<|> Server b route Proxy (a :<|> b) request respond = route pa a request $ \ mResponse -> if isMismatch mResponse then route pb b request $ \mResponse' -> respond (mResponse <> mResponse') else respond mResponse where pa = Proxy :: Proxy a pb = Proxy :: Proxy b -- | A client querying function for @a ':<|>' b@ will actually hand you -- one function for querying @a@ and another one for querying @b@, -- stitching them together with ':<|>', which really is just like a pair. -- -- > type MyApi = "books" :> Get [Book] -- GET /books -- > :<|> "books" :> ReqBody Book :> Post Book -- POST /books -- > -- > myApi :: Proxy MyApi -- > myApi = Proxy -- > -- > getAllBooks :: BaseUrl -> EitherT String IO [Book] -- > postNewBook :: Book -> BaseUrl -> EitherT String IO Book -- > (getAllBooks :<|> postNewBook) = client myApi instance (HasClient a, HasClient b) => HasClient (a :<|> b) where type Client (a :<|> b) = Client a :<|> Client b clientWithRoute Proxy req = clientWithRoute (Proxy :: Proxy a) req :<|> clientWithRoute (Proxy :: Proxy b) req -- | The generated docs for @a ':<|>' b@ just appends the docs -- for @a@ with the docs for @b@. instance (HasDocs layout1, HasDocs layout2) => HasDocs (layout1 :<|> layout2) where docsFor Proxy (ep, action) = docsFor p1 (ep, action) <> docsFor p2 (ep, action) where p1 :: Proxy layout1 p1 = Proxy p2 :: Proxy layout2 p2 = Proxy