Skip to content

Commit

Permalink
Add new HasClient instances to match the new HasServer instances
Browse files Browse the repository at this point in the history
  • Loading branch information
lexi-lambda committed May 3, 2017
1 parent 9d9e3b9 commit d4ff777
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 22 deletions.
15 changes: 15 additions & 0 deletions src/Servant/Checked/Exceptions/Internal/Servant/API.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

{- |
Module : Servant.Checked.Exceptions.Internal.Servant.API
Expand All @@ -16,6 +18,10 @@ This module defines the 'Throws' and 'Throwing' types.

module Servant.Checked.Exceptions.Internal.Servant.API where

import Servant.API ((:>))

import Servant.Checked.Exceptions.Internal.Util (Snoc)

-- | 'Throws' is used in Servant API definitions and signifies that an API will
-- throw the given error.
--
Expand All @@ -28,3 +34,12 @@ data Throws (e :: *)

-- | This is used internally and should not be used by end-users.
data Throwing (e :: [*])

-- | Used by the 'HasServer' and 'HasClient' instances for
-- @'Throwing' es ':>' api ':>' apis@ to detect @'Throwing' es@ followed
-- immediately by @'Throws' e@.
type family ThrowingNonterminal api where
ThrowingNonterminal (Throwing es :> Throws e :> api) =
Throwing (Snoc es e) :> api
ThrowingNonterminal (Throwing es :> c :> api) =
c :> Throwing es :> api
39 changes: 27 additions & 12 deletions src/Servant/Checked/Exceptions/Internal/Servant/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ This module only exports 'HasClient' instances for 'Throws' and 'Throwing'.
module Servant.Checked.Exceptions.Internal.Servant.Client where

import Data.Proxy (Proxy(Proxy))
import Servant.API (Verb, (:>))
import Servant.API (Verb, (:>), (:<|>))
import Servant.Client (HasClient(clientWithRoute, Client))
import Servant.Common.Req (Req)

import Servant.Checked.Exceptions.Internal.Envelope (Envelope)
import Servant.Checked.Exceptions.Internal.Servant.API
(Throws, Throwing)
import Servant.Checked.Exceptions.Internal.Util (Snoc)
(Throws, Throwing, ThrowingNonterminal)

-- TODO: Make sure to also account for when headers are being used.

Expand Down Expand Up @@ -63,17 +62,33 @@ instance (HasClient (Verb method status ctypes (Envelope es a))) =>
clientWithRoute Proxy =
clientWithRoute (Proxy :: Proxy (Verb method status ctypes (Envelope es a)))

-- | When @'Throwing' es@ comes before ':<|>', push @'Throwing' es@ into each
-- branch of the API.
instance HasClient ((Throwing es :> api1) :<|> (Throwing es :> api2)) =>
HasClient (Throwing es :> (api1 :<|> api2)) where

type Client (Throwing es :> (api1 :<|> api2)) =
Client ((Throwing es :> api1) :<|> (Throwing es :> api2))

clientWithRoute
:: Proxy (Throwing es :> (api1 :<|> api2))
-> Req
-> Client ((Throwing es :> api1) :<|> (Throwing es :> api2))
clientWithRoute _ =
clientWithRoute (Proxy :: Proxy ((Throwing es :> api1) :<|> (Throwing es :> api2)))

-- | When a @'Throws' e@ comes immediately after a @'Throwing' es@, 'Snoc' the
-- @e@ onto the @es@.
instance (HasClient (Throwing (Snoc es e) :> api)) =>
HasClient (Throwing es :> Throws e :> api) where
-- @e@ onto the @es@. Otherwise, if @'Throws' e@ comes before any other
-- combinator, push it down so it is closer to the 'Verb'.
instance HasClient (ThrowingNonterminal (Throwing es :> api :> apis)) =>
HasClient (Throwing es :> api :> apis) where

type Client (Throwing es :> Throws e :> api) =
Client (Throwing (Snoc es e) :> api)
type Client (Throwing es :> api :> apis) =
Client (ThrowingNonterminal (Throwing es :> api :> apis))

clientWithRoute
:: Proxy (Throwing es :> Throws e :> api)
:: Proxy (Throwing es :> api :> apis)
-> Req
-> Client (Throwing (Snoc es e) :> api)
clientWithRoute Proxy =
clientWithRoute (Proxy :: Proxy (Throwing (Snoc es e) :> api))
-> Client (ThrowingNonterminal (Throwing es :> api :> apis))
clientWithRoute _ =
clientWithRoute (Proxy :: Proxy (ThrowingNonterminal (Throwing es :> api :> apis)))
11 changes: 1 addition & 10 deletions src/Servant/Checked/Exceptions/Internal/Servant/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ import Servant

import Servant.Checked.Exceptions.Internal.Envelope (Envelope)
import Servant.Checked.Exceptions.Internal.Servant.API
(Throws, Throwing)
import Servant.Checked.Exceptions.Internal.Util (Snoc)
(Throws, Throwing, ThrowingNonterminal)

-- TODO: Make sure to also account for when headers are being used.

Expand Down Expand Up @@ -83,14 +82,6 @@ instance HasServer ((Throwing es :> api1) :<|> (Throwing es :> api2)) context =>
-> Router env
route _ = route (Proxy :: Proxy ((Throwing es :> api1) :<|> (Throwing es :> api2)))

-- | Used by the 'HasServer' instance for @'Throwing' es ':>' api ':>' apis@ to
-- detect @'Throwing' es@ followed immediately by @'Throws' e@.
type family ThrowingNonterminal api where
ThrowingNonterminal (Throwing es :> Throws e :> api) =
Throwing (Snoc es e) :> api
ThrowingNonterminal (Throwing es :> c :> api) =
c :> Throwing es :> api

-- | When a @'Throws' e@ comes immediately after a @'Throwing' es@, 'Snoc' the
-- @e@ onto the @es@. Otherwise, if @'Throws' e@ comes before any other
-- combinator, push it down so it is closer to the 'Verb'.
Expand Down

0 comments on commit d4ff777

Please sign in to comment.