{- |

Handling for the command-line options that can be used to configure
Dyre. As of the last count, there are four of them, and more are
unlikely to be needed. The only one that a user should ever need to
use is the @--force-reconf@ option, so the others all begin with
@--dyre-<option-name>@.

At the start of the program, before anything else occurs, the
'withDyreOptions' function is used to hide Dyre's command-line
options. They are loaded into the @IO@ monad using the module
"System.IO.Storage". This keeps them safely out of the way of
the user code and our own.

Later, when Dyre needs to access the options, it does so through
the accessor functions defined here. When it comes time to pass
control over to a new binary, it gets an argument list which
preserves the important flags with a call to 'customOptions'.

-}
module Config.Dyre.Options
  ( removeDyreOptions
  , withDyreOptions
  , customOptions
  , getDenyReconf
  , getForceReconf
  , getDebug
  , getMasterBinary
  , getStatePersist
  ) where

import Data.List                     (isPrefixOf)
import Data.Maybe                    (fromMaybe)
import System.IO.Storage             (withStore, putValue, getValue, getDefaultValue)
import System.Environment            (getArgs, getProgName, withArgs)
import System.Environment.Executable (getExecutablePath)

import Config.Dyre.Params

-- | Remove all Dyre's options from the given commandline arguments.
removeDyreOptions :: [String] -> [String]
removeDyreOptions :: [[Char]] -> [[Char]]
removeDyreOptions = forall a. (a -> Bool) -> [a] -> [a]
filter forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a}. Eq a => [[a]] -> [a] -> Bool
prefixElem [[Char]]
dyreArgs
  where prefixElem :: [[a]] -> [a] -> Bool
prefixElem [[a]]
xs = forall (t :: * -> *). Foldable t => t Bool -> Bool
or forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall a b. (a -> b) -> a -> b
($) (forall a b. (a -> b) -> [a] -> [b]
map forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf [[a]]
xs) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> [a]
repeat

-- | Store Dyre's command-line options to the IO-Store "dyre",
--   and then execute the provided IO action with all Dyre's
--   options removed from the command-line arguments.
withDyreOptions :: Params c r -> IO a -> IO a
withDyreOptions :: forall c r a. Params c r -> IO a -> IO a
withDyreOptions Params{configCheck :: forall cfgType a. Params cfgType a -> Bool
configCheck = Bool
check} IO a
action = forall a. [Char] -> IO a -> IO a
withStore [Char]
"dyre" forall a b. (a -> b) -> a -> b
$ do
    -- Pretty important
    [[Char]]
args <- IO [[Char]]
getArgs

    -- If the flag exists, it overrides the current file. Likewise,
    --   if it doesn't exist, we end up with the path to our current
    --   file. This seems like a sensible way to do it.
    -- Don't use 'getExecutablePath' if we're byassing the rest of Dyre.
    [Char]
this <- if Bool
check then IO [Char]
getExecutablePath else IO [Char]
getProgName
    forall a. Typeable a => [Char] -> [Char] -> a -> IO ()
putValue [Char]
"dyre" [Char]
"masterBinary" [Char]
this
    [[Char]] -> [Char] -> [Char] -> IO ()
storeFlag [[Char]]
args [Char]
"--dyre-master-binary=" [Char]
"masterBinary"

    -- Load the other important arguments into IO storage.
    [[Char]] -> [Char] -> [Char] -> IO ()
storeFlag [[Char]]
args [Char]
"--dyre-state-persist=" [Char]
"persistState"
    forall a. Typeable a => [Char] -> [Char] -> a -> IO ()
putValue [Char]
"dyre" [Char]
"forceReconf"  forall a b. (a -> b) -> a -> b
$ [Char]
"--force-reconf" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
args
    forall a. Typeable a => [Char] -> [Char] -> a -> IO ()
putValue [Char]
"dyre" [Char]
"denyReconf"   forall a b. (a -> b) -> a -> b
$ [Char]
"--deny-reconf"  forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
args
    forall a. Typeable a => [Char] -> [Char] -> a -> IO ()
putValue [Char]
"dyre" [Char]
"debugMode"    forall a b. (a -> b) -> a -> b
$ [Char]
"--dyre-debug"   forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
args

    -- We filter the arguments, so now Dyre's arguments 'vanish'
    forall a. [[Char]] -> IO a -> IO a
withArgs ([[Char]] -> [[Char]]
removeDyreOptions [[Char]]
args) IO a
action

-- | Get the value of the @--force-reconf@ flag, which is used
--   to force a recompile of the custom configuration.
getForceReconf :: IO Bool
getForceReconf :: IO Bool
getForceReconf = forall a. Typeable a => [Char] -> [Char] -> a -> IO a
getDefaultValue [Char]
"dyre" [Char]
"forceReconf" Bool
False

-- | Get the value of the @--deny-reconf@ flag, which disables
--   recompilation. This overrides "--force-reconf", too.
getDenyReconf :: IO Bool
getDenyReconf :: IO Bool
getDenyReconf = forall a. Typeable a => [Char] -> [Char] -> a -> IO a
getDefaultValue [Char]
"dyre" [Char]
"denyReconf" Bool
False

-- | Get the value of the @--dyre-debug@ flag, which is used
--   to debug a program without installation. Specifically,
--   it forces the application to use @./cache/@ as the cache
--   directory, and @./@ as the configuration directory.
getDebug  :: IO Bool
getDebug :: IO Bool
getDebug = forall a. Typeable a => [Char] -> [Char] -> a -> IO a
getDefaultValue [Char]
"dyre" [Char]
"debugMode" Bool
False

-- | Get the path to the master binary. This is set to the path of
--   the /current/ binary unless the @--dyre-master-binary=@ flag
--   is set. Obviously, we pass the @--dyre-master-binary=@ flag to
--   the custom configured application from the master binary.
getMasterBinary :: IO (Maybe String)
getMasterBinary :: IO (Maybe [Char])
getMasterBinary = forall a. Typeable a => [Char] -> [Char] -> IO (Maybe a)
getValue [Char]
"dyre" [Char]
"masterBinary"

-- | Get the path to a persistent state file. This is set only when
--   the @--dyre-state-persist=@ flag is passed to the program. It
--   is used internally by "Config.Dyre.Relaunch" to save and restore
--   state when relaunching the program.
getStatePersist :: IO (Maybe String)
getStatePersist :: IO (Maybe [Char])
getStatePersist = forall a. Typeable a => [Char] -> [Char] -> IO (Maybe a)
getValue [Char]
"dyre" [Char]
"persistState"

-- | Return the set of options which will be passed to another instance
--   of Dyre. Preserves the master binary, state file, and debug mode
--   flags, but doesn't pass along the forced-recompile flag. Can be
--   passed a set of other arguments to use, or it defaults to using
--   the current arguments when passed 'Nothing'.
customOptions :: Maybe [String] -> IO [String]
customOptions :: Maybe [[Char]] -> IO [[Char]]
customOptions Maybe [[Char]]
otherArgs = do
    Maybe [Char]
masterPath <- IO (Maybe [Char])
getMasterBinary
    Maybe [Char]
stateFile  <- IO (Maybe [Char])
getStatePersist
    Bool
debugMode  <- IO Bool
getDebug
    [[Char]]
mainArgs <- forall b a. b -> (a -> b) -> Maybe a -> b
maybe IO [[Char]]
getArgs forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe [[Char]]
otherArgs

    -- Combine the other arguments with the Dyre-specific ones
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ [[Char]]
mainArgs forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
        [ [[Char]
"--dyre-debug" | Bool
debugMode]
        , [[Char]
"--dyre-state-persist=" forall a. [a] -> [a] -> [a]
++ [Char]
sf | Just [Char]
sf <- [Maybe [Char]
stateFile]]
        , [ [Char]
"--dyre-master-binary="
            forall a. [a] -> [a] -> [a]
++ forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => [Char] -> a
error [Char]
"'dyre' data-store doesn't exist (in Config.Dyre.Options.customOptions)") Maybe [Char]
masterPath]
        ]

-- | Look for the given flag in the argument array, and store
--   its value under the given name if it exists.
storeFlag :: [String] -> String -> String -> IO ()
storeFlag :: [[Char]] -> [Char] -> [Char] -> IO ()
storeFlag [[Char]]
args [Char]
flag [Char]
name
    | forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
match  = forall (m :: * -> *) a. Monad m => a -> m a
return ()
    | Bool
otherwise   = forall a. Typeable a => [Char] -> [Char] -> a -> IO ()
putValue [Char]
"dyre" [Char]
name forall a b. (a -> b) -> a -> b
$ forall a. Int -> [a] -> [a]
drop (forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
flag) (forall a. [a] -> a
head [[Char]]
match)
  where match :: [[Char]]
match = forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf [Char]
flag) [[Char]]
args

-- | The array of all arguments that Dyre recognizes. Used to
--   make sure none of them are visible past 'withDyreOptions'
dyreArgs :: [String]
dyreArgs :: [[Char]]
dyreArgs = [ [Char]
"--force-reconf", [Char]
"--deny-reconf"
           , [Char]
"--dyre-state-persist", [Char]
"--dyre-debug"
           , [Char]
"--dyre-master-binary" ]