@@ -1,217 +1,142 @@
1−-- | Description: ways to build a reified api from a servant description.
3−-- arguably this could be more general and be abstracted away from even relying on servant
4−-- but that's future work.
6{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE AllowAmbiguousTypes #-}
7{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
8{-# LANGUAGE DerivingStrategies #-}
4{-# LANGUAGE DerivingStrategies #-}
9−{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
11−{-# LANGUAGE GADTs #-}
12{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
13{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
14{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE PolyKinds #-}
15{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE StandaloneDeriving #-}
11+{-# LANGUAGE TupleSections #-}
16{-# LANGUAGE TypeApplications #-}
12{-# LANGUAGE TypeApplications #-}
17{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeFamilies #-}
18{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE TypeOperators #-}
19{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
20−{-# LANGUAGE CPP #-}
16+
21module Roboservant.Types.ReifiedApi where
17module Roboservant.Types.ReifiedApi where
23−import Data.Dynamic (Dynamic)
19+
24−import Control.Exception(Exception)
20+import Control.Monad.Except (runExceptT)
25−import Data.List.NonEmpty (NonEmpty)
21+import Data.Dynamic (Dynamic, toDyn)
22+import Data.Function ((&))
26−import Data.Typeable (Typeable)
23+import Data.Typeable (TypeRep, Typeable, typeRep)
27−import GHC.Generics ((:*:)(..))
24+import GHC.TypeLits (Symbol)
28−import Roboservant.Types.Internal
29−import Roboservant.Types.Breakdown
30−import Roboservant.Types.BuildFrom
31−import Data.Kind(Type)
32import Servant
25import Servant
33import Servant.API.Modifiers(FoldRequired,FoldLenient)
26import Servant.API.Modifiers(FoldRequired,FoldLenient)
34−import GHC.TypeLits (Symbol)
27+import Roboservant.Types.FlattenServer
35−import qualified Data.Text as T
28+import Roboservant.Types.Breakdown
36−import qualified Data.Vinyl as V
29+import Data.List.NonEmpty (NonEmpty)
37−import qualified Data.Vinyl.Curry as V
30+
38−import qualified Type.Reflection as R
40newtype ApiOffset = ApiOffset Int
32newtype ApiOffset = ApiOffset Int
41− deriving (Eq, Show, Ord)
33+ deriving (Eq, Show)
42 deriving newtype (Enum, Num)
34 deriving newtype (Enum, Num)
44−type TypedF = (:*:) R.TypeRep
36+type ReifiedEndpoint = ([TypeRep], NonEmpty Dynamic)
46−newtype Argument a = Argument
38+type ReifiedApi = [(ApiOffset, ReifiedEndpoint)]
47− { getArgument :: Stash -> Maybe (StashValue a)
50−data ReifiedEndpoint = forall as. (V.RecordToList as, V.RMap as) => ReifiedEndpoint
51− { reArguments :: V.Rec (TypedF Argument) as
52− , reEndpointFunc :: V.Curried as (IO (Either InteractionError (NonEmpty (Dynamic,Int))))
55−instance Show ReifiedEndpoint where
41+class ToReifiedApi (endpoints :: [*]) where
56− show _ = "lol"
42+ toReifiedApi :: Bundled endpoints -> Proxy endpoints -> ReifiedApi
58−class ( V.RecordToList (EndpointArgs endpoint)
59− , V.RMap (EndpointArgs endpoint)
60− ) => ToReifiedEndpoint (endpoint :: Type) where
44+class ToReifiedEndpoint (endpoint :: *) where
61− type EndpointArgs endpoint :: [Type]
45+ toReifiedEndpoint :: Dynamic -> Proxy endpoint -> ReifiedEndpoint
62− type EndpointRes endpoint :: Type
64− reifiedEndpointArguments :: V.Rec (TypedF Argument) (EndpointArgs endpoint)
47+instance ToReifiedApi '[] where
48+ toReifiedApi NoEndpoints _ = []
51+ (Typeable (Normal (ServerT endpoint Handler)), NormalizeFunction (ServerT endpoint Handler), ToReifiedEndpoint endpoint, ToReifiedApi endpoints, Typeable (ServerT endpoint Handler)) =>
52+ ToReifiedApi (endpoint : endpoints)
54+ toReifiedApi (endpoint `AnEndpoint` endpoints) _ =
55+ (0,) (toReifiedEndpoint (toDyn (normalize endpoint)) (Proxy @endpoint))
57+ (\(n, x) -> (n + 1, x))
58+ (toReifiedApi endpoints (Proxy @endpoints))
67−tagType :: Typeable a => f a -> TypedF f a
60+class NormalizeFunction m where
68−tagType = (R.typeRep :*:)
61+ type Normal m
62+ normalize :: m -> Normal m
70−data InteractionError = InteractionError
64+instance NormalizeFunction x => NormalizeFunction (r -> x) where
71− { errorMessage :: T.Text
65+ type Normal (r -> x) = r -> Normal x
72− , fatalError :: Bool
66+ normalize = fmap normalize
75−instance Exception InteractionError
68+instance (Typeable x, Breakdown x) => NormalizeFunction (Handler x) where
69+ type Normal (Handler x) = IO (Either ServerError (NonEmpty Dynamic))
70+ normalize handler = (runExceptT . runHandler') handler >>= \case
71+ Left serverError -> pure (Left serverError)
72+ Right x -> pure $ Right $ breakdown x
74+-- pure (Right (typeRep (Proxy @x), toDyn x))
80 (Typeable responseType, Breakdown responseType) =>
77 (Typeable responseType, Breakdown responseType) =>
81 ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
78 ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
83− type EndpointArgs (Verb method statusCode contentTypes responseType) = '[]
80+ toReifiedEndpoint endpoint _ = id
84− type EndpointRes (Verb method statusCode contentTypes responseType) = responseType
81+ ([], _ endpoint)
85− reifiedEndpointArguments = V.RNil
87−instance ToReifiedEndpoint (NoContentVerb method)
89− type EndpointArgs (NoContentVerb method) = '[]
90− type EndpointRes (NoContentVerb method) = NoContent
91− reifiedEndpointArguments = V.RNil
94 (ToReifiedEndpoint endpoint) =>
84 (ToReifiedEndpoint endpoint) =>
95 ToReifiedEndpoint ((x :: Symbol) :> endpoint)
85 ToReifiedEndpoint ((x :: Symbol) :> endpoint)
97− type EndpointArgs ((x :: Symbol) :> endpoint) = EndpointArgs endpoint
87+ toReifiedEndpoint endpoint _ =
98− type EndpointRes ((x :: Symbol) :> endpoint) = EndpointRes endpoint
88+ toReifiedEndpoint endpoint (Proxy @endpoint)
99− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
102− (ToReifiedEndpoint endpoint) =>
103− ToReifiedEndpoint (RemoteHost :> endpoint)
105− type EndpointArgs (RemoteHost :> endpoint) = EndpointArgs endpoint
106− type EndpointRes (RemoteHost :> endpoint) = EndpointRes endpoint
107− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
112 (ToReifiedEndpoint endpoint) =>
91 (ToReifiedEndpoint endpoint) =>
113 ToReifiedEndpoint (Description s :> endpoint)
92 ToReifiedEndpoint (Description s :> endpoint)
115− type EndpointArgs (Description s :> endpoint) = EndpointArgs endpoint
94+ toReifiedEndpoint endpoint _ =
116− type EndpointRes (Description s :> endpoint) = EndpointRes endpoint
95+ toReifiedEndpoint endpoint (Proxy @endpoint)
117− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
120 (ToReifiedEndpoint endpoint) =>
98 (ToReifiedEndpoint endpoint) =>
121 ToReifiedEndpoint (Summary s :> endpoint)
99 ToReifiedEndpoint (Summary s :> endpoint)
123− type EndpointArgs (Summary s :> endpoint) = EndpointArgs endpoint
101+ toReifiedEndpoint endpoint _ =
124− type EndpointRes (Summary s :> endpoint) = EndpointRes endpoint
102+ toReifiedEndpoint endpoint (Proxy @endpoint)
125− reifiedEndpointArguments = reifiedEndpointArguments @endpoint
128− (Typeable requestType
129− ,BuildFrom requestType
130− ,ToReifiedEndpoint endpoint) =>
105+ (Typeable requestType, ToReifiedEndpoint endpoint) =>
131 ToReifiedEndpoint (QueryFlag name :> endpoint)
106 ToReifiedEndpoint (QueryFlag name :> endpoint)
133− type EndpointArgs (QueryFlag name :> endpoint) = Bool ': EndpointArgs endpoint
108+ toReifiedEndpoint endpoint _ =
134− type EndpointRes (QueryFlag name :> endpoint) = EndpointRes endpoint
109+ toReifiedEndpoint endpoint (Proxy @endpoint)
135− reifiedEndpointArguments = tagType (Argument (buildFrom @Bool)) V.:& reifiedEndpointArguments @endpoint
110+ & \(args, result, typeRepMap) -> (typeRep (Proxy @Bool) : args, result, typeRepMap)
137−type IfLenient s mods t = If (FoldLenient mods) (Either s t) t
138−type IfRequired mods t = If (FoldRequired mods) t (Maybe t)
139−type IfRequiredLenient s mods t = IfRequired mods (IfLenient s mods t)
142− ( BuildFrom (IfRequiredLenient T.Text mods paramType)
143− , ToReifiedEndpoint endpoint
113+ (Typeable (If (FoldRequired mods) paramType (Maybe paramType)), ToReifiedEndpoint endpoint, SBoolI (FoldRequired mods)) =>
145 ToReifiedEndpoint (QueryParam' mods name paramType :> endpoint)
114 ToReifiedEndpoint (QueryParam' mods name paramType :> endpoint)
147− type EndpointArgs (QueryParam' mods name paramType :> endpoint) = IfRequiredLenient T.Text mods paramType ': EndpointArgs endpoint
116+ toReifiedEndpoint endpoint _ =
148− type EndpointRes (QueryParam' mods name paramType :> endpoint) = EndpointRes endpoint
117+ toReifiedEndpoint endpoint (Proxy @endpoint)
149− reifiedEndpointArguments =
118+ & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldRequired mods) paramType (Maybe paramType))) : args, result, typeRepMap)
150− tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods paramType)))
151− V.:& reifiedEndpointArguments @endpoint
155− ( BuildFrom paramType
121+ (Typeable (If (FoldRequired mods) headerType (Maybe headerType)), ToReifiedEndpoint endpoint, SBoolI (FoldRequired mods)) =>
156− , ToReifiedEndpoint endpoint
160− ToReifiedEndpoint (QueryParams name paramType :> endpoint)
162− type EndpointArgs (QueryParams name paramType :> endpoint) = [paramType] ': EndpointArgs endpoint
163− type EndpointRes (QueryParams name paramType :> endpoint) = EndpointRes endpoint
164− reifiedEndpointArguments =
165− tagType (Argument (buildFrom @[paramType]))
166− V.:& reifiedEndpointArguments @endpoint
172− ( BuildFrom (IfRequiredLenient T.Text mods headerType)
173− , ToReifiedEndpoint endpoint
175 ToReifiedEndpoint (Header' mods headerName headerType :> endpoint)
122 ToReifiedEndpoint (Header' mods headerName headerType :> endpoint)
177− type EndpointArgs (Header' mods headerName headerType :> endpoint) = IfRequiredLenient T.Text mods headerType ': EndpointArgs endpoint
124+ toReifiedEndpoint endpoint _ =
178− type EndpointRes (Header' mods headerName headerType :> endpoint) = EndpointRes endpoint
125+ toReifiedEndpoint endpoint (Proxy @endpoint)
179− reifiedEndpointArguments =
126+ & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldRequired mods) headerType (Maybe headerType))) : args, result, typeRepMap)
180− tagType (Argument (buildFrom @(IfRequiredLenient T.Text mods headerType)))
181− V.:& reifiedEndpointArguments @endpoint
183−#if MIN_VERSION_servant(0,17,0)
185− ( BuildFrom (IfLenient String mods captureType)
186− , ToReifiedEndpoint endpoint) =>
129+ (Typeable captureType, ToReifiedEndpoint endpoint) =>
187 ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
130 ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
189− type EndpointArgs (Capture' mods name captureType :> endpoint) = IfLenient String mods captureType ': EndpointArgs endpoint
132+ toReifiedEndpoint endpoint _ =
190− type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
133+ toReifiedEndpoint endpoint (Proxy @endpoint)
191− reifiedEndpointArguments =
134+ & \(args, result, typeRepMap) -> (typeRep (Proxy @captureType) : args, result, typeRepMap)
192− tagType (Argument (buildFrom @(IfLenient String mods captureType)))
193− V.:& reifiedEndpointArguments @endpoint
196− ( BuildFrom captureType
197− , ToReifiedEndpoint endpoint) =>
198− ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
200− type EndpointArgs (Capture' mods name captureType :> endpoint) = captureType ': EndpointArgs endpoint
201− type EndpointRes (Capture' mods name captureType :> endpoint) = EndpointRes endpoint
202− reifiedEndpointArguments =
203− tagType (Argument (buildFrom @(captureType)))
204− V.:& reifiedEndpointArguments @endpoint
209− ( BuildFrom (IfLenient String mods requestType)
137+ (Typeable (If (FoldLenient mods) (Either String requestType) requestType), ToReifiedEndpoint endpoint, SBoolI (FoldLenient mods)) =>
210− , ToReifiedEndpoint endpoint) =>
211 ToReifiedEndpoint (ReqBody' mods contentTypes requestType :> endpoint)
138 ToReifiedEndpoint (ReqBody' mods contentTypes requestType :> endpoint)
213− type EndpointArgs (ReqBody' mods contentTypes requestType :> endpoint) = IfLenient String mods requestType ': EndpointArgs endpoint
140+ toReifiedEndpoint endpoint _ =
214− type EndpointRes (ReqBody' mods contentTypes requestType :> endpoint) = EndpointRes endpoint
141+ toReifiedEndpoint endpoint (Proxy @endpoint)
215− reifiedEndpointArguments =
142+ & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldLenient mods) (Either String requestType) requestType)) : args, result, typeRepMap)
216− tagType (Argument (buildFrom @(IfLenient String mods requestType)))
217− V.:& reifiedEndpointArguments @endpoint