packages feed

kevin-0.11.0: src/Kevin/Base.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}

module Kevin.Base (
    module Kevin.Types,
    KevinException(..),
    _KevinException,
    KevinServer(..),
    User(..),

    module K,

    io,
    runPrinter,

    printf
) where

import           Control.Applicative              ((<$>))
import           Control.Concurrent as K          (forkIO)
import           Control.Concurrent.Chan as K
import           Control.Concurrent.STM.TVar as K
import           Control.Exception
import           Control.Exception as K           (IOException)
import           Control.Exception.Lens
import           Control.Lens as K
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707
import           Control.Monad.Catch as K
#else
import           Control.Monad.CatchIO as K
#endif
import           Control.Monad.Reader as K
import qualified Data.ByteString.Char8 as T       (hGetLine, hPutStr)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import           Data.Typeable
import           Kevin.Chatrooms as K
import           Kevin.Settings as K
import           Kevin.Types
import           Kevin.Util.Logger
import           Network as K
import           System.IO as K
import           System.IO.Error
import qualified Text.Printf as P

runPrinter :: Chan T.Text -> Handle -> IO ()
runPrinter ch h = void . forkIO . forever $ readChan ch >>= T.hPutStr h . T.encodeUtf8

io :: MonadIO m => IO a -> m a
io = liftIO

class KevinServer a where
    readClient, readServer   :: a -> IO T.Text
    writeClient, writeServer :: a -> T.Text -> IO ()
    closeClient, closeServer :: a -> IO ()

data KevinException = ServerParseFailure String T.Text
                    | ClientParseFailure String T.Text
                    | ServerClosed
                    | ClientClosed
                    deriving Typeable

instance Show KevinException where
    show (ServerParseFailure reason input) =
        P.printf "Parse failure (%s) on packet from server:\n%s" reason (show input)
    show (ClientParseFailure reason input) =
        P.printf "Parse failure (%s) on packet from client:\n%s" reason (show input)
    show ServerClosed = "Server closed connection"
    show ClientClosed = "Client closed connection"

instance Exception KevinException

_KevinException :: Prism' SomeException KevinException
_KevinException = exception

-- actions

hGetCharTimeout :: Handle -> Int -> IO Char
hGetCharTimeout h t = do
    hSetBuffering h NoBuffering
    ready <- hWaitForInput h t
    if ready
        then hGetChar h
        else throwIO $ mkIOError eofErrorType "read timeout" (Just h) Nothing

hGetSep :: Char -> Handle -> IO String
hGetSep sep h = fix $ \f -> do
                    ch <- hGetCharTimeout h 180000
                    if ch == sep
                        then return ""
                        else (ch:) <$> f

instance KevinServer Kevin where
    readClient k = do
        line <- T.decodeUtf8 <$> T.hGetLine (irc k)
        return $ T.init line
    readServer k = T.pack <$> hGetSep '\NUL' (damn k)

    writeClient k = writeChan (iChan k)
    writeServer k = writeChan (dChan k)

    closeClient = hClose . irc
    closeServer = hClose . damn