packages feed

phoityne-0.0.2.0: app/Phoityne/IO/Main.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiWayIf          #-}

module Phoityne.IO.Main (run) where

-- モジュール
import Phoityne.Constant
import qualified Phoityne.Argument as A
import qualified Phoityne.IO.Control as CTRL

-- システム
import System.IO
import Data.Either.Utils
import Safe
import qualified Control.Exception as E
import qualified System.IO.Error as E
import qualified Data.ConfigFile as C
import qualified System.Console.CmdArgs as CMD
import qualified System.Log.Logger as L
import qualified System.Log.Formatter as L
import qualified System.Log.Handler as LH
import qualified System.Log.Handler.Simple as LHS

-- |
--  アプリケーションメイン
-- 
run :: IO Int
run = flip E.catches handlers $ do

  -- コマンドライン引数設定
  args <- getArgs

  -- INI設定ファイルのRead
  iniSet <- loadIniFile args

  -- Loggerのセットアップ
  setupLogger iniSet

  -- ロジック実行
  flip E.finally finalProc $ do
    CTRL.run args iniSet

  where
    handlers = [ E.Handler helpExcept
               , E.Handler ioExcept
               , E.Handler someExcept
               ]
    finalProc = L.removeAllHandlers 
    helpExcept (_ :: A.HelpExitException) = return 0
    ioExcept   (e :: E.IOException)       = print e >> return 1
    someExcept (e :: E.SomeException)     = print e >> return 1

-- |
--  引数データの取得
-- 
getArgs :: IO A.ArgData
getArgs = E.catches (CMD.cmdArgs A.config) handlers
  where
    handlers = [E.Handler someExcept]
    someExcept (e :: E.SomeException) = if
      | "ExitSuccess" == show e -> E.throw A.HelpExitException
      | otherwise -> E.throwIO e

-- |
--  INI設定ファイルデータの取得
-- 
loadIniFile :: A.ArgData          -- コマンドライン引数
            -> IO C.ConfigParser  -- INI設定
loadIniFile args = do
  cp <- if null (A.file args) then return $ forceEither $ C.readstring C.emptyCP defaultIniSetting
          else forceEither <$> C.readfile C.emptyCP (A.file args)

  return cp{ C.accessfunc = C.interpolatingAccess 5 }

-- |
-- デフォルトINI設定
-- 
defaultIniSetting :: String
defaultIniSetting = unlines [
    "[DEFAULT]"
  , "work_dir = ./"
  , ""
  , "[LOG]"
  , "file  = %(work_dir)sphoityne.log"
  , "level = INFO"
  , ""
  , "[PHOITYNE]"
  , "target = ."
  , "ghci_auto_start = True"
  ]

-- |
--  Loggerのセットアップ
-- 
setupLogger :: C.ConfigParser -- INI設定
            -> IO ()          -- なし
setupLogger cp = do
  let logFile  = forceEither $ C.get cp _INI_SEC_LOG _INI_LOG_FILE
      logLevel = forceEither $ C.get cp _INI_SEC_LOG _INI_LOG_LEVEL

  level <- case readMay logLevel of
    Just a  -> return a
    Nothing -> E.throwIO . E.userError $ "invalid log level[" ++ logLevel ++ "]"

  logStream <- openFile logFile AppendMode
  hSetEncoding logStream utf8

  logH <- LHS.streamHandler logStream level
  logSTD <- LHS.streamHandler stdout level
  
  let logHandle  = logH {LHS.closeFunc = hClose}
      logFormat  = L.tfLogFormatter _LOG_FORMAT_DATE _LOG_FORMAT
      logHandler = LH.setFormatter logHandle logFormat
      logHndlerSTD = LH.setFormatter logSTD logFormat

  L.updateGlobalLogger L.rootLoggerName $ L.setHandlers ([] :: [LHS.GenericHandler Handle])
  L.updateGlobalLogger _LOG_NAME $ L.setHandlers [logHandler, logHndlerSTD]
  L.updateGlobalLogger _LOG_NAME $ L.setLevel level