{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving, DeriveFunctor #-} module Text.Transf.Process ( defaultMain, defaultMain', ) where import Control.Exception import Control.Applicative import Control.Monad (when) import Control.Monad.Error hiding (mapM) import Control.Monad.Plus hiding (mapM) import Data.Semigroup hiding (Option) import Data.List (find) import Data.Maybe (fromMaybe, maybeToList) import Data.Traversable (mapM) import Data.Typeable import System.IO import System.Exit import System.Environment import System.Console.GetOpt import Text.Transf import Prelude hiding (readFile, writeFile) -- | -- Creates a Unix style text processor from a 'Transform'. -- -- The resulting action should be used as the main of an application -- and will render a program of the given name that responds to @-v@ -- and @-h@ flags. If given no flags it runs the text transformer over -- the standard input and output streams. If an error occurs the program -- halts and prints an error message to the standard error stream. -- -- > defaultMain name transf -- defaultMain :: String -> Transform -> IO () defaultMain name transf = defaultMain' name [] (const transf) -- | -- Like 'defaultMain', but customizes the transform based on the -- given options. -- -- The @help@ and @version@ flags are added automatically. -- -- > defaultMain' name opts transf -- defaultMain' :: String -> [OptDescr a] -> ([a] -> Transform) -> IO () defaultMain' name optDesc transf = do let optDesc' = stdOptDesc ++ (fmap . mapOptDescr) User optDesc (opts, args, optErrs) <- getOpt Permute optDesc' <$> getArgs return () let usage = usageInfo (header name) optDesc' let printUsage = putStr (usage ++ "\n") >> exitSuccess let printVersion = putStr (version name ++ "\n") >> exitSuccess -- when (Help `elem` opts) printUsage when (Version `elem` opts) printVersion let opts' = fmap (\(User a) -> a) opts runFilter (transf opts') return () where version name = name ++ "-0.9" header name = "Usage: "++name++" [options]\n" ++ "Usage: "++name++" [options] files...\n" ++ "\n" ++ "Options:" runFilter :: Transform -> IO () runFilter transf = run transf stdin stdout -- stdOptDesc :: [UserOpt a] stdOptDesc = [ Option ['h'] ["help"] (NoArg Help) "Print help and exit", Option ['v'] ["version"] (NoArg Version) "Print version and exit" ] run :: Transform -> Handle -> Handle -> IO () run transf fin fout = do res <- runContext $ do input <- liftIO $ hGetContents fin output <- runTransform transf input liftIO $ hPutStr fout output case res of Left e -> hPutStrLn stderr ("Error: " ++ e) >> exitFailure Right _ -> exitSuccess mapOptDescr :: (a -> b) -> OptDescr a -> OptDescr b mapOptDescr = fmap -- TODO orphans deriving instance Functor OptDescr deriving instance Functor ArgDescr data UserOpt a = Help | Version | User a instance Eq (UserOpt a) where Help == Help = True Version == Version = True _ == _ = False