packages feed

heyefi-2.0.0.0: src/HEyefi/Config.hs

module HEyefi.Config where

import           HEyefi.Log (logInfo)
import           HEyefi.Prelude
import           HEyefi.Types (
    Config(..)
  , CardConfig
  , SharedConfig
  , LogLevel(Info)
  , cardMap
  , uploadDirectory
  , logLevel
  , HEyefiM(..))

import           Control.Concurrent.STM (
    TVar
  , readTVar
  , newTVar
  , writeTVar
  , atomically
  , retry)
import           Control.Monad.Catch (
    finally
  , catches
  , Handler (..)
  , SomeException (..))
import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.State.Lazy (modify, get, runStateT)
import           Data.Configurator (load, Worth (Required), getMap)
import           Data.Configurator.Types (Value, ConfigError (ParseError))
import qualified Data.Configurator.Types as CT
import           Data.HashMap.Strict ()
import qualified Data.HashMap.Strict as HM
import           Data.Maybe (mapMaybe)
import           Data.Text (Text)
import qualified Data.Text as T

insertCard :: Text -> Text -> Config -> Config
insertCard macAddress uploadKey c =
  Config {
    cardMap = HM.insert macAddress uploadKey (cardMap c),
    uploadDirectory = uploadDirectory c,
    logLevel = logLevel c,
    lastSNonce = lastSNonce c
    }


waitForWake :: TVar (Maybe Int) -> HEyefiM ()
waitForWake wakeSig = liftIO (atomically (
  do state <- readTVar wakeSig
     case state of
      Just _ -> writeTVar wakeSig Nothing
      Nothing -> retry))

convertCardList :: Value -> Either Text [(Text, Text)]
convertCardList (CT.List innerList) =
  Right (mapMaybe extractTuple innerList)
  where
    extractTuple :: Value -> Maybe (Text, Text)
    extractTuple (CT.List [CT.String macAddress, CT.String key]) =
      Just (macAddress, key)
    extractTuple _ = Nothing
convertCardList _ = Left cardsFormatDoesNotMatch

getCardConfig :: HM.HashMap CT.Name CT.Value -> HEyefiM CardConfig
getCardConfig configMap = do
  let cards = HM.lookup "cards" configMap
  case cards of
   Nothing -> do
     logInfo missingCardsDefinition
     return HM.empty
   Just l ->
     case convertCardList l of
      Left msg -> do
        logInfo msg
        return HM.empty
      Right cardList ->
        return (HM.fromList cardList)

convertUploadDirectory :: Value -> Either Text FilePath
convertUploadDirectory (CT.String uploadDir) =
  Right (T.unpack uploadDir)
convertUploadDirectory _ =
  Left uploadDirFormatDoesNotMatch

getUploadDirectory :: HM.HashMap CT.Name CT.Value -> HEyefiM FilePath
getUploadDirectory configMap = do
  let uploadDir = HM.lookup "upload_dir" configMap
  case uploadDir of
   Nothing -> do
     logInfo missingUploadDirDefinition
     return ""
   Just uD ->
     case convertUploadDirectory uD of
      Left msg -> do
        logInfo msg
        return ""
      Right path ->
        return path

reloadConfig :: FilePath -> HEyefiM Config
reloadConfig configPath = do
  logInfo (tryingToLoadConfiguration (T.pack configPath))
  catches (
    do
      config <- liftIO (load [Required configPath])
      configMap <- liftIO (getMap config)
      cardConfig <- getCardConfig configMap
      uploadDir <- getUploadDirectory configMap
      logInfo loadedConfiguration
      return Config {
        cardMap = cardConfig,
        uploadDirectory = uploadDir,
        logLevel = Info,
        lastSNonce = "" } -- TODO: Careful, we might be erasing something here.
    )
    [Handler (\(ParseError p msg) -> do
                 logInfo (errorParsingConfigurationFile (T.pack p) (T.pack msg))
                 return emptyConfig),
     Handler (\(SomeException _) -> do
                 logInfo (couldNotFindConfigurationFile (T.pack configPath))
                 return emptyConfig)]

runWithConfig :: Config -> HEyefiM a -> IO (a,Config)
runWithConfig c m = runStateT (runHeyefi m) c

runWithEmptyConfig :: HEyefiM a -> IO (a,Config)
runWithEmptyConfig = runWithConfig emptyConfig

emptyConfig :: Config
emptyConfig = Config { cardMap = HM.empty
                     , uploadDirectory = ""
                     , logLevel = Info
                     , lastSNonce = ""}

newConfig :: IO SharedConfig
newConfig = atomically (newTVar emptyConfig)

-- Example config:
-- cards = [["0012342de4ce","e7403a0123402ca062"],["1234562d5678","12342a062"]]
-- upload_dir = "/data/annex/doxie/unsorted"
monitorConfig :: FilePath -> SharedConfig -> TVar (Maybe Int) -> HEyefiM ()
monitorConfig configPath sharedConfig wakeSignal =
  finally
    (do
        config <- reloadConfig configPath
        liftIO (atomically (writeTVar sharedConfig config)))
    (waitForWake wakeSignal)

getUploadKeyForMacaddress :: Text -> HEyefiM (Maybe Text)
getUploadKeyForMacaddress mac = do
  c <- get
  return (HM.lookup mac (cardMap c))

putSNonce :: Text -> HEyefiM ()
putSNonce snonce =
  modify (\ s ->
            s { lastSNonce = snonce })