module Main (main) where import Control.Monad import Control.Monad.Except import Control.Monad.Reader import Data.ByteString.Lazy qualified as BL import Data.List import Data.List.NonEmpty qualified as NE import Data.Maybe import Data.Proxy import Data.Text qualified as T import System.Console.ANSI import System.Console.GetOpt import System.Directory import System.Environment import System.Exit import System.FilePath import System.IO import Command import Command.Checkout import Command.JobId import Command.Log import Command.Run import Config import Output import Repo import Version data CmdlineOptions = CmdlineOptions { optShowHelp :: Bool , optShowVersion :: Bool , optCommon :: CommonOptions , optStorage :: Maybe FilePath , optOutput :: Maybe [ OutputType ] } defaultCmdlineOptions :: CmdlineOptions defaultCmdlineOptions = CmdlineOptions { optShowHelp = False , optShowVersion = False , optCommon = defaultCommonOptions , optStorage = Nothing , optOutput = Nothing } options :: [ OptDescr (CmdlineOptions -> Except String CmdlineOptions) ] options = [ Option [ 'h' ] [ "help" ] (NoArg $ \opts -> return opts { optShowHelp = True }) "show this help and exit" , Option [ 'V' ] [ "version" ] (NoArg $ \opts -> return opts { optShowVersion = True }) "show version and exit" , Option [ 'j' ] [ "jobs" ] (ReqArg (\num opts -> return opts { optCommon = (optCommon opts) { optJobs = read num }}) "") ("number of jobs to run simultaneously (default " <> show (optJobs defaultCommonOptions) <> ")") , Option [] [ "repo" ] (ReqArg (\value opts -> case span (/= ':') value of ( repo, ':' : path ) -> return opts { optCommon = (optCommon opts) { optRepo = DeclaredRepo (RepoName $ T.pack repo) path : optRepo (optCommon opts) } } _ -> throwError $ "--repo: invalid value `" <> value <> "'" ) ":") ("override or declare repo path") , Option [] [ "storage" ] (ReqArg (\value opts -> return opts { optStorage = Just value }) "") "set storage path" , Option [] [ "terminal-output" ] (NoArg $ \opts -> return opts { optOutput = Just $ TerminalOutput : fromMaybe [] (optOutput opts) }) "use terminal-style output (default if standard output is terminal)" , Option [] [ "log-output" ] (OptArg (\value opts -> return opts { optOutput = Just $ LogOutput (fromMaybe "-" value) : fromMaybe [] (optOutput opts) }) "") "use log-style output to or standard output" , Option [] [ "test-output" ] (OptArg (\value opts -> return opts { optOutput = Just $ TestOutput (fromMaybe "-" value) : fromMaybe [] (optOutput opts) }) "") "use test-style output to or standard output" ] data SomeCommandType = forall c. Command c => SC (Proxy c) commands :: NE.NonEmpty SomeCommandType commands = ( SC $ Proxy @RunCommand) NE.:| [ SC $ Proxy @CheckoutCommand , SC $ Proxy @JobIdCommand , SC $ Proxy @LogCommand ] lookupCommand :: String -> Maybe SomeCommandType lookupCommand name = find p commands where p (SC cmd) = commandName cmd == name main :: IO () main = do args <- getArgs let isPathArgument path = maybe False (/= '-') (listToMaybe path) && any isPathSeparator path let ( mbRootPath, args' ) = case args of (path : rest) | isPathArgument path -> ( Just path, rest ) _ -> ( Nothing, args ) (opts, cmdargs) <- case getOpt RequireOrder options args' of (os, cmdargs, []) -> do let merge :: ([String], CmdlineOptions) -> (CmdlineOptions -> Except String CmdlineOptions) -> ([String], CmdlineOptions) merge ( errs, o ) f = case runExcept $ f o of Left err -> ( err : errs, o ) Right o' -> ( errs, o' ) case foldl merge ( [], defaultCmdlineOptions ) os of ( [], opts ) -> return ( opts , cmdargs ) ( errs, _ ) -> do hPutStrLn stderr $ unlines (reverse errs) <> "Try `minici --help' for more information." exitFailure (_, _, errs) -> do hPutStrLn stderr $ concat errs <> "Try `minici --help' for more information." exitFailure when (optShowHelp opts) $ do let header = "Usage: minici [] [