@@ -2,10 +2,13 @@
22{-# LANGUAGE AllowAmbiguousTypes #-}
33{-# LANGUAGE DataKinds #-}
44{-# LANGUAGE DerivingStrategies #-}
5+{-# LANGUAGE FlexibleContexts #-}
56{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE GADTs #-}
68{-# LANGUAGE GeneralizedNewtypeDeriving #-}
79{-# LANGUAGE LambdaCase #-}
810{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE RecordWildCards #-}
912{-# LANGUAGE ScopedTypeVariables #-}
1013{-# LANGUAGE StandaloneDeriving #-}
1114{-# LANGUAGE TupleSections #-}
@@ -18,43 +21,76 @@ module Roboservant.Types.ReifiedApi where
1821
1922
2023import Control.Monad.Except (runExceptT)
24+import Data.Bifunctor
25+import Data.Dependent.Sum
2126import Data.Dynamic (Dynamic, toDyn)
2227import Data.Function ((&))
28+import Data.Kind
29+import Data.List.NonEmpty (NonEmpty)
2330import Data.Typeable (TypeRep, Typeable, typeRep)
31+import GHC.Generics ((:*:)(..))
2432import GHC.TypeLits (Symbol)
33+import Roboservant.Types.Breakdown
34+import Roboservant.Types.FlattenServer
2535import Servant
2636import Servant.API.Modifiers(FoldRequired,FoldLenient)
27−import Roboservant.Types.FlattenServer
28−import Roboservant.Types.Breakdown
29−import Data.List.NonEmpty (NonEmpty)
37+import qualified Data.Text as T
38+import qualified Data.Vinyl as V
39+import qualified Data.Vinyl.Curry as V
40+import qualified Type.Reflection as R
3041
3142
3243newtype ApiOffset = ApiOffset Int
3344 deriving (Eq, Show)
3445 deriving newtype (Enum, Num)
3546
36−type ReifiedEndpoint = ([(TypeRep, Stash -> Maybe (NonEmpty ([Provenance], Dynamic)))], Dynamic)
47+type TypedF = (:*:) R.TypeRep
48+
49+newtype Argument a = Argument
50+ { getArgument :: Stash -> Maybe (StashValue a)
51+ }
52+
53+data ReifiedEndpoint = forall as. (V.RecordToList as, V.RMap as) => ReifiedEndpoint
54+ { reArguments :: V.Rec (TypedF Argument) as
55+ , reEndpointFunc :: V.Curried as (IO (Either ServerError (NonEmpty Dynamic)))
56+ }
3757
3858type ReifiedApi = [(ApiOffset, ReifiedEndpoint)]
3959
60+tagType :: Typeable a => f a -> TypedF f a
61+tagType = (R.typeRep :*:)
4062
4163class ToReifiedApi (endpoints :: [*]) where
4264 toReifiedApi :: Bundled endpoints -> Proxy endpoints -> ReifiedApi
4365
44−class ToReifiedEndpoint (endpoint :: *) where
45− toReifiedEndpoint :: Dynamic -> Proxy endpoint -> ReifiedEndpoint
66+class ( V.Curried (EndpointArgs endpoint) (Handler (EndpointRes endpoint)) ~ Server endpoint
67+ , V.RecordToList (EndpointArgs endpoint)
68+ , V.RMap (EndpointArgs endpoint)
69+ ) => ToReifiedEndpoint (endpoint :: *) where
70+ type EndpointArgs endpoint :: [Type]
71+ type EndpointRes endpoint :: Type
72+
73+ reifiedEndpointArguments :: V.Rec (TypedF Argument) (EndpointArgs endpoint)
4674
4775instance ToReifiedApi '[] where
4876 toReifiedApi NoEndpoints _ = []
4977
5078instance
51− (Typeable (Normal (ServerT endpoint Handler)), NormalizeFunction (ServerT endpoint Handler), ToReifiedEndpoint endpoint, ToReifiedApi endpoints, Typeable (ServerT endpoint Handler)) =>
79+ ( Typeable (EndpointRes endpoint)
80+ , NormalizeFunction (ServerT endpoint Handler)
81+ , Normal (ServerT endpoint Handler) ~ V.Curried (EndpointArgs endpoint) (IO (Either ServerError (NonEmpty Dynamic)))
82+ , ToReifiedEndpoint endpoint
83+ , ToReifiedApi endpoints, Typeable (ServerT endpoint Handler)
84+ ) =>
5285 ToReifiedApi (endpoint : endpoints)
5386 where
5487 toReifiedApi (endpoint `AnEndpoint` endpoints) _ =
55− (0,) (toReifiedEndpoint (toDyn (normalize endpoint)) (Proxy @endpoint))
56− : map
57− (\(n, x) -> (n + 1, x))
88+ (0, ReifiedEndpoint
89+ { reArguments = reifiedEndpointArguments @endpoint
90+ , reEndpointFunc = normalize endpoint
91+ }
92+ )
93+ : (map . first) (+1)
5894 (toReifiedApi endpoints (Proxy @endpoints))
5995
6096class NormalizeFunction m where
@@ -71,35 +107,37 @@ instance (Typeable x, Breakdown x) => NormalizeFunction (Handler x) where
71107 Left serverError -> pure (Left serverError)
72108 Right x -> pure $ Right $ breakdown x
73109
74−-- pure (Right (typeRep (Proxy @x), toDyn x))
75−
76110instance
77111 (Typeable responseType, Breakdown responseType) =>
78112 ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
79113 where
80− toReifiedEndpoint endpoint _ = id
81− ([], endpoint)
114+ type EndpointArgs (Verb method statusCode contentTypes responseType) = '[]
115+ type EndpointRes (Verb method statusCode contentTypes responseType) = responseType
116+ reifiedEndpointArguments = V.RNil
82117
83118instance
84119 (ToReifiedEndpoint endpoint) =>
85120 ToReifiedEndpoint ((x :: Symbol) :> endpoint)
86121 where
87− toReifiedEndpoint endpoint _ =
88− toReifiedEndpoint endpoint (Proxy @endpoint)
122+ type EndpointArgs ((x :: Symbol) :> endpoint) = EndpointArgs endpoint
123+ type EndpointRes ((x :: Symbol) :> endpoint) = EndpointRes endpoint
124+ reifiedEndpointArguments = reifiedEndpointArguments @endpoint
89125
90126instance
91127 (ToReifiedEndpoint endpoint) =>
92128 ToReifiedEndpoint (Description s :> endpoint)
93129 where
94− toReifiedEndpoint endpoint _ =
95− toReifiedEndpoint endpoint (Proxy @endpoint)
130+ type EndpointArgs (Description s :> endpoint) = EndpointArgs endpoint
131+ type EndpointRes (Description s :> endpoint) = EndpointRes endpoint
132+ reifiedEndpointArguments = reifiedEndpointArguments @endpoint
96133
97134instance
98135 (ToReifiedEndpoint endpoint) =>
99136 ToReifiedEndpoint (Summary s :> endpoint)
100137 where
101− toReifiedEndpoint endpoint _ =
102− toReifiedEndpoint endpoint (Proxy @endpoint)
138+ type EndpointArgs (Summary s :> endpoint) = EndpointArgs endpoint
139+ type EndpointRes (Summary s :> endpoint) = EndpointRes endpoint
140+ reifiedEndpointArguments = reifiedEndpointArguments @endpoint
103141
104142instance
105143 (Typeable requestType
@@ -107,58 +145,57 @@ instance
107145 ,ToReifiedEndpoint endpoint) =>
108146 ToReifiedEndpoint (QueryFlag name :> endpoint)
109147 where
110− toReifiedEndpoint endpoint _ =
111− toReifiedEndpoint endpoint (Proxy @endpoint)
112− & \(args, result) -> ((typeRep (Proxy @Bool),buildFrom @Bool) : args, result)
148+ type EndpointArgs (QueryFlag name :> endpoint) = Bool ': EndpointArgs endpoint
149+ type EndpointRes (QueryFlag name :> endpoint) = EndpointRes endpoint
150+ reifiedEndpointArguments = tagType (Argument (buildFrom @Bool)) V.:& reifiedEndpointArguments @endpoint
151+
152+type IfLenient s mods t = If (FoldLenient mods) (Either s t) t
153+type IfRequired mods t = If (FoldRequired mods) t (Maybe t)
154+type IfRequiredLenient s mods t = IfRequired mods (IfLenient s mods t)
113155
114156instance
115− ( Typeable (If (FoldRequired mods) paramType (Maybe paramType))
116− , BuildFrom (If (FoldRequired mods) paramType (Maybe paramType))
157+ ( BuildFrom (IfRequiredLenient T.Text mods paramType)
117158 , ToReifiedEndpoint endpoint
118− , SBoolI (FoldRequired mods)) =>
159+ ) =>
119160 ToReifiedEndpoint (QueryParam' mods name paramType :> endpoint)
120161 where
121− toReifiedEndpoint endpoint _ =
122− toReifiedEndpoint endpoint (Proxy @endpoint)
123− & \(args, result) ->
124− ((typeRep (Proxy @(If (FoldRequired mods) paramType (Maybe paramType)))
125− ,buildFrom @(If (FoldRequired mods) paramType (Maybe paramType)))
126− : args, result)
162+ type EndpointArgs (QueryParam' mods name paramType :> endpoint) = IfRequiredLenient T.Text mods paramType ': EndpointArgs endpoint
163+ type EndpointRes (QueryParam' mods name paramType :> endpoint) = EndpointRes endpoint
164+ reifiedEndpointArguments =
165+ tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods paramType)))
166+ V.:& reifiedEndpointArguments @endpoint
167+
127168
128169instance
129− ( Typeable (If (FoldRequired mods) headerType (Maybe headerType))
130− , BuildFrom (If (FoldRequired mods) headerType (Maybe headerType))
170+ ( BuildFrom (IfRequiredLenient T.Text mods headerType)
131171 , ToReifiedEndpoint endpoint
132− , SBoolI (FoldRequired mods)) =>
172+ ) =>
133173 ToReifiedEndpoint (Header' mods headerName headerType :> endpoint)
134174 where
135− toReifiedEndpoint endpoint _ =
136− toReifiedEndpoint endpoint (Proxy @endpoint)
137− & \(args, result) -> ((typeRep (Proxy @(If (FoldRequired mods) headerType (Maybe headerType)))
138− ,buildFrom @(If (FoldRequired mods) headerType (Maybe headerType)))
139−
140− : args, result)
175+ type EndpointArgs (Header' mods headerName headerType :> endpoint) = IfRequiredLenient T.Text mods headerType ': EndpointArgs endpoint
176+ type EndpointRes (Header' mods headerName headerType :> endpoint) = EndpointRes endpoint
177+ reifiedEndpointArguments =
178+ tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods headerType)))
179+ V.:& reifiedEndpointArguments @endpoint
141180
142181instance
143− ( Typeable captureType
144− , BuildFrom captureType
182+ ( BuildFrom (IfLenient String mods captureType)
145183 , ToReifiedEndpoint endpoint) =>
146184 ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
147185 where
148− toReifiedEndpoint endpoint _ =
149− toReifiedEndpoint endpoint (Proxy @endpoint)
150− & \(args, result) -> ((typeRep (Proxy @captureType)
151− ,buildFrom @captureType)
152− : args, result)
186+ type EndpointArgs (Capture' mods name captureType :> endpoint) = IfLenient String mods captureType ': EndpointArgs endpoint
187+ type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
188+ reifiedEndpointArguments =
189+ tagType (Argument (buildFrom @(IfLenient String mods captureType)))
190+ V.:& reifiedEndpointArguments @endpoint
153191
154192instance
155− ( Typeable (If (FoldLenient mods) (Either String requestType) requestType)
156− , BuildFrom (If (FoldLenient mods) (Either String requestType) requestType)
157− , ToReifiedEndpoint endpoint, SBoolI (FoldLenient mods)) =>
193+ ( BuildFrom (IfLenient String mods requestType)
194+ , ToReifiedEndpoint endpoint) =>
158195 ToReifiedEndpoint (ReqBody' mods contentTypes requestType :> endpoint)
159196 where
160− toReifiedEndpoint endpoint _ =
161− toReifiedEndpoint endpoint (Proxy @endpoint)
162− & \(args, result) -> ((typeRep (Proxy @(If (FoldLenient mods) (Either String requestType) requestType))
163− ,buildFrom @(If (FoldLenient mods) (Either String requestType) requestType))
164− : args, result)
197+ type EndpointArgs (ReqBody' mods contentTypes requestType :> endpoint) = IfLenient String mods requestType ': EndpointArgs endpoint
198+ type EndpointRes (ReqBody' mods contentTypes requestType :> endpoint) = EndpointRes endpoint
199+ reifiedEndpointArguments =
200+ tagType (Argument (buildFrom @(IfLenient String mods requestType)))
201+ V.:& reifiedEndpointArguments @endpoint