packages feed

mathblog-0.3: src/MB/Startup.hs

{-# LANGUAGE CPP #-}
module MB.Startup
    ( StartupConfig(..)
    , startupConfigFromEnv
#ifdef TESTING
    , startupConfig
    , baseDirEnvName
    , baseDirParamName
    , getDataDirFlag
#endif
    )
where

import Control.Monad
    ( mplus
    , when
    )
import System.Exit
    ( exitFailure
    )
import System.Environment
    ( getEnvironment
    , getArgs
    )
import System.Console.GetOpt

data StartupConfig = StartupConfig { listenMode :: Bool
                                   , dataDirectory :: FilePath
                                   , initDataDirectory :: Bool
                                   , forceRegeneration :: Bool
                                   }
                     deriving (Show, Eq)

data Flag = Listen
          | DataDir FilePath
          | InitDataDirectory
          | ForceRegenerate
            deriving (Eq)

getDataDirFlag :: [Flag] -> Maybe FilePath
getDataDirFlag [] = Nothing
getDataDirFlag (DataDir p:_) = Just p
getDataDirFlag (_:fs) = getDataDirFlag fs

options :: [OptDescr Flag]
options = [ Option ['d'] [baseDirParamName] (ReqArg DataDir "PATH")
                       $ "Path where blog files will be stored.  If this is\n" ++
                             "not specified, " ++ baseDirEnvName ++ " must be set in the\n" ++
                             "environment."

          , Option ['i'] ["init"] (NoArg InitDataDirectory)
                       $ "Initialize the blog data directory."
          , Option ['l'] ["listen"] (NoArg Listen)
                       $ "Make mb poll periodically and regenerate your\n" ++
                             "blog content when something changes.  This is\n" ++
                             "useful if you want to run a local web server\n" ++
                             "to view your posts as you're writing them."
          , Option ['f'] ["force"] (NoArg ForceRegenerate)
                       "Force a rebuild of all blog content."
          ]

-- |Inspect the program environment to create a startup configuration.
-- If the configuration information is invalid or absent, this will
-- automatically emit usage info and exit the program.
startupConfigFromEnv :: IO StartupConfig
startupConfigFromEnv = do
  env  <- getEnvironment
  args <- getArgs

  case startupConfig args env of
    Nothing -> usage >> exitFailure
    Just conf -> do
      when (head (dataDirectory conf) /= '/') $
           do
             putStrLn $ baseDirEnvName ++ " or " ++
                          "--" ++ baseDirParamName ++ "=<path>" ++
                                   " must specify an absolute path"
             exitFailure

      return conf

-- |Create a startup configuration from the specified arguments list
-- and environment variable list.  Returns Nothing if the required
-- information was absent.
startupConfig :: [String] -> [(String, String)] -> Maybe StartupConfig
startupConfig args env =
    case getOpt Permute options args of
      (_, _, (_:_)) -> Nothing
      (flags, _, []) -> do
        let lm = Listen `elem` flags
            i = InitDataDirectory `elem` flags
            f = ForceRegenerate `elem` flags
        d <- getDataDirFlag flags `mplus` (lookup baseDirEnvName env)

        return $ StartupConfig { listenMode = lm
                               , dataDirectory = d
                               , initDataDirectory = i
                               , forceRegeneration = f
                               }

baseDirEnvName :: String
baseDirEnvName = "MB_BASE_DIR"

baseDirParamName :: String
baseDirParamName = "baseDir"

usage :: IO ()
usage = do
  let h = concat [ "Usage: mb [args]\n\n"
                 , "mb is a tool for creating and managing a mathematically-inclined\n"
                 , "weblog.\n"
                 ]
  putStrLn $ usageInfo h options