module Control.Monad.Apiary.Internal
( ApiaryT
, runApiaryTWith
, runApiaryWith
, runApiary
, getApiaryTWith
, getApiaryWith
, getApiary
, ApiaryConfig(..)
, action
, middleware
, group
, document
, precondition
, noDoc
, apiaryConfig
, apiaryExt
, Filter
, Filter'
, focus
) where
import qualified Network.Wai as Wai
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid(Monoid(..))
import Control.Applicative(Applicative(..), (<$>))
#endif
import Control.Monad(liftM)
import Control.Monad.Trans(MonadIO(liftIO), MonadTrans(lift))
import Control.Monad.Trans.Control
( MonadTransControl(..), MonadBaseControl(..)
, ComposeSt, defaultLiftBaseWith, defaultRestoreM
)
import Control.Monad.Base(MonadBase(..))
import Control.Monad.Apiary.Action.Internal
(ActionT, ApiaryConfig, getRequest
, Extensions(NoExtension)
, execActionT, hoistActionT, applyDict, rootPattern
)
import qualified Data.Apiary.Routing as R
import Data.Apiary.Method(Method, renderMethod)
import Data.Apiary.Extension ( Has, MonadExts(..), getExt, noExtension )
import Data.Apiary.Extension.Internal(Initializer(..), allMiddleware, allMiddleware')
import Data.Apiary.Document.Internal(Doc(..), docsToDocuments)
import Text.Blaze.Html(Html)
import qualified Data.Text as T
type Filter exts actM m inp out =
ApiaryT exts out actM m () -> ApiaryT exts inp actM m ()
type Filter' exts actM m = forall prms. Filter exts actM m prms prms
type ActionT' exts actM = ActionT exts '[] actM
data ApiaryEnv exts prms actM = ApiaryEnv
{ envMethod :: Maybe Method
, envPath :: R.Path prms (ActionT' exts actM) () -> R.Path '[] (ActionT' exts actM) ()
, envConfig :: ApiaryConfig
, envDoc :: Doc -> Doc
, envExts :: Extensions exts
}
initialEnv :: Monad actM => ApiaryConfig -> Extensions exts -> ApiaryEnv exts '[] actM
initialEnv conf = ApiaryEnv Nothing id conf id
data ApiaryWriter exts actM = ApiaryWriter
{ writerRouter :: R.Router '[] (ActionT' exts actM) () -> R.Router '[] (ActionT' exts actM) ()
, writerDoc :: [Doc] -> [Doc]
, writerMw :: Wai.Middleware
}
instance Monoid (ApiaryWriter exts actM) where
mempty = ApiaryWriter id id id
ApiaryWriter ra da am `mappend` ApiaryWriter rb db bm
= ApiaryWriter (ra . rb) (da . db) (am . bm)
newtype ApiaryT exts prms actM m a = ApiaryT { unApiaryT :: forall b.
ApiaryEnv exts prms actM
-> (a -> ApiaryWriter exts actM -> m b)
-> m b
}
apiaryT :: Monad m
=> (ApiaryEnv exts prms actM -> m (a, ApiaryWriter exts actM))
-> ApiaryT exts prms actM m a
apiaryT f = ApiaryT $ \rdr cont -> f rdr >>= \(a, w) -> cont a w
routerToAction :: Monad actM => R.Router '[] (ActionT' exts actM) () -> ActionT' exts actM ()
routerToAction router = do
req <- getRequest
R.execute router (Wai.requestMethod req) (Wai.pathInfo req)
runApiaryTWith :: (Monad actM, Monad m)
=> (forall b. actM b -> IO b)
-> (Wai.Application -> m a)
-> Initializer m '[] exts
-> ApiaryConfig
-> ApiaryT exts '[] actM m ()
-> m a
runApiaryTWith runAct run (Initializer ir) conf m = ir NoExtension $ \exts -> do
wtr <- unApiaryT m (initialEnv conf exts) (\_ w -> return w)
let doc = docsToDocuments $ writerDoc wtr []
rtr = writerRouter wtr R.empty
mw = allMiddleware exts . writerMw wtr
mw' = allMiddleware' exts
app = mw $ execActionT conf exts doc (mw' $ hoistActionT runAct $ routerToAction rtr)
run $! app
runApiaryWith :: Monad m
=> (Wai.Application -> m a)
-> Initializer m '[] exts
-> ApiaryConfig
-> ApiaryT exts '[] IO m ()
-> m a
runApiaryWith = runApiaryTWith id
runApiary :: Monad m
=> (Wai.Application -> m a)
-> ApiaryConfig
-> ApiaryT '[] '[] IO m ()
-> m a
runApiary run = runApiaryWith run noExtension
getApiaryTWith :: (Monad actM, Monad m)
=> (forall b. actM b -> IO b)
-> Initializer m '[] exts
-> ApiaryConfig
-> ApiaryT exts '[] actM m ()
-> m Wai.Application
getApiaryTWith runAct (Initializer ir) conf m = ir NoExtension $ \exts -> do
wtr <- unApiaryT m (initialEnv conf exts) (\_ w -> return w)
let doc = docsToDocuments $ writerDoc wtr []
rtr = writerRouter wtr R.empty
mw = allMiddleware exts . writerMw wtr
mw' = allMiddleware' exts
app = mw $ execActionT conf exts doc (mw' $ hoistActionT runAct $ routerToAction rtr)
return $! app
getApiaryWith :: Monad m
=> Initializer m '[] exts
-> ApiaryConfig
-> ApiaryT exts '[] IO m ()
-> m Wai.Application
getApiaryWith = getApiaryTWith id
getApiary :: Monad m
=> ApiaryConfig
-> ApiaryT '[] '[] IO m ()
-> m Wai.Application
getApiary = getApiaryWith noExtension
instance Functor (ApiaryT exts prms actM m) where
fmap f m = ApiaryT $ \env cont ->
unApiaryT m env $ \a hdr -> hdr `seq` cont (f a) hdr
instance Monad actM => Applicative (ApiaryT exts prms actM m) where
pure x = ApiaryT $ \_ cont -> cont x mempty
mf <*> ma = ApiaryT $ \env cont ->
unApiaryT mf env $ \f hdr ->
unApiaryT ma env $ \a hdr' ->
let hdr'' = hdr `mappend` hdr'
in hdr'' `seq` cont (f a) hdr''
instance Monad actM => Monad (ApiaryT exts prms actM m) where
return x = ApiaryT $ \_ cont -> cont x mempty
m >>= k = ApiaryT $ \env cont ->
unApiaryT m env $ \a hdr ->
unApiaryT (k a) env $ \b hdr' ->
let hdr'' = hdr `mappend` hdr'
in hdr'' `seq` cont b hdr''
instance Monad actM => MonadTrans (ApiaryT exts prms actM) where
lift m = ApiaryT $ \_ c -> m >>= \a -> c a mempty
instance (Monad actM, MonadIO m) => MonadIO (ApiaryT exts prms actM m) where
liftIO m = ApiaryT $ \_ c -> liftIO m >>= \a -> c a mempty
instance (Monad actM, MonadBase b m) => MonadBase b (ApiaryT exts prms actM m) where
liftBase m = ApiaryT $ \_ c -> liftBase m >>= \a -> c a mempty
instance Monad actM => MonadTransControl (ApiaryT exts prms actM) where
#if MIN_VERSION_monad_control(1,0,0)
type StT (ApiaryT exts prms actM) a = (a, ApiaryWriter exts actM)
liftWith f = apiaryT $ \env ->
liftM (\a -> (a, mempty :: ApiaryWriter exts actM))
(f $ \t -> unApiaryT t env (\a w -> return (a,w)))
restoreT = apiaryT . const
#else
newtype StT (ApiaryT exts prms actM) a = StTApiary { unStTApiary :: (a, ApiaryWriter exts actM) }
liftWith f = apiaryT $ \env ->
liftM (\a -> (a, mempty))
(f $ \t -> liftM StTApiary $ unApiaryT t env (\a w -> return (a,w)))
restoreT m = apiaryT $ \_ -> liftM unStTApiary m
#endif
instance (Monad actM, MonadBaseControl b m) => MonadBaseControl b (ApiaryT exts prms actM m) where
#if MIN_VERSION_monad_control(1,0,0)
type StM (ApiaryT exts prms actM m) a = ComposeSt (ApiaryT exts prms actM) m a
liftBaseWith = defaultLiftBaseWith
restoreM = defaultRestoreM
#else
newtype StM (ApiaryT exts prms actM m) a = StMApiary { unStMApiary :: ComposeSt (ApiaryT exts prms actM) m a }
liftBaseWith = defaultLiftBaseWith StMApiary
restoreM = defaultRestoreM unStMApiary
#endif
instance Monad actM => MonadExts exts (ApiaryT exts prms actM m) where
getExts = envExts <$> getApiaryEnv
getApiaryEnv :: Monad actM => ApiaryT exts prms actM m (ApiaryEnv exts prms actM)
getApiaryEnv = ApiaryT $ \env cont -> cont env mempty
apiaryExt :: (Has e exts, Monad actM) => proxy e -> ApiaryT exts prms actM m e
apiaryExt = getExt
apiaryConfig :: Monad actM => ApiaryT exts prms actM m ApiaryConfig
apiaryConfig = liftM envConfig getApiaryEnv
addRoute :: Monad actM => ApiaryWriter exts actM -> ApiaryT exts prms actM m ()
addRoute r = ApiaryT $ \_ cont -> cont () r
focus :: Monad actM
=> (Doc -> Doc)
-> Maybe Method
-> (R.Path prms' (ActionT exts '[] actM) () -> R.Path prms (ActionT exts '[] actM) ())
-> Filter exts actM m prms prms'
focus d meth pth m = ApiaryT $ \ApiaryEnv{..} cont -> unApiaryT m ApiaryEnv
{ envMethod = maybe envMethod Just meth
, envPath = envPath . pth
, envDoc = envDoc . d
, envConfig = envConfig
, envExts = envExts
} cont
action :: Monad actM => ActionT exts prms actM () -> ApiaryT exts prms actM m ()
action a = do
env <- getApiaryEnv
let meth = renderMethod <$> envMethod env
path = envPath env (R.action meth $ flip applyDict a)
addRoute $ ApiaryWriter
(R.insert path)
(envDoc env Action:)
id
middleware :: Monad actM => Wai.Middleware -> ApiaryT exts prms actM m ()
middleware mw = addRoute (ApiaryWriter id id mw)
insDoc :: (Doc -> Doc) -> Filter' exts actM m
insDoc d m = ApiaryT $ \env cont -> unApiaryT m env
{ envDoc = envDoc env . d } cont
group :: T.Text -> Filter' exts actM m
group = insDoc . DocGroup
document :: T.Text -> Filter' exts actM m
document = insDoc . Document
precondition :: Html -> Filter' exts actM m
precondition = insDoc . DocPrecondition
noDoc :: Filter' exts actM m
noDoc = insDoc DocDropNext