Skip to content

Commit 5fa99be

Browse files
author
Catherine Galkina
committed
Expand left-nested APIs by default
1 parent d128fae commit 5fa99be

File tree

1 file changed

+40
-11
lines changed

1 file changed

+40
-11
lines changed

servant-client/src/Servant/Client/Generic.hs

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
{-# LANGUAGE MultiParamTypeClasses #-}
66
{-# LANGUAGE TypeFamilies #-}
77
{-# LANGUAGE TypeOperators #-}
8+
{-# LANGUAGE UndecidableInstances #-}
89
module Servant.Client.Generic
910
( ClientLike(..)
10-
, genericMkClient
11+
, genericMkClientL
12+
, genericMkClientP
1113
) where
1214

13-
import Generics.SOP (Generic, I(..), NP(..), NS(Z), Rep, SOP(..), to)
15+
import Generics.SOP (Code, Generic, I(..), NP(..), NS(Z), Rep, SOP(..), to)
1416
import Servant.API ((:<|>)(..))
1517
import Servant.Client (ClientM)
1618

@@ -40,20 +42,20 @@ import Servant.Client (ClientM)
4042
-- > instance (Client API ~ client) => ClientLike client APIClient
4143
-- >
4244
-- > data NestedClient = NestedClient
43-
-- > { getString :: ClientM String
45+
-- > { getString :: ClientM String
4446
-- > , postBaz :: Maybe Char -> ClientM ()
4547
-- > } deriving GHC.Generic
4648
-- >
47-
-- > instance Generic.SOP.Generic
49+
-- > instance Generic.SOP.Generic NestedClient
4850
-- > instance (Client NestedAPI ~ client) => ClientLike client NestedClient
4951
-- >
5052
-- > mkAPIClient :: APIClient
5153
-- > mkAPIClient = mkClient (client (Proxy :: Proxy API))
5254
class ClientLike client custom where
5355
mkClient :: client -> custom
54-
default mkClient :: (Generic custom, GClientLikeP client xs, SOP I '[xs] ~ Rep custom)
56+
default mkClient :: (Generic custom, Code custom ~ '[xs], GClientList client '[], GClientLikeL (ClientList client '[]) xs)
5557
=> client -> custom
56-
mkClient = genericMkClient
58+
mkClient = genericMkClientL
5759

5860
instance ClientLike client custom
5961
=> ClientLike (a -> client) (a -> custom) where
@@ -62,9 +64,7 @@ instance ClientLike client custom
6264
instance ClientLike (ClientM a) (ClientM a) where
6365
mkClient = id
6466

65-
-- | This class is used to match client functions to the
66-
-- representation of client structure type as sum of products
67-
-- and basically does all the internal job to build this structure.
67+
-- GClientLikeP
6868
class GClientLikeP client xs where
6969
gMkClientP :: client -> NP I xs
7070

@@ -75,8 +75,37 @@ instance (GClientLikeP b (y ': xs), ClientLike a x)
7575
instance ClientLike a x => GClientLikeP a '[x] where
7676
gMkClientP a = I (mkClient a) :* Nil
7777

78+
-- GClientLikeL
79+
class GClientLikeL (xs :: [*]) (ys :: [*]) where
80+
gMkClientL :: NP I xs -> NP I ys
81+
82+
instance GClientLikeL '[] '[] where
83+
gMkClientL Nil = Nil
84+
85+
instance (ClientLike x y, GClientLikeL xs ys) => GClientLikeL (x ': xs) (y ': ys) where
86+
gMkClientL (I x :* xs) = I (mkClient x) :* gMkClientL xs
87+
88+
type family ClientList (client :: *) (acc :: [*]) :: [*] where
89+
ClientList (a :<|> b) acc = ClientList a (ClientList b acc)
90+
ClientList a acc = a ': acc
91+
92+
class GClientList client (acc :: [*]) where
93+
gClientList :: client -> NP I acc -> NP I (ClientList client acc)
94+
95+
instance (GClientList b acc, GClientList a (ClientList b acc))
96+
=> GClientList (a :<|> b) acc where
97+
gClientList (a :<|> b) acc = gClientList a (gClientList b acc)
98+
99+
instance {-# OVERLAPPABLE #-} (ClientList client acc ~ (client ': acc))
100+
=> GClientList client acc where
101+
gClientList c acc = I c :* acc
102+
78103
-- | Generate client structure from client type.
79-
genericMkClient :: (Generic custom, GClientLikeP client xs, SOP I '[xs] ~ Rep custom)
104+
genericMkClientL :: (Generic custom, Code custom ~ '[xs], GClientList client '[], GClientLikeL (ClientList client '[]) xs)
105+
=> client -> custom
106+
genericMkClientL = to . SOP . Z . gMkClientL . flip gClientList Nil
107+
108+
genericMkClientP :: (Generic custom, Code custom ~ '[xs], GClientLikeP client xs)
80109
=> client -> custom
81-
genericMkClient = to . SOP . Z . gMkClientP
110+
genericMkClientP = to . SOP . Z . gMkClientP
82111

0 commit comments

Comments
 (0)