packages feed

epub-tools-4.0: src/app/EpubTools/EpubName/Main.hs

{-# LANGUAGE FlexibleContexts, TupleSections #-}

module EpubTools.EpubName.Main
  ( initialize
  )
  where

import Control.Applicative (asum)
import Control.Monad.Except (MonadError, throwError)
import Control.Monad.Trans (MonadIO, liftIO)
import Data.List.NonEmpty hiding (map)
import Data.Maybe (fromMaybe)
import System.Directory (doesFileExist)
import System.Environment (lookupEnv)
import System.Exit (ExitCode)
import System.FilePath ((</>))
import System.IO.Error (tryIOError)
import Text.Printf (PrintfArg, PrintfType, printf)

import EpubTools.EpubName.Common
  ( Options (rulesPaths, verbosityLevel)
  , RulesLocation (BuiltinRules, RulesPath, RulesViaEnv), RulesLocations (..)
  , VerbosityLevel (Normal)
  )
import qualified EpubTools.EpubName.Doc.Rules as Rules
import EpubTools.EpubName.Format.Compile (parseRules)
import EpubTools.EpubName.Format.Format (Formatter)
import EpubTools.EpubName.Util (exitInitFailure)


initialize :: (MonadError ExitCode m, MonadIO m) =>
   Options -> m [Formatter]
initialize opts = do
  erls <- liftIO . tryIOError . locateRules . rulesPaths $ opts
  rls <- case erls of
    Right rls' -> pure rls'
    Left err   -> do
      _ <- liftIO $ print err
      throwError exitInitFailure
  parseFormatters (verbosityLevel opts) rls


locateRules :: RulesLocations -> IO (String, String)
locateRules (RulesLocations rls) = do
  let loadActions = map loadRuleFile . toList $ rls
  mbLoadedFiles <- sequence loadActions
  pure $ fromMaybe undefined . asum $ mbLoadedFiles


loadRuleFile :: RulesLocation -> IO (Maybe (String, String))

loadRuleFile (RulesPath filePath) = do
  mbExistFile <- mbExists filePath
  case mbExistFile of
    Nothing -> pure Nothing
    (Just path) -> (Just . (filePath, )) <$> readFile path

loadRuleFile (RulesViaEnv varName pathSuffix) = do
  mbPathPrefix <- lookupEnv varName
  let mbFullPath = (</> pathSuffix) <$> mbPathPrefix
  maybe (pure Nothing) (loadRuleFile . RulesPath) mbFullPath

loadRuleFile BuiltinRules = pure $ Just ("built-in", Rules.defaults)


mbExists :: FilePath -> IO (Maybe FilePath)
mbExists p = do
   e <- doesFileExist p
   if e
      then return $ Just p
      else return Nothing


parseFormatters :: (MonadError ExitCode m, MonadIO m) =>
   VerbosityLevel -> (String, String) -> m [Formatter]
parseFormatters verbosity (name, contents) = case parseRules name contents of
   Left err  -> do
      _ <- liftIO $ printf "%s:\n%s" name (show err)
      throwError exitInitFailure
   Right fs  -> do
      liftIO $ showRulesSource verbosity name
      return fs


showRulesSource :: (Monad m, PrintfType (m ()), PrintfArg t) =>
   VerbosityLevel -> t -> m ()
showRulesSource verbosity name
  | verbosity > Normal = printf "Rules loaded from: %s\n" name
  | otherwise = return ()