{- Copyright 2015-2022 NGLess Authors - License: MIT -} module CmdArgs ( ColorSetting(..) , Verbosity(..) , NThreadsOpts(..) , NGLessInput(..) , NGLessArgs(..) , NGLessMode(..) , nglessArgs ) where {-| This is a separate module so that Main & Configuration - can share these objects. Putting them in Configuration would, however, - pollute it as it is imported from everywhere. -} import Options.Applicative import Text.Read (readMaybe) import qualified Data.Configurator.Types as CF import qualified Data.Text as T data Verbosity = Quiet | Normal | Loud deriving (Show, Eq, Ord, Enum) data ColorSetting = AutoColor | NoColor | ForceColor deriving (Eq, Show) instance CF.Configured ColorSetting where convert (CF.String "auto") = Just AutoColor convert (CF.String "force") = Just ForceColor convert (CF.String "none") = Just NoColor convert _ = Nothing data NThreadsOpts = NThreads Int | NThreadsAuto deriving (Eq, Show) instance CF.Configured NThreadsOpts where convert (CF.String "auto") = Just NThreadsAuto convert (CF.String val) = NThreads <$> (readMaybe $ T.unpack val) convert _ = Nothing data NGLessInput = InlineScript String | ScriptFilePath FilePath deriving (Eq, Show) data NGLessArgs = NGLessArgs { mode :: NGLessMode , verbosity :: Verbosity , quiet :: Bool , color :: Maybe ColorSetting , trace_flag :: Maybe Bool } deriving (Eq, Show) data NGLessMode = DefaultMode { input :: NGLessInput , debug_mode :: String , validateOnly :: Bool , print_last :: Bool , nThreads :: NThreadsOpts , strictThreads :: Maybe Bool , createReportDirectory :: Maybe Bool , html_report_directory :: Maybe FilePath , temporary_directory :: Maybe FilePath , keep_temporary_files :: Maybe Bool , config_files :: [FilePath] , no_header :: Bool , subsampleMode :: Bool , experimentalFeatures :: Bool , exportJSON :: Maybe FilePath , exportCWL :: Maybe FilePath , deprecationCheck :: Bool , searchPath :: [FilePath] , indexPath :: Maybe FilePath , extraArgs :: [String] } | InstallReferenceMode { refname :: T.Text } | CreateReferencePackMode { oname :: FilePath , genome_url :: String , gtf_url :: Maybe String , functional_map_url :: Maybe String } | DownloadFileMode { origUrl :: String , localFile :: FilePath } | DownloadDemoMode { demoName :: String } | PrintPathMode { pathDesired :: String } | CheckInstallMode { checkInstallVerbose :: Bool } deriving (Eq, Show) parseVerbosity = option (eitherReader readVerbosity) (long "verbosity" <> short 'v' <> value Normal) where readVerbosity :: String -> Either String Verbosity readVerbosity "quiet" = Right Quiet readVerbosity "normal" = Right Normal readVerbosity "full" = Right Loud readVerbosity other = Left ("Cannot parse '" ++ other ++ "' as a verbosity") parseColor = optional $ option (eitherReader readColor) (long "color" <> help colorHelp) where readColor "auto" = Right AutoColor readColor "no" = Right NoColor readColor "yes" = Right ForceColor readColor "force" = Right ForceColor readColor _ = Left "Could not parse color option (valid options are 'auto', 'force', and 'no')" colorHelp = "Color settings, one of 'auto' (color if writing to a terminal, this is the default), 'force' (always color), 'no' (no color)." -- The input can be either inline (using -e ... or --script "...") or given as -- a filepath parseInput :: Parser NGLessInput parseInput = InlineScript <$> strOption (long "script" <> short 'e' <> help "inline script to execute") <|> ScriptFilePath <$> strArgument (metavar "INPUT" <> help "Filename of script to interpret") -- A integer literal or the string "auto" parseNThreads = option (eitherReader readNThreads) (long "jobs" <> short 'j' <> long "threads" <> value (NThreads 1) <> help "Nr of threads to use") where readNThreads "auto" = Right NThreadsAuto readNThreads val = case readMaybe val of Just n -> Right (NThreads n) Nothing -> Left ("Failed to parse "++val++" as a threads option") -- An option with 3 states -- "" -> Nothing -- "--option" -> Just True -- "--no-option" -> Just False -- -- This builds on the `switch` function because it distinguishes between not -- passing in the option. triSwitch :: String -> String -> Parser (Maybe Bool) triSwitch name helpmsg = optional (flag' True (long name <> help helpmsg) <|> flag' False (long ("no-"++name) <> help ("opposite of --"++name))) mainArgs = DefaultMode <$> parseInput -- input :: NGLessInput <*> strOption (long "debug" <> value "") -- debug_mode :: String <*> switch (long "validate-only" <> short 'n' <> help "Only validate input, do not run script") -- validateOnly :: Bool <*> switch (long "print-last" <> short 'p' <> help "print value of last line in script") -- print_last :: Bool <*> parseNThreads <*> triSwitch "strict-threads" "strictly respect the --threads option (by default, NGLess will, occasionally, use more threads than specified)" -- scrict-threads :: Bool <*> triSwitch "create-report" "create the report directory" -- createReportDirectory :: Bool <*> optional (strOption $ long "html-report-directory" <> short 'o' <> help "name of output directory") -- html_report_directory :: Maybe FilePath <*> optional (strOption $ long "temporary-directory" <> short 't' <> help "Directory where to store temporary files") -- temporary_directory :: Maybe FilePath <*> triSwitch "keep-temporary-files" "Whether to keep temporary files (default is delete them)" -- keep_temporary_files :: Maybe Bool <*> many (strOption $ long "config-file" <> help "Configuration files to parse") -- config_files :: Maybe [FilePath] <*> switch (long "no-header" <> help "Do not print copyright information") -- no_header :: Bool <*> switch (long "subsample" <> help "Subsample mode: quickly test a pipeline by discarding 99% of the input")-- subsampleMode :: Bool <*> switch (long "experimental-features" <> help "Whether to allow the use of experimental features") -- experimentalFeatures :: Bool <*> optional (strOption $ long "export-json" <> help "File to write JSON representation of script to") -- exportJSON :: Maybe FilePath <*> optional (strOption $ long "export-cwl" <> help "File to write CWL wrapper of given script") -- exportCWL :: Maybe FilePath <*> switch (long "check-deprecation" <> help "Check if ngless version or any used modules have been deprecated")-- deprecationCheck :: Bool <*> many ( (strOption $ long "search-dir" <> help "Deprecated. Use --search-path instead") -- searchDir :: [FilePath] <|> (strOption $ long "search-path" <> help "Reference search directories (replace in script)") -- searchPath :: [FilePath] ) <*> optional (strOption $ long "index-path" <> help "Index path (directory where indices are stored)") -- indexPath :: Maybe FilePath <*> many (strArgument (metavar "ARGV")) -- extraArgs :: [String] installArgs = (flag' InstallReferenceMode (long "install-reference-data")) <*> (T.pack <$> strArgument (help "Name of reference to install" <> metavar "REF")) -- += details [ "Example:" , "(sudo) ngless --install-reference-data sacCer3" ] createRefArgs = flag' CreateReferencePackMode (long "create-reference-pack" <> internal) <*> strOption (long "output-name" <> internal) <*> strOption (long "genome-url" <> internal) <*> optional (strOption $ long "gtf-url" <> internal) <*> optional (strOption $ long "functional-map-url" <> internal) -- += details ["Example:", "ngless --create-reference-pack ref.tar.gz -g http://...genome.fa.gz -a http://...gtf.fa.gz"] downloadFileArgs = flag' DownloadFileMode (long "download-file") <*> strOption (long "download-url") <*> strOption (long "local-file") downloadDemoArgs = flag' DownloadDemoMode (long "download-demo") <*> strArgument (metavar "DEMO-NAME") printPathArgs = flag' PrintPathMode (long "print-path") <*> strArgument (metavar "EXEC") checkInstallArgs = flag' CheckInstallMode (long "check-install" <> help "Check if ngless is correctly installed") <*> switch (long "verbose" <> short 'v' <> help "Print paths") nglessArgs :: Parser NGLessArgs nglessArgs = NGLessArgs <$> (mainArgs <|> downloadFileArgs <|> downloadDemoArgs <|> installArgs <|> createRefArgs <|> printPathArgs <|> checkInstallArgs) <*> parseVerbosity <*> switch (long "quiet" <> short 'q') <*> parseColor <*> triSwitch "trace" "Set highest verbosity mode" -- trace_flag :: Maybe Bool