@@ -2,25 +2,188 @@
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DerivingStrategies #-}
3{-# LANGUAGE DerivingStrategies #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE PolyKinds #-}
6{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE StandaloneDeriving #-}
11+{-# LANGUAGE TupleSections #-}
12+{-# LANGUAGE TypeApplications #-}
7{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
10−module Roboservant.Types
17+module Roboservant.Types where
11− ( module Roboservant.Types.Breakdown,
18+
12− module Roboservant.Types.BuildFrom,
19+import Control.Monad.Except (runExceptT)
13− module Roboservant.Types.ReifiedApi,
20+import Data.Dynamic (Dynamic, toDyn)
14− module Roboservant.Types.ReifiedApi.Server,
21+import Data.Function ((&))
15− module Roboservant.Types.Internal,
22+import Data.IORef (IORef)
16− module Roboservant.Types.Config,
23+import Data.List.NonEmpty (NonEmpty)
17− )
24+import Data.Map.Strict (Map)
18−where
25+import Data.Typeable (TypeRep, Typeable, typeRep)
19−
26+import GHC.TypeLits (Symbol)
20−import Roboservant.Types.Breakdown
27+import Hedgehog
21−import Roboservant.Types.BuildFrom
28+import Servant
22−import Roboservant.Types.Config
29+import Servant.API.Modifiers
23−import Roboservant.Types.Internal
30+
24−import Roboservant.Types.ReifiedApi
31+data State v
25−import Roboservant.Types.ReifiedApi.Server
32+ = State
26−import Roboservant.Types.Orphans()
33+ { stateRefs :: Map TypeRep (NonEmpty (Var (Opaque (IORef Dynamic)) v))
36+class FlattenServer api where
37+ flattenServer :: Server api -> Bundled (Endpoints api)
40+ ( Endpoints (endpoint :<|> api) ~ (endpoint ': Endpoints api),
41+ Server (endpoint :<|> api) ~ (Server endpoint :<|> Server api),
44+ FlattenServer (endpoint :<|> api)
46+ flattenServer (endpoint :<|> server) = endpoint `AnEndpoint` flattenServer @api server
49+ ( HasServer (x :> api) '[],
50+ Endpoints (x :> api) ~ '[x :> api]
52+ FlattenServer (x :> api)
54+ flattenServer server = server `AnEndpoint` NoEndpoints
57+ ( HasServer (Verb method statusCode contentTypes responseType) '[],
58+ Endpoints (Verb method statusCode contentTypes responseType) ~ '[Verb method statusCode contentTypes responseType]
60+ FlattenServer (Verb method statusCode contentTypes responseType)
62+ flattenServer server = server `AnEndpoint` NoEndpoints
64+type ReifiedEndpoint = ([TypeRep], TypeRep, Dynamic)
66+type ReifiedApi = [(ApiOffset, [TypeRep], TypeRep, Dynamic)]
68+data Bundled endpoints where
69+ AnEndpoint :: Server endpoint -> Bundled endpoints -> Bundled (endpoint ': endpoints)
70+ NoEndpoints :: Bundled '[]
72+class ToReifiedApi (endpoints :: [*]) where
73+ toReifiedApi :: Bundled endpoints -> Proxy endpoints -> ReifiedApi
75+class ToReifiedEndpoint (endpoint :: *) where
76+ toReifiedEndpoint :: Dynamic -> Proxy endpoint -> ReifiedEndpoint
78+instance ToReifiedApi '[] where
79+ toReifiedApi NoEndpoints _ = []
82+ (Typeable (Normal (ServerT endpoint Handler)), NormalizeFunction (ServerT endpoint Handler), ToReifiedEndpoint endpoint, ToReifiedApi endpoints, Typeable (ServerT endpoint Handler)) =>
83+ ToReifiedApi (endpoint : endpoints)
85+ toReifiedApi (endpoint `AnEndpoint` endpoints) _ =
86+ withOffset (toReifiedEndpoint (toDyn (normalize endpoint)) (Proxy @endpoint))
88+ (\(n, x, y, z) -> (n + 1, x, y, z))
89+ (toReifiedApi endpoints (Proxy @endpoints))
91+ withOffset (x, y, z) = (0, x, y, z)
93+class NormalizeFunction m where
95+ normalize :: m -> Normal m
97+instance NormalizeFunction x => NormalizeFunction (r -> x) where
98+ type Normal (r -> x) = r -> Normal x
99+ normalize = fmap normalize
101+instance Typeable x => NormalizeFunction (Handler x) where
102+ type Normal (Handler x) = IO (Either ServerError (TypeRep, Dynamic))
103+ normalize handler = (runExceptT . runHandler') handler >>= \case
104+ Left serverError -> pure (Left serverError)
105+ Right x -> pure (Right (typeRep (Proxy @x), toDyn x))
108+ Typeable responseType =>
109+ ToReifiedEndpoint (Verb method statusCode contentTypes responseType)
111+ toReifiedEndpoint endpoint _ =
112+ ([], typeRep (Proxy @responseType), endpoint)
115+ (ToReifiedEndpoint endpoint) =>
116+ ToReifiedEndpoint ((x :: Symbol) :> endpoint)
118+ toReifiedEndpoint endpoint _ =
119+ toReifiedEndpoint endpoint (Proxy @endpoint)
122+ (ToReifiedEndpoint endpoint) =>
123+ ToReifiedEndpoint (Description s :> endpoint)
125+ toReifiedEndpoint endpoint _ =
126+ toReifiedEndpoint endpoint (Proxy @endpoint)
129+ (ToReifiedEndpoint endpoint) =>
130+ ToReifiedEndpoint (Summary s :> endpoint)
132+ toReifiedEndpoint endpoint _ =
133+ toReifiedEndpoint endpoint (Proxy @endpoint)
136+ (Typeable requestType, ToReifiedEndpoint endpoint) =>
137+ ToReifiedEndpoint (QueryFlag name :> endpoint)
139+ toReifiedEndpoint endpoint _ =
140+ toReifiedEndpoint endpoint (Proxy @endpoint)
141+ & \(args, result, typeRepMap) -> (typeRep (Proxy @Bool) : args, result, typeRepMap)
144+ (Typeable (If (FoldRequired mods) paramType (Maybe paramType)), ToReifiedEndpoint endpoint, SBoolI (FoldRequired mods)) =>
145+ ToReifiedEndpoint (QueryParam' mods name paramType :> endpoint)
147+ toReifiedEndpoint endpoint _ =
148+ toReifiedEndpoint endpoint (Proxy @endpoint)
149+ & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldRequired mods) paramType (Maybe paramType))) : args, result, typeRepMap)
152+ (Typeable (If (FoldRequired mods) headerType (Maybe headerType)), ToReifiedEndpoint endpoint, SBoolI (FoldRequired mods)) =>
153+ ToReifiedEndpoint (Header' mods headerName headerType :> endpoint)
155+ toReifiedEndpoint endpoint _ =
156+ toReifiedEndpoint endpoint (Proxy @endpoint)
157+ & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldRequired mods) headerType (Maybe headerType))) : args, result, typeRepMap)
160+ (Typeable captureType, ToReifiedEndpoint endpoint) =>
161+ ToReifiedEndpoint (Capture' mods name captureType :> endpoint)
163+ toReifiedEndpoint endpoint _ =
164+ toReifiedEndpoint endpoint (Proxy @endpoint)
165+ & \(args, result, typeRepMap) -> (typeRep (Proxy @captureType) : args, result, typeRepMap)
168+ (Typeable (If (FoldLenient mods) (Either String requestType) requestType), ToReifiedEndpoint endpoint, SBoolI (FoldLenient mods)) =>
169+ ToReifiedEndpoint (ReqBody' mods contentTypes requestType :> endpoint)
171+ toReifiedEndpoint endpoint _ =
172+ toReifiedEndpoint endpoint (Proxy @endpoint)
173+ & \(args, result, typeRepMap) -> (typeRep (Proxy @(If (FoldLenient mods) (Either String requestType) requestType)) : args, result, typeRepMap)
175+newtype ApiOffset = ApiOffset Int
177+ deriving newtype (Enum, Num)
179+-- | we need to specify an offset because it's entirely possible to have two
180+-- functions with the same arguments that do different things.
181+data Op (v :: * -> *) = Op ApiOffset [(TypeRep, Var (Opaque (IORef Dynamic)) v)]
182+ | Preload TypeRep (Var Dynamic v)
184+deriving instance Show (Op Symbolic)
186+instance HTraversable Op where
188+ Op offset args -> Op offset <$> traverse (\(t, v) -> (t,) <$> htraverse r v) args
189+ Preload tr v -> Preload tr <$> htraverse r v