okapi-0.1.0.0: src/Okapi/Type.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StrictData #-}
module Okapi.Type where
import qualified Control.Applicative as Applicative
import qualified Control.Concurrent.Chan as Chan
-- import qualified Network.Wai.EventSource as EventSource
import qualified Control.Concurrent.STM.TVar as TVar
import qualified Control.Monad as Monad
import qualified Control.Monad.Except as Except
import qualified Control.Monad.IO.Class as IO
import qualified Control.Monad.Morph as Morph
import qualified Control.Monad.Reader.Class as Reader
import qualified Control.Monad.State.Class as State
import qualified Control.Monad.Trans.Except as ExceptT
import qualified Control.Monad.Trans.State.Strict as StateT
import qualified Data.ByteString.Lazy as LazyByteString
import qualified Data.Text as Text
import qualified Data.Vault.Lazy as Vault
import qualified GHC.Natural as Natural
import qualified Network.HTTP.Types as HTTP
import qualified Okapi.EventSource as EventSource
type Path = [Text.Text]
type Headers = [HTTP.Header]
type QueryItem = (Text.Text, Maybe Text.Text)
type Query = [QueryItem]
data State = State
{ stateRequest :: Request,
-- , stateEventSourcePoolTVar :: TVar.TVar EventSource.EventSourcePool
stateRequestMethodParsed :: Bool,
stateRequestBodyParsed :: Bool
-- , stateResulted :: Bool
}
data Request = Request
{ requestMethod :: HTTP.Method,
requestPath :: Path,
requestQuery :: Query,
requestBody :: IO LazyByteString.ByteString,
requestHeaders :: Headers,
requestVault :: Vault.Vault
}
data Result
= ResultResponse Response
| ResultFile File
| ResultEventSource EventSource.EventSource
-- ResultJob (IO ())
data File = File
{ fileStatus :: Natural.Natural
, fileHeaders :: Headers
, filePath :: FilePath
}
data Response = Response
{ responseStatus :: Natural.Natural,
responseHeaders :: Headers,
responseBody :: LazyByteString.ByteString
}
-- TODO: ADD Text field to skip fo
data Failure = Skip | Error Response
newtype OkapiT m a = OkapiT {unOkapiT :: ExceptT.ExceptT Failure (StateT.StateT State m) a}
deriving newtype
( Except.MonadError Failure,
State.MonadState State
)
instance Functor m => Functor (OkapiT m) where
fmap :: (a -> b) -> OkapiT m a -> OkapiT m b
fmap f okapiT =
OkapiT . ExceptT.ExceptT . StateT.StateT $
( fmap (\ ~(a, s') -> (f <$> a, s'))
. StateT.runStateT (ExceptT.runExceptT $ unOkapiT okapiT)
)
{-# INLINE fmap #-}
instance Monad m => Applicative (OkapiT m) where
pure x = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> pure (Right x, s)
{-# INLINEABLE pure #-}
(OkapiT (ExceptT.ExceptT (StateT.StateT mf))) <*> (OkapiT (ExceptT.ExceptT (StateT.StateT mx))) = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> do
~(eitherF, s') <- mf s
case eitherF of
Left error -> pure (Left error, s)
Right f -> do
~(eitherX, s'') <- mx s'
case eitherX of
Left error' -> pure (Left error', s')
Right x -> pure (Right $ f x, s'')
{-# INLINEABLE (<*>) #-}
m *> k = m >> k
{-# INLINE (*>) #-}
instance Monad m => Applicative.Alternative (OkapiT m) where
empty = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> pure (Left Skip, s)
{-# INLINE empty #-}
(OkapiT (ExceptT.ExceptT (StateT.StateT mx))) <|> (OkapiT (ExceptT.ExceptT (StateT.StateT my))) = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> do
(eitherX, stateX) <- mx s
case eitherX of
Left Skip -> do
(eitherY, stateY) <- my s
case eitherY of
Left Skip -> pure (Left Skip, s)
Left error@(Error _) -> pure (Left error, s)
Right y -> pure (Right y, stateY)
Left error@(Error _) -> pure (Left error, s)
Right x -> pure (Right x, stateX)
{-# INLINEABLE (<|>) #-}
instance Monad m => Monad (OkapiT m) where
return = pure
{-# INLINEABLE return #-}
(OkapiT (ExceptT.ExceptT (StateT.StateT mx))) >>= f = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> do
~(eitherX, s') <- mx s
case eitherX of
Left error -> pure (Left error, s)
Right x -> do
~(eitherResult, s'') <- StateT.runStateT (ExceptT.runExceptT $ unOkapiT $ f x) s'
case eitherResult of
Left error' -> pure (Left error', s')
Right res -> pure (Right res, s'')
{-# INLINEABLE (>>=) #-}
instance Monad m => Monad.MonadPlus (OkapiT m) where
mzero = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> pure (Left Skip, s)
{-# INLINE mzero #-}
(OkapiT (ExceptT.ExceptT (StateT.StateT mx))) `mplus` (OkapiT (ExceptT.ExceptT (StateT.StateT my))) = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> do
(eitherX, stateX) <- mx s
case eitherX of
Left Skip -> do
(eitherY, stateY) <- my s
case eitherY of
Left Skip -> pure (Left Skip, s)
Left error@(Error _) -> pure (Left error, s)
Right y -> pure (Right y, stateY)
Left error@(Error _) -> pure (Left error, s)
Right x -> pure (Right x, stateX)
{-# INLINEABLE mplus #-}
instance IO.MonadIO m => IO.MonadIO (OkapiT m) where
liftIO = Morph.lift . IO.liftIO
instance Reader.MonadReader r m => Reader.MonadReader r (OkapiT m) where
ask = Morph.lift Reader.ask
local = mapOkapiT . Reader.local
where
mapOkapiT :: (m (Either Failure a, State) -> n (Either Failure b, State)) -> OkapiT m a -> OkapiT n b
mapOkapiT f okapiT = OkapiT . ExceptT.ExceptT . StateT.StateT $ f . StateT.runStateT (ExceptT.runExceptT $ unOkapiT okapiT)
reader = Morph.lift . Reader.reader
-- instance State.MonadState s m => State.MonadState s (OkapiT m) where
-- get = Morph.lift State.get
-- put = Morph.lift . State.put
instance Morph.MonadTrans OkapiT where
lift :: Monad m => m a -> OkapiT m a
lift action = OkapiT . ExceptT.ExceptT . StateT.StateT $ \s -> do
result <- action
pure (Right result, s)
instance Morph.MFunctor OkapiT where
hoist :: Monad m => (forall a. m a -> n a) -> OkapiT m b -> OkapiT n b
hoist nat okapiT = OkapiT . ExceptT.ExceptT . StateT.StateT $ (nat . StateT.runStateT (ExceptT.runExceptT $ unOkapiT okapiT))
type MonadOkapi m =
( Functor m,
Applicative m,
Applicative.Alternative m,
Monad m,
Monad.MonadPlus m,
IO.MonadIO m,
Except.MonadError Failure m,
State.MonadState State m
)