packages feed

hnormalise-0.4.7.0: src/HNormalise/Config.hs

{- hnormalise - a log normalisation library
 -
 - Copyright Andy Georges (c) 2017
 -
 - All rights reserved.
 -
 - Redistribution and use in source and binary forms, with or without
 - modification, are permitted provided that the following conditions are met:
 -
 - * Redistributions of source code must retain the above copyright
 - notice, this list of conditions and the following disclaimer.
 -
 - * Redistributions in binary form must reproduce the above
 - copyright notice, this list of conditions and the following
 - disclaimer in the documentation and/or other materials provided
 - with the distribution.
 -
 - * Neither the name of Author name here nor the names of other
 - contributors may be used to endorse or promote products derived
 - from this software without specific prior written permission.
 -
 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-}
{-# LANGUAGE DuplicateRecordFields      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE TemplateHaskell            #-}

module HNormalise.Config
    ( Config(..)
    , ConnectionType(..)
    , InputConfig(..)
    , LoggingConfig(..)
    , OutputConfig(..)
    , TcpOutputConfig(..)
    , TcpPortConfig(..)
    , ZeroMQOutputConfig(..)
    , ZeroMQPortConfig(..)
    , connectionType
    , defaultLoggingFrequency
    , loadConfig
    ) where

--------------------------------------------------------------------------------
import           Control.Monad    (mplus)
import           Data.Aeson       (defaultOptions)
import           Data.Aeson.TH    (deriveJSON)
import qualified Data.ByteString  as B
import           Data.Monoid      ((<>))
import           Data.Text        (Text)
import qualified Data.Yaml        as Y
import           System.Directory

import           Debug.Trace

--------------------------------------------------------------------------------
data ConnectionType = TCP
                    | ZeroMQ
                    deriving (Eq, Ord, Show)

connectionType :: Config -> ConnectionType
connectionType c =
    case input c >>= \(InputConfig t z) -> t of
        Just _ -> TCP
        _      -> ZeroMQ

--------------------------------------------------------------------------------
defaultLoggingFrequency = 100000

data LoggingConfig = LoggingConfig
    { frequency :: !(Maybe Int)    -- ^ How often should we write an update (# of messages processed)
    } deriving (Show)

--------------------------------------------------------------------------------
data TcpPortConfig = TcpPortConfig
    { host :: !(Maybe Text)
    , port :: !(Maybe Int)
    } deriving (Show)

--------------------------------------------------------------------------------
instance Monoid TcpPortConfig where
    mempty = TcpPortConfig Nothing Nothing
    mappend (TcpPortConfig hl pl) (TcpPortConfig hr pr) = TcpPortConfig
        { host = hl `mplus` hr
        , port = pl `mplus` pr
        }

--------------------------------------------------------------------------------
data TcpOutputConfig = TcpOutputConfig
    { success :: !(Maybe TcpPortConfig)
    , failure :: !(Maybe TcpPortConfig)
    } deriving Show

--------------------------------------------------------------------------------
instance Monoid TcpOutputConfig where
    mempty = TcpOutputConfig Nothing Nothing
    mappend (TcpOutputConfig sl fl) (TcpOutputConfig sr fr) = TcpOutputConfig
        { success = sl `mplus` sr
        , failure = fl `mplus` fr
        }

--------------------------------------------------------------------------------
data ZeroMQPortConfig = ZeroMQPortConfig
    { method :: !(Maybe Text)
    , host   :: !(Maybe Text)
    , port   :: !(Maybe Int)
    } deriving (Show)

--------------------------------------------------------------------------------
instance Monoid ZeroMQPortConfig where
    mempty = ZeroMQPortConfig Nothing Nothing Nothing
    mappend (ZeroMQPortConfig ml hl pl) (ZeroMQPortConfig mr hr pr) = ZeroMQPortConfig
        { method = ml `mplus` mr
        , host   = hl `mplus` hr
        , port   = pl `mplus` pr
        }

--------------------------------------------------------------------------------
data ZeroMQOutputConfig = ZeroMQOutputConfig
    { success :: !(Maybe ZeroMQPortConfig)
    , failure :: !(Maybe ZeroMQPortConfig)
    } deriving (Show)

--------------------------------------------------------------------------------
instance Monoid ZeroMQOutputConfig where
    mempty = ZeroMQOutputConfig Nothing Nothing
    mappend (ZeroMQOutputConfig sl fl) (ZeroMQOutputConfig sr fr) = ZeroMQOutputConfig
        { success = sl `mplus` sr
        , failure = fl `mplus` fr
        }

--------------------------------------------------------------------------------
data InputConfig = InputConfig
    { tcp    :: !(Maybe TcpPortConfig)
    , zeromq :: !(Maybe ZeroMQPortConfig)
    } deriving (Show)

--------------------------------------------------------------------------------
instance Monoid InputConfig where
    mempty = InputConfig Nothing Nothing
    mappend (InputConfig tl zl) (InputConfig tr zr) = InputConfig
        { tcp    = tl `mplus` tr
        , zeromq = zl `mplus` zr
        }

--------------------------------------------------------------------------------
data OutputConfig = OutputConfig
    { tcp    :: !(Maybe TcpOutputConfig)
    , zeromq :: !(Maybe ZeroMQOutputConfig)
    } deriving (Show)

--------------------------------------------------------------------------------
instance Monoid OutputConfig where
    mempty = OutputConfig Nothing Nothing
    mappend (OutputConfig tl zl) (OutputConfig tr zr) = OutputConfig
        { tcp    = tl `mplus` tr
        , zeromq = zl `mplus` zr
        }


--------------------------------------------------------------------------------
defaultInputTcpConfig = TcpPortConfig
    { port = Just 4019
    , host = Just "localhost"
    }

defaultOutputTcpConfig = TcpOutputConfig
    { success = Just TcpPortConfig { host = Just "localhost", port = Just 26001 }
    , failure = Just TcpPortConfig { host = Just "localhost", port = Just 26002 }
    }

defaultInputConfig = InputConfig
    { tcp = Just defaultInputTcpConfig
    , zeromq = Nothing
    }

defaultOutputConfig = OutputConfig
    { tcp = Just defaultOutputTcpConfig
    , zeromq = Nothing
    }

--------------------------------------------------------------------------------
data Config = Config
    { logging :: !(Maybe LoggingConfig)
    , input   :: !(Maybe InputConfig)
    , output  :: !(Maybe OutputConfig)
    , fields  :: !(Maybe [(Text, Text)])
    } deriving (Show)

--------------------------------------------------------------------------------
instance Monoid Config where
    mempty = Config Nothing Nothing Nothing Nothing
    mappend l r = Config
        { logging = logging l `mplus` logging r
        , input  = input l  `mplus` input r
        , output = output l `mplus` output r
        , fields = fields l `mplus` fields r
        }

defaultConfig = Config
    { logging = Just LoggingConfig { frequency = Just defaultLoggingFrequency }
    , input = Nothing
    , output = Nothing
    , fields = Nothing
    }

--------------------------------------------------------------------------------
systemConfigFileLocation :: FilePath
systemConfigFileLocation = "/etc/hnormalise.yaml"

--------------------------------------------------------------------------------
readConfig :: FilePath -> IO Config
readConfig fp = do
    exists <- doesFileExist fp
    if exists then do
        contents <- B.readFile fp
        trace "file read" $ return ()
        case Y.decodeEither contents of
            Left err     -> error $ "HNormalise.Config.readConfig: " ++ err
            Right config -> trace ("got " ++ show config) $ return (config :: Config)
    else
        return mempty


--------------------------------------------------------------------------------
loadConfig :: Maybe FilePath -> IO Config
loadConfig fp = do
    userConfig <- case fp of
        Just fp' -> readConfig fp'
        Nothing  -> return mempty
    systemConfig <- readConfig systemConfigFileLocation
    return $ userConfig <> systemConfig <> defaultConfig

--------------------------------------------------------------------------------
$(deriveJSON defaultOptions ''LoggingConfig)
$(deriveJSON defaultOptions ''TcpPortConfig)
$(deriveJSON defaultOptions ''TcpOutputConfig)
$(deriveJSON defaultOptions ''ZeroMQPortConfig)
$(deriveJSON defaultOptions ''ZeroMQOutputConfig)
$(deriveJSON defaultOptions ''InputConfig)
$(deriveJSON defaultOptions ''OutputConfig)
$(deriveJSON defaultOptions ''Config)