never executed always true always false
    1 module CommandLine.Program (ProgramResult(..), ProgramIO, run, failed, CommandLine.Program.error, showUsage, liftEither, liftM, liftME, mapError) where
    2 
    3 -- Common handling for command line programs
    4 
    5 import Prelude ()
    6 import Relude hiding (putStrLn, exitSuccess, exitFailure)
    7 
    8 import CommandLine.InfoFormatter (ToConsole(..))
    9 import CommandLine.World
   10 import qualified Data.Text as Text
   11 import System.Exit (ExitCode(..))
   12 
   13 import qualified Options.Applicative as OptParse
   14 
   15 
   16 class MapError f where
   17     mapError :: (x -> y) -> f x a -> f y a
   18 
   19 
   20 data ProgramResult err a
   21     = ShowUsage
   22     | ProgramError err
   23     | ProgramSuccess a
   24     | ProgramFailed
   25     deriving (Functor)
   26 
   27 instance MapError ProgramResult where
   28     mapError f = \case
   29         ShowUsage -> ShowUsage
   30         ProgramError x -> ProgramError $ f x
   31         ProgramSuccess a -> ProgramSuccess a
   32         ProgramFailed -> ProgramFailed
   33 
   34 instance Applicative (ProgramResult err) where
   35     pure a = ProgramSuccess a
   36     liftA2 f (ProgramSuccess a) (ProgramSuccess b) = ProgramSuccess (f a b)
   37     liftA2 _ (ProgramError err) _ = ProgramError err
   38     liftA2 _ ProgramFailed _ = ProgramFailed
   39     liftA2 _ ShowUsage _ = ShowUsage
   40     liftA2 _ _ (ProgramError err) = ProgramError err
   41     liftA2 _ _ ProgramFailed = ProgramFailed
   42     liftA2 _ _ ShowUsage = ShowUsage
   43 
   44 instance Monad (ProgramResult err) where
   45     ShowUsage >>= _ = ShowUsage
   46     ProgramFailed >>= _ = ProgramFailed
   47     (ProgramError err) >>= _ = ProgramError err
   48     (ProgramSuccess a) >>= f = f a
   49 
   50 
   51 newtype ProgramIO m x a =
   52     ProgramIO (m (ProgramResult x a))
   53     deriving (Functor)
   54 
   55 instance Functor m => MapError (ProgramIO m) where
   56     mapError f (ProgramIO m) = ProgramIO (fmap (mapError f) m)
   57 
   58 instance Monad m => Applicative (ProgramIO m x) where
   59     pure a = ProgramIO $ return $ pure a
   60     liftA2 f (ProgramIO a) (ProgramIO b) =
   61         ProgramIO $ do
   62             a' <- a
   63             b' <- b
   64             return $ liftA2 f a' b'
   65 
   66 instance Monad m => Monad (ProgramIO m x) where
   67     (ProgramIO m) >>= f = ProgramIO (m >>= f')
   68         where
   69             f' r =
   70                 case r of
   71                     ShowUsage -> return $ ShowUsage
   72                     ProgramError x -> return $ ProgramError x
   73                     ProgramFailed -> return $ ProgramFailed
   74                     ProgramSuccess a -> (\(ProgramIO z) -> z) $ f a
   75 
   76 failed :: Applicative m => ProgramIO m x a
   77 failed = ProgramIO $ pure $ ProgramFailed
   78 
   79 error :: Applicative m => x -> ProgramIO m x a
   80 error x = ProgramIO $ pure $ ProgramError x
   81 
   82 showUsage :: Applicative m => ProgramIO m x a
   83 showUsage = ProgramIO $ pure $ ShowUsage
   84 
   85 liftEither :: Applicative m => Either x a -> ProgramIO m x a
   86 liftEither (Left x) = ProgramIO $ pure $ ProgramError x
   87 liftEither (Right a) = ProgramIO $ pure $ ProgramSuccess a
   88 
   89 liftM :: Functor m => m a -> ProgramIO m x a
   90 liftM m = ProgramIO (ProgramSuccess <$> m)
   91 
   92 liftME :: Monad m => m (Either x a) -> ProgramIO m x a
   93 liftME m = ProgramIO (m >>= ((\(ProgramIO z) -> z) . liftEither))
   94 
   95 
   96 run ::
   97     World m =>
   98     ToConsole err =>
   99     OptParse.ParserInfo flags
  100     -> (flags -> ProgramIO m err ())
  101     -> [String]
  102     -> m ()
  103 run flagsParser run' args =
  104     let
  105         parsePreferences =
  106             OptParse.prefs (mempty <> OptParse.showHelpOnError)
  107 
  108         parseFlags =
  109             OptParse.execParserPure parsePreferences flagsParser
  110     in
  111     do
  112         flags <- handleParseResult $ parseFlags args
  113         case flags of
  114             Nothing -> return ()
  115             Just flags' ->
  116                 do
  117                     result <- (\(ProgramIO m) -> m) $ run' flags'
  118                     case result of
  119                         ShowUsage ->
  120                             (handleParseResult $ parseFlags ["--help"])
  121                                 -- TODO: handleParseResult is exitSuccess, so we never get to exitFailure
  122                                 >> exitFailure
  123 
  124                         ProgramError err ->
  125                             putStrLnStderr (toConsole err)
  126                                 >> exitFailure
  127 
  128                         ProgramSuccess () ->
  129                             exitSuccess
  130 
  131                         ProgramFailed ->
  132                             exitFailure
  133 
  134 
  135 {-| copied from Options.Applicative -}
  136 handleParseResult :: World m => OptParse.ParserResult a -> m (Maybe a)
  137 handleParseResult (OptParse.Success a) = return (Just a)
  138 handleParseResult (OptParse.Failure failure) = do
  139     progn <- getProgName
  140     let (msg, exit) = OptParse.renderFailure failure (Text.unpack progn)
  141     case exit of
  142         ExitSuccess -> putStrLn (Text.pack msg) *> exitSuccess *> return Nothing
  143         _           -> putStrLnStderr (Text.pack msg) *> exitFailure *> return Nothing
  144 handleParseResult (OptParse.CompletionInvoked _) =
  145     -- do
  146     --     progn <- getProgName
  147     --     msg <- OptParse.execCompletion compl progn
  148     --     putStr msg
  149     --     const undefined <$> exitSuccess
  150     Relude.error "Shell completion not yet implemented"