5
5
{-# LANGUAGE MultiParamTypeClasses #-}
6
6
{-# LANGUAGE TypeFamilies #-}
7
7
{-# LANGUAGE TypeOperators #-}
8
+ {-# LANGUAGE UndecidableInstances #-}
8
9
module Servant.Client.Generic
9
10
( ClientLike (.. )
10
- , genericMkClient
11
+ , genericMkClientL
12
+ , genericMkClientP
11
13
) where
12
14
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 )
14
16
import Servant.API ((:<|>) (.. ))
15
17
import Servant.Client (ClientM )
16
18
@@ -40,20 +42,20 @@ import Servant.Client (ClientM)
40
42
-- > instance (Client API ~ client) => ClientLike client APIClient
41
43
-- >
42
44
-- > data NestedClient = NestedClient
43
- -- > { getString :: ClientM String
45
+ -- > { getString :: ClientM String
44
46
-- > , postBaz :: Maybe Char -> ClientM ()
45
47
-- > } deriving GHC.Generic
46
48
-- >
47
- -- > instance Generic.SOP.Generic
49
+ -- > instance Generic.SOP.Generic NestedClient
48
50
-- > instance (Client NestedAPI ~ client) => ClientLike client NestedClient
49
51
-- >
50
52
-- > mkAPIClient :: APIClient
51
53
-- > mkAPIClient = mkClient (client (Proxy :: Proxy API))
52
54
class ClientLike client custom where
53
55
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 )
55
57
=> client -> custom
56
- mkClient = genericMkClient
58
+ mkClient = genericMkClientL
57
59
58
60
instance ClientLike client custom
59
61
=> ClientLike (a -> client ) (a -> custom ) where
@@ -62,9 +64,7 @@ instance ClientLike client custom
62
64
instance ClientLike (ClientM a ) (ClientM a ) where
63
65
mkClient = id
64
66
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
68
68
class GClientLikeP client xs where
69
69
gMkClientP :: client -> NP I xs
70
70
@@ -75,8 +75,37 @@ instance (GClientLikeP b (y ': xs), ClientLike a x)
75
75
instance ClientLike a x => GClientLikeP a '[x ] where
76
76
gMkClientP a = I (mkClient a) :* Nil
77
77
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
+
78
103
-- | 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 )
80
109
=> client -> custom
81
- genericMkClient = to . SOP . Z . gMkClientP
110
+ genericMkClientP = to . SOP . Z . gMkClientP
82
111
0 commit comments