module NoSlow.Util.Opts ( OptM, update, exit, error, parse, parseAll, printError, noArg, reqArg, readArg, helpArg, listArg, selArg, matchName, matchLib, matchGroup ) where import NoSlow.Util.Tag import System.Console.GetOpt import System.Exit import System.Environment ( getProgName ) import System.IO ( stderr, hPutStrLn ) import Data.List ( foldl' ) import Data.Monoid import Prelude hiding ( error, read ) data Result a = Ok a | Exit (IO ()) ExitCode | Error String instance Functor Result where fmap f r = r >>= return . f instance Monad Result where return = Ok Ok x >>= f = f x Exit p c >>= _ = Exit p c Error s >>= _ = Error s newtype OptM a = OptM (a -> Result a) instance Monoid (OptM a) where mempty = OptM Ok mappend (OptM f) o = OptM (apply o . f) result :: Result a -> OptM a result = OptM . const update :: (a -> a) -> OptM a update f = OptM $ Ok . f exit :: IO () -> ExitCode -> OptM a exit p = result . Exit p error :: String -> OptM a error = result . Error errorMsg :: String -> OptM a -> OptM a errorMsg msg (OptM f) = OptM $ \x -> case f x of Error _ -> Error msg r -> r check :: Bool -> OptM a -> OptM a check True o = o check False _ = error "invalid argument" read :: Read a => (a -> OptM b) -> String -> OptM b read f s = case reads s of [(x,"")] -> f x _ -> error "invalid argument" require :: (a -> Bool) -> (a -> OptM b) -> a -> OptM b require f g x = check (f x) (g x) apply :: OptM a -> Result a -> Result a apply (OptM f) r = r >>= f chain :: Result a -> (a -> OptM b) -> OptM b chain r o = OptM $ \x -> do { a <- r; run (o a) x } run :: OptM a -> a -> Result a run (OptM f) x = f x parse :: a -> [OptDescr (OptM a)] -> [String] -> IO (a, [String]) parse a options args = case getOpt Permute options args of (os, args', []) -> case run (mconcat os) a of Ok r -> return (r, args') Exit p c -> do { p; exitWith c } Error s -> printError s (_, _, (s : _)) -> printError s parseAll :: a -> [OptDescr (OptM a)] -> [String] -> IO a parseAll a options args = do (r, args') <- parse a options args case args' of [] -> return r s : _ -> printError $ "extra argument `" ++ s ++ "'" printError :: String -> IO a printError msg = do prog <- getProgName hPutStrLn stderr $ "Error: " ++ msg hPutStrLn stderr $ "Run \"" ++ prog ++ " --help\" for usage information" exitWith (ExitFailure 1) noArg :: (a -> a) -> ArgDescr (OptM a) noArg = NoArg . update reqArg :: (String -> a -> a) -> String -> ArgDescr (OptM a) reqArg f = ReqArg (update . f) readArg :: Read a => String -> (a -> Bool) -> (a -> b -> b) -> String -> ArgDescr (OptM b) readArg s f g = ReqArg (errorMsg msg . (read $ require f $ update . g)) where msg = "invalid " ++ s helpArg :: IO () -> ArgDescr (OptM a) helpArg p = NoArg (exit p ExitSuccess) listArg :: (String -> Result a) -> ([a] -> b -> b) -> String -> ArgDescr (OptM b) listArg f g = ReqArg $ \s -> chain (mapM f (split ',' s)) (update . g) selArg :: (String -> Result (Tag -> Bool)) -> ((Tag -> Bool) -> a -> a) -> String -> ArgDescr (OptM a) selArg f g = listArg f (g . foldr or (const False)) where or f g t = f t || g t matchName :: String -> Result (Tag -> Bool) matchName s = Ok $ \t -> tagName t == s matchLib :: String -> Result (Tag -> Bool) matchLib "" = Ok $ const False matchLib s = case split '/' s of [lib] -> Ok $ \t -> tagLibrary t == lib [lib,sub] -> Ok $ \t -> tagLibrary t == lib && tagSubsystem t == sub _ -> Error $ "Invalid library " ++ s matchGroup :: String -> Result (Tag -> Bool) matchGroup grp = Ok $ \t -> tagGroup t == grp