{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Boots.CLI(
CLI(..)
, interruptCli
, runCLI
) where
import Boots.Prelude
import Control.Exception (Exception, catch)
import Control.Monad.Catch (MonadThrow (..))
import Data.List (intercalate)
import Data.Text (Text)
import Data.Version (Version, showVersion)
import Data.Void
import Options.Applicative
import Salak
import qualified Text.Megaparsec as M
import qualified Text.Megaparsec.Char as M
#if __GLASGOW_HASKELL__ < 804
import Data.Semigroup
#endif
data CLI = CLI
{ cliV :: Bool
, cOpt :: [(Text, Text)]
}
data CliInterrupt = CliInterrupt deriving Show
instance Exception CliInterrupt
interruptCli :: MonadThrow m => m a
interruptCli = throwM CliInterrupt
cli :: Parser CLI
cli = CLI
<$> switch (long "version" <> short 'V' <> help "Print version information")
<*> many (argument (eitherReader go) (metavar "KEY=VAL..."))
where
go = mapLeft M.errorBundlePretty . M.parse kv "" . fromString
kv :: P (Text, Text)
kv = do
k <- key
_ <- M.char '='
v <- val
return (fromString $ intercalate "." k, fromString v)
key = ((:) <$> M.lowerChar <*> M.many (M.choice [ M.lowerChar, M.digitChar, M.char '-'])) `M.sepBy` M.char '.'
val = M.some M.printChar
type P = M.Parsec Void Text
runCLI :: Version -> (ParseCommandLine -> IO ()) -> IO ()
runCLI v f = (execParser go >>= g2) `catch` ge
where
go = info (cli <**> helper) fullDesc
ge CliInterrupt = return ()
g2 CLI{..} = if cliV
then putStrLn $ showVersion v
else f $ \_ -> return cOpt