import Data.Monoid import System.Environment ( getArgs ) data Config = Config { printer :: First String , verbose :: NumFlag , options :: [String] } deriving (Eq, Show) data NumFlag = NumFlag Int deriving (Eq, Show) instance Monoid NumFlag where mempty = NumFlag 1 _ `mappend` (NumFlag 0) = NumFlag 0 (NumFlag 0) `mappend` _ = NumFlag 0 (NumFlag x) `mappend` (NumFlag y) = NumFlag $ max x y instance Monoid Config where mempty = defaultConfig c1 `mappend` c2 = Config (printer c1 `mappend` printer c2) (verbose c1 `mappend` verbose c2) (options c1 `mappend` options c2) defaultConfig :: Config defaultConfig = Config mempty mempty mempty -- | Table (associative list) containing all known command line arguments. -- | For all arguments, the list stores a function to apply to the full -- | argument string to process the right sight of '=' (if applicable). configTable :: [ (String, String -> Config) ] configTable = [ ("-v", \x -> defaultConfig { verbose = NumFlag . read . tail $ dropWhile (/= '=') x} ) , ("-q", \_ -> defaultConfig { verbose = NumFlag 0} ) , ("-printer", \x -> defaultConfig {printer = First . Just . tail $ dropWhile (/= '=') x} ) , ("-opt", \x -> defaultConfig { options = (:[]) . tail $ dropWhile (/= '=') x} ) ] -- | Process a single command line option. -- | Try to look it up in the 'configTable'. If found, the corresponding -- | function is applied, if not, default config is used. processArg :: String -> Config processArg arg = let opt = takeWhile (/= '=') arg in case lookup opt configTable of Nothing -> defaultConfig Just f -> f arg -- | This is entry point of our program. First, we get commandline arguments. -- | Then we process each of them separately with 'processArg'. -- | Last, we 'mconcat' them with our monoid instance to get merged flags. main :: IO () main = getArgs >>= print . mconcat . map processArg