angle-0.8.1.0: angle-commandline/Angle/Options.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-|
Module : Angle.Options
Description : Options for Angle executable.
Copyright : Copyright (C) 2015 Ben Moon
License : GNU GPL, version 3
Maintainer : GuiltyDolphin@gmail.com
Stability : alpha
Defines the 'Options' type for use in other modules that
provide for the angle command-line software.
-}
module Angle.Options
( Options(..)
, getOptions
, OptionEnv
, runWithOptions
) where
import Control.Applicative (Applicative)
import Control.Monad.Reader
import Data.List (foldl')
import Data.List.Split (splitOn)
import System.Console.GetOpt
import System.Environment (getArgs)
import System.Exit (exitSuccess)
import System.IO (hPutStrLn, stderr)
import Paths_angle (version)
import Data.Version (showVersion)
data Options = Options
{ optVerbose :: Bool
, optFiles :: [FilePath]
, optOutput :: Maybe FilePath
, optInteractive :: Bool
, optAbort :: Bool
, optCode :: [String]
, optNonOpts :: [String]
, optSearchPath :: [FilePath]
}
newtype OptionEnv a = OptionEnv
{ runOptionEnv :: ReaderT Options IO a
} deriving ( Functor, Applicative
, Monad, MonadIO, MonadReader Options)
runWithOptions :: Options -> OptionEnv a -> IO a
runWithOptions opts optEnv = runReaderT (runOptionEnv optEnv) opts
defaultOptions :: Options
defaultOptions = Options { optVerbose = False
, optFiles = []
, optOutput = Nothing
, optInteractive = False
, optAbort = False
, optCode = []
, optNonOpts = []
, optSearchPath = [ "~/.angle/lib"
, "~/.angle/usr/lib"
]
}
options :: [ OptDescr (Options -> IO Options) ]
options =
[ Option "f" ["file"]
(ReqArg
(\arg opt -> return opt { optFiles = optFiles opt ++ [arg] })
"FILE")
"Input file"
, Option "c" ["program"]
(ReqArg
(\arg opt -> return opt { optCode = optCode opt ++ [arg] })
"TEXT")
"Line of code to be executed directly (multiple -c's allowed)"
, Option "" ["path"]
(ReqArg
(\arg opt -> return opt { optSearchPath = splitOn ":" arg })
"PATH(S)")
("Paths to use instead of the defaults."
++ " These paths are searched when performing include")
, Option "i" ["interactive"]
(NoArg
(\opt -> return opt { optInteractive = True }))
"Run Angle in interactive (REPL) mode"
, Option "h" ["help"]
(NoArg
(\_ -> do
hPutStrLn stderr (usageInfo usage options)
exitSuccess))
"Display help"
, Option "V" ["version"]
(NoArg
(\_ -> do
hPutStrLn stderr $ "angle " ++ showVersion version
exitSuccess))
"Display current program version"
, Option "v" ["verbose"]
(NoArg
(\opt -> return opt { optVerbose = True }))
"Display additional program output"
, Option "" ["abort"]
(NoArg
(\opt -> return opt { optAbort = True }))
"Abort on first file that fails to run"
]
-- | Program usage header.
usage :: String
usage = "Usage: angle [OPTION...] [file]"
getOptions :: IO Options
getOptions = do
args <- getArgs
(opts, nonOpts) <-
case getOpt RequireOrder options args of
(opt,nonOpt,[]) -> return (opt,nonOpt)
(_,_,errs) -> ioError
(userError (concat errs ++ usageInfo usage options))
let (fileN, nOpts) = case nonOpts of
(x:xs) -> ([x], xs)
[] -> ([], [])
foldl' (>>=) (return defaultOptions { optFiles = fileN, optNonOpts = nOpts }) opts
-- foldl' (>>=) (return defaultOptions { optFiles = nonOpts }) opts