packages feed

bein-0.3.1: Bein/Types.hs

{-# LANGUAGE DeriveDataTypeable, MultiParamTypeClasses, FlexibleInstances #-}
module Bein.Types where

import Data.List ( intercalate )
import System.Posix.Types ( UserID )
import Control.Concurrent.STM ( TVar, atomically, readTVar )
import Control.Monad ()
import Control.Monad.Reader ( ReaderT(..), ask )
import Control.Monad.Trans ( liftIO )
import Control.Exception ( Exception )
import Data.Typeable ( Typeable )
import Database.HDBC ( fromSql, toSql, SqlValue(SqlString) )
import Database.HDBC.PostgreSQL
import Data.Convertible ( Convertible(..), convError )

newtype ExecutionID = ExecutionID Int deriving (Eq,Show,Read)

type BeinM a b = ReaderT a IO b

class BeinState a where
  configT :: a -> TVar Configuration
  db :: a -> Connection

configField :: BeinState s => (Configuration -> a) -> ReaderT s IO a
configField f = ask >>= liftIO.atomically.readTVar.configT >>= return.f

data Configuration = Configuration {
  file_repository :: String,
  scratch_directory :: String,
  static_content_directory :: FilePath,
  perl_executable :: String,
  r_executable :: String,
  max_executions :: Int,
  minion_command :: String, -- "/path/to/minion -d %s -e %d" where %s will be the directory
                            -- to work in, and %d the id of the execution to run.
  daemon_port :: String, -- By default /tmp/.s.BEIND
  minion_port :: String, -- By default .s.minion in the execution directory
  http_port :: Int, -- default 8082
  http_base_url :: String,
  http_base_path :: String,
  template_path :: String,
  authentication :: Authentication
} deriving (Eq,Show,Read)


instance Convertible SqlValue Authentication where
  safeConvert x = case readsPrec 1 (fromSql x) of
    [] -> convError "Invalid authentication type" x
    [(v,"")] -> return v
    _  -> convError "Read multiple authentication types in string.  Make up your mind!" x
  
instance Convertible Authentication SqlValue where
  safeConvert = return . toSql . show

instance Convertible SqlValue ExecutionID where
  safeConvert = return . ExecutionID . fromSql
  
instance Convertible ExecutionID SqlValue where
  safeConvert (ExecutionID x) = return $ toSql x

unExecutionID :: ExecutionID -> Int
unExecutionID (ExecutionID x) = x

instance Convertible [ExecutionID] SqlValue where
  safeConvert xs = let xs' = map (show.unExecutionID) xs
                   in return $ SqlString $ "{" ++ intercalate "," xs' ++ "}"

data ConfigurationError = InvalidAuthentication String deriving (Eq,Show,Read,Typeable)

instance Exception ConfigurationError 

data Authentication = None | SameUser | OnlyUser UserID deriving (Eq,Show,Read,Typeable)

data AuthenticationFailed = AuthenticationFailed deriving (Show,Typeable)
instance Exception AuthenticationFailed

data BeinError = NoDatabaseServer String
               | DatabaseAccessError String
               | MissingConfigurationField String
               | NoScratchDirectory String
               | CannotWriteToScratchDirectory String
               | NoFileDirectory String
               | CannotWriteToFileDirectory String
               | MissingFile Int String String -- ID, user_filename, stored_as
               | MinionExecutionFailed String
               | UnknownExecution ExecutionID
               | NoSuchJob ExecutionID
               | FailedToKillJob ExecutionID
               | UnknownCommand String
               deriving (Eq,Show,Read)
                        
errorNumber :: BeinError -> Int
errorNumber (NoDatabaseServer _) = 10
errorNumber (DatabaseAccessError _) = 15
errorNumber (MissingConfigurationField _) = 17
errorNumber (NoScratchDirectory _) = 20
errorNumber (CannotWriteToScratchDirectory _) = 25
errorNumber (NoFileDirectory _) = 30
errorNumber (CannotWriteToFileDirectory _) = 35
errorNumber (MissingFile _ _ _) = 36
errorNumber (MinionExecutionFailed _) = 100
errorNumber (UnknownExecution _) = 110
errorNumber (NoSuchJob _) = 120
errorNumber (FailedToKillJob _) = 150
errorNumber (UnknownCommand _) = 200

errorName :: BeinError -> String
errorName (NoDatabaseServer _) = "no database server"
errorName (DatabaseAccessError _) = "database access error"
errorName (MissingConfigurationField _) = "missing configuration field"
errorName (NoScratchDirectory _) = "no scratch directory"
errorName (CannotWriteToScratchDirectory _) = "cannot write to scratch directory"
errorName (NoFileDirectory _) = "no file directory"
errorName (CannotWriteToFileDirectory _) = "cannot write to file directory"
errorName (MissingFile _ _ _) = "missing file"
errorName (MinionExecutionFailed _) = "minion execution failed"
errorName (UnknownExecution _) = "unknown execution"
errorName (NoSuchJob _) = "no such job"
errorName (FailedToKillJob _) = "failed to kill job"
errorName (UnknownCommand _) = "unknown command"