{-# LANGUAGE KindSignatures #-}

{-|

Module      : Network.N2O.Types

Description : Basic types

Copyright   : (c) Marat Khafizov, 2018

License     : BSD-3

Maintainer  : xafizoff@gmail.com

Stability   : experimental

Portability : not portable



Basic types



-}
module Network.N2O.Types where

import qualified Data.Binary as B
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import Data.IORef
import Data.Map.Strict (Map, (!?), insert)
import qualified Data.Text.Lazy as TL

-- | An HTTP header

type Header = (BS.ByteString, BS.ByteString)

-- | An HTTP request

data Req = Req
  { reqPath :: BS.ByteString
  , reqMeth :: BS.ByteString
  , reqVers :: BS.ByteString
  , reqHead :: [Header]
  }

-- | The N2O context data type

-- This is the key data type of the N2O. @(f :: * -> *)@ - type constructor

-- for the protocol handler's input type. @(a :: *)@ - base type for the

-- event handler's input type. I.e. @(f a)@ gives input type for the

-- protocol handler. @(Event a)@ gives input type for the event handler.

data Context (f :: * -> *) a = Context
  { cxHandler :: Event a -> N2O f a (Result a)
  , cxReq :: Req
  , cxMiddleware :: [Context f a -> Context f a]
  , cxProtos :: [Proto f a]
  , cxDePickle :: BL.ByteString -> Maybe a
  , cxPickle :: a -> BL.ByteString
  , cxState :: Map BS.ByteString BL.ByteString
  }

-- | Result of the message processing

data Result a
  = Reply a
  | Ok
  | Unknown
  | Empty
  deriving (Show, Eq)

-- | N2O protocol handler

newtype Proto f a = Proto
  { protoInfo :: f a -> N2O f a (Result (f a))
  }

-- | Event data type

data Event a
  = Init
  | Message a
  | Terminate

-- | Local mutable state

type State f a = IORef (Context f a)

-- | 'N2OT' over 'IO' with 'N2OState' as env

type N2O f a = N2OT (State f a) IO

-- | Reader monad transformer

newtype N2OT state m a = N2OT
  { runN2O :: state -> m a
  }

instance Functor m => Functor (N2OT state m) where
  fmap f (N2OT g) = N2OT (fmap f . g)

instance Applicative m => Applicative (N2OT state m) where
  pure = N2OT . const . pure
  (N2OT f) <*> (N2OT g) = N2OT $ \state -> f state <*> g state

instance Monad m => Monad (N2OT state m) where
  m >>= k =
    N2OT $ \state -> do
      a <- runN2O m state
      runN2O (k a) state