content: --------------------------------------------------------------------------------
  -- | Module providing the main hakyll function and command-line argument parsing
  module Hakyll.Main
      ( -- * Entry points
        hakyll
      , hakyllWith
      , hakyllWithArgs
      , hakyllWithExitCode
      , hakyllWithExitCodeAndArgs
  
        -- * Command line argument parsers
      , Options(..)
      , Command(..)
      , optionParser
      , commandParser
      , defaultCommands
      , defaultParser
      , defaultParserPure
      , defaultParserPrefs
      , defaultParserInfo
      ) where
  
  
  --------------------------------------------------------------------------------
  import           System.Environment        (getProgName)
  import           System.Exit               (ExitCode (ExitSuccess), exitWith)
  import           System.IO.Unsafe          (unsafePerformIO)
  
  
  --------------------------------------------------------------------------------
  import qualified Options.Applicative       as OA
  
  
  --------------------------------------------------------------------------------
  import qualified Hakyll.Check              as Check
  import qualified Hakyll.Commands           as Commands
  import qualified Hakyll.Core.Configuration as Config
  import qualified Hakyll.Core.Logger        as Logger
  import           Hakyll.Core.Rules
  import           Hakyll.Core.Runtime
  
  
  --------------------------------------------------------------------------------
  -- | This usually is the function with which the user runs the hakyll compiler
  hakyll :: Rules a -> IO ()
  hakyll = hakyllWith Config.defaultConfiguration
  
  --------------------------------------------------------------------------------
  -- | A variant of 'hakyll' which allows the user to specify a custom
  -- configuration
  hakyllWith :: Config.Configuration -> Rules a -> IO ()
  hakyllWith conf rules = hakyllWithExitCode conf rules >>= exitWith
  
  --------------------------------------------------------------------------------
  -- | A variant of 'hakyll' which returns an 'ExitCode'
  hakyllWithExitCode :: Config.Configuration -> Rules a -> IO ExitCode
  hakyllWithExitCode conf rules =  do
      args <- defaultParser conf
      hakyllWithExitCodeAndArgs conf args rules
  
  --------------------------------------------------------------------------------
  -- | A variant of 'hakyll' which expects a 'Configuration' and command-line
  -- 'Options'. This gives freedom to implement your own parsing.
  hakyllWithArgs :: Config.Configuration -> Options -> Rules a -> IO ()
  hakyllWithArgs conf args rules =
      hakyllWithExitCodeAndArgs conf args rules >>= exitWith
  
  --------------------------------------------------------------------------------
  hakyllWithExitCodeAndArgs :: Config.Configuration ->
                                Options -> Rules a -> IO ExitCode
  hakyllWithExitCodeAndArgs conf args rules = do
      let args' = optCommand args
          verbosity' = if verbosity args then Logger.Debug else Logger.Message
          check     =
              if internal_links args' then Check.InternalLinks else Check.All
  
      logger <- Logger.new verbosity'
      invokeCommands args' conf check logger rules
  
  --------------------------------------------------------------------------------
  defaultParser :: Config.Configuration -> IO Options
  defaultParser conf =
      OA.customExecParser defaultParserPrefs (defaultParserInfo conf)
  
  
  --------------------------------------------------------------------------------
  defaultParserPure :: Config.Configuration -> [String] -> OA.ParserResult Options
  defaultParserPure conf =
    OA.execParserPure defaultParserPrefs (defaultParserInfo conf)
  
  
  --------------------------------------------------------------------------------
  defaultParserPrefs :: OA.ParserPrefs
  defaultParserPrefs = OA.prefs OA.showHelpOnError
  
  --------------------------------------------------------------------------------
  defaultParserInfo :: Config.Configuration -> OA.ParserInfo Options
  defaultParserInfo conf =
    OA.info (OA.helper <*> optionParser conf) (OA.fullDesc <> OA.progDesc (
                                                  progName ++ " - Static site compiler created with Hakyll"))
  
  --------------------------------------------------------------------------------
  invokeCommands :: Command -> Config.Configuration ->
                    Check.Check -> Logger.Logger -> Rules a -> IO ExitCode
  invokeCommands args conf check logger rules =
      case args of
          Build   mode   -> Commands.build mode conf logger rules
          Check   _      -> Commands.check conf logger check
          Clean          -> Commands.clean conf logger >> ok
          Deploy         -> Commands.deploy conf
          Preview p      -> Commands.preview conf logger rules p >> ok
          Rebuild        -> Commands.rebuild conf logger rules
          Server  _ _    -> Commands.server conf logger (host args) (port args) >> ok
          Watch   _ p s  -> Commands.watch conf logger (host args) p (not s) rules >> ok
      where
          ok = return ExitSuccess
  
  
  --------------------------------------------------------------------------------
  
  -- | The parsed command-line options.
  data Options = Options {verbosity :: Bool, optCommand :: Command}
      deriving (Show)
  
  -- | The command to run.
  data Command
      = Build RunMode
      -- ^ Generate the site.
      | Check   {internal_links :: Bool}
      -- ^ Validate the site output.
      | Clean
      -- ^ Clean up and remove cache.
      | Deploy
      -- ^ Upload/deploy your site.
      | Preview {port :: Int}
      -- ^ [DEPRECATED] Please use the watch command.
      | Rebuild
      -- ^ Clean and build again.
      | Server  {host :: String, port :: Int}
      -- ^ Start a preview server.
      | Watch   {host :: String, port :: Int, no_server :: Bool }
      -- ^ Autocompile on changes and start a preview server.
      deriving (Show)
  
  {-# DEPRECATED Preview "Use Watch instead." #-}
  
  optionParser :: Config.Configuration -> OA.Parser Options
  optionParser conf = Options <$> verboseParser <*> commandParser conf
      where
      verboseParser = OA.switch (OA.long "verbose" <> OA.short 'v' <> OA.help "Run in verbose mode")
  
  
  commandParser :: Config.Configuration -> OA.Parser Command
  commandParser conf =  OA.subparser $ foldr ((<>) . produceCommand) mempty (defaultCommands conf)
      where
      produceCommand (c,a,b) = OA.command c (OA.info (OA.helper <*> a) (b))
  
  
  defaultCommands :: Config.Configuration -> [(String, OA.Parser Command, OA.InfoMod a)]
  defaultCommands conf =
      [ ( "build"
        , pure Build <*> OA.flag RunModeNormal RunModePrintOutOfDate (OA.long "dry-run" <> OA.help "Don't build, only print out-of-date items")
        , OA.fullDesc <> OA.progDesc "Generate the site"
        )
      , ( "check"
        , pure Check <*> OA.switch (OA.long "internal-links" <> OA.help "Check internal links only")
        , OA.fullDesc <> OA.progDesc "Validate the site output"
        )
      , ( "clean"
        , pure Clean
        , OA.fullDesc <> OA.progDesc "Clean up and remove cache"
        )
      , ( "deploy"
        , pure Deploy
        , OA.fullDesc <> OA.progDesc "Upload/deploy your site"
         )
      , ( "preview"
        , pure Preview <*> portParser
        , OA.fullDesc <> OA.progDesc "[DEPRECATED] Please use the watch command"
        )
      , ( "rebuild"
        , pure Rebuild
        , OA.fullDesc <> OA.progDesc "Clean and build again"
        )
      , ( "server"
        , pure Server <*> hostParser <*> portParser
        , OA.fullDesc <> OA.progDesc "Start a preview server"
        )
      , ( "watch"
        , pure Watch <*> hostParser <*> portParser <*> OA.switch (OA.long "no-server" <> OA.help "Disable the built-in web server")
        , OA.fullDesc <> OA.progDesc "Autocompile on changes and start a preview server.  You can watch and recompile without running a server with --no-server."
        )
      ]
      where
      portParser = OA.option OA.auto (OA.long "port" <> OA.help "Port to listen on" <> OA.value (Config.previewPort conf))
      hostParser = OA.strOption (OA.long "host" <> OA.help "Host to bind on" <> OA.value (Config.previewHost conf))
  
  
  --------------------------------------------------------------------------------
  -- | This is necessary because not everyone calls their program the same...
  progName :: String
  progName = unsafePerformIO getProgName
  {-# NOINLINE progName #-}
  