-- Copyright: 2009 Dino Morelli -- License: BSD3 (see LICENSE) -- Author: Dino Morelli {-# LANGUAGE FlexibleContexts #-} module Cargs.Opts ( parseOpts ) where import Control.Monad.Error import System.Console.GetOpt import System.Directory ( doesFileExist ) import Text.Printf ( printf ) import Cargs.Common ( Options (..) ) import Cargs.Conf ( confToCommand ) import Cargs.Log ( logMsg, setLoggingVerbose ) import Paths_cargs defaultOptions :: Options defaultOptions = Options { optHelp = False , optNoAction = False , optVerbose = False } options :: [OptDescr (Options -> Options)] options = [ Option ['h'] ["help"] (NoArg (\opts -> opts { optHelp = True } )) "This help text" , Option ['n'] ["no-action"] (NoArg (\opts -> opts { optNoAction = True } )) "Show command that would be executed, but do nothing" , Option ['v'] ["verbose"] (NoArg (\opts -> opts { optVerbose = True } )) "Log events with timestamp such as conf file load, command execution and completion." ] {- Parsing function to deal with both the args and config file loading and parsing. -} parseOpts :: (MonadIO m, MonadError String m) => [String] -> m (Options, String) parseOpts args = do examplePath <- liftIO $ getDataFileName "example" (opts, nonOpts) <- case getOpt Permute options args of (o, ns, []) -> return ((foldl (flip id) defaultOptions o), ns) (_,_,errs) -> throwError (concat errs ++ (usageText examplePath)) when ( optVerbose opts ) $ liftIO setLoggingVerbose when ( optHelp opts ) $ throwError $ usageText examplePath config <- case nonOpts of (p:_) -> do confExists <- liftIO $ doesFileExist p unless (confExists) $ throwError noConfErrMsg confContents <- liftIO $ readFile p let config = confToCommand confContents liftIO $ logMsg $ printf "Config file loaded: %s" p return config _ -> throwError noConfErrMsg return (opts, config) where noConfErrMsg = "No config file specified" usageText :: FilePath -> String usageText examplePath = (usageInfo header options) ++ "\n" ++ footer where header = init $ unlines [ "cargs - Config file to args" , "" , "Usage:" , " cargs [OPTIONS] [CONFPATH]" , "" , "CONFPATH Path to a config file" , "" , "Options:" ] footer = init $ unlines [ "Example config files can be found in " ++ examplePath , "" , "This program turns the contents of a config file into a shell command. And then executes it with timestamped log output." , "" , "Take care with wildly sending files to this thing. It sounds special when we say 'config file', but the reality is this program will de-comment, de-blankline and de-newline any file you give it and try to run the resulting string as a shell command. That's it, no magic." , "" , "The exit code will be that of the shell command that was executed. If this program fails before it gets that far (say, if you give it a bad option or no config path at all) it will exit with 255." , "" , "I have to admit that this software is of questionable utility. You can achieve something very similar by writing a shell script using backslash line continuation." , "" , "In its defense, my motivations were: Ability to mark-up the conf file with comments and whitespace. Handy no-action capability so you can confirm that your command line looks good. Timestamped log output of excution. And, it's just plain fun to write Haskell software." , "" , "Version 1.0.1 2009-May-09 Dino Morelli " ]