module Helper.Commands ( Command , CommandName , Help , CommandSpec (..) , module System.Console.GetOpt , defaultMain , usage ) where import Text.PrettyPrint(renderStyle,render,nest,vcat,hsep,style ,Mode(..),mode,text,(<>),($$),($+$),(<+>)) import System.Console.GetOpt import System.Environment (getArgs) import System.IO (stderr,hPutStr) import qualified Data.List as List type Command opts = (opts -> [String] -> IO ()) type CommandName = String type Help = String data CommandSpec opts = CommandSpec (Command opts) Help [OptDescr (opts -> opts)] [String] defaultMain :: opts -> [(String, CommandSpec opts)] -> String -> IO () defaultMain def commands header = do args <- getArgs let theUsage = usage commands header case args of [] -> theUsage [] command:opts -> case List.lookup command commands of Nothing -> theUsage ["Invalid command: " ++ command] Just spec -> runCommand theUsage def spec opts runCommand :: ([String] -> IO ()) -> opts -> CommandSpec opts -> [String] -> IO () runCommand theUsage def (CommandSpec command help optDesc argnames) args = case getOpt Permute optDesc args of (o,n,[] ) -> command (foldr ($) def o) n (_,_,errs) -> theUsage errs usage :: [(String, CommandSpec t)] -> String -> [String] -> IO () usage commands header errs = hPutStr stderr . render $ vcat (List.map text errs) $$ usageMsg commands header usageMsg commands header = text header $+$ (vcat (List.map commandUsage commands)) commandUsage (name , CommandSpec command help optionDesc args) = text name <> text ":" $$ (nest 10 (text help)) $$ (text name <+> text (if null optionDesc then "" else "[OPTION...]") <+> hsep (map text args)) <+> (nest 10 (text $ usageInfo "" optionDesc))