{-# 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"