packages feed

endo-0.1.0.0: example/Example7.hs

{-# LANGUAGE NoImplicitPrelude #-}
-- |
-- Module:       $HEADER$
-- Description:  Example 7
-- Copyright:    (c) 2015 Peter Trsko
-- License:      BSD3
--
-- Maintainer:   peter.trsko@gmail.com
-- Stability:    experimental
-- Portability:  NoImplicitPrelude
--
-- Example 2.
module Example7
  where

import Prelude (Bounded(maxBound, minBound), Enum(fromEnum))

import Control.Applicative (Applicative((<*>)), (<$>))
import Control.Monad (Monad((>>=)))
import Data.Char (toLower)
import qualified Data.Data as Data (Data(toConstr), showConstr)
import Data.Either (Either(Left, Right))
import Data.Function ((.), ($), id)
import Data.List ((++))
import Data.List (concat, intersperse, lookup, map, null, unwords)
import Data.Maybe (Maybe(Just, Nothing))
import Data.Monoid (Endo, (<>))
import Data.Tuple (fst)
import System.IO (IO, print)
import Text.Show (Show(show))

import Control.Monad.Trans.Identity (IdentityT(IdentityT, runIdentityT))

import Control.Lens ((.~), set)
import Options.Applicative
    ( Parser
    , eitherReader
    , execParser
    , flag
    , fullDesc
    , help
    , info
    , long
    , metavar
    , option
    , optional
    , short
    )

import Data.Monoid.Endo (E, runEndo)
import Data.Monoid.Endo.Fold (foldEndo)

import Example.Config (Config(Config), Verbosity(..))
import Example.Config.Lens (outputFile, verbosity)


options :: Parser Config
options = runIdentityT $ runEndo defaultConfig <$> options'
  where
    options' :: IdentityT Parser (Endo Config)
    options' = foldEndo
        <*> outputOption     -- :: IdentityT Parser (Maybe (E Config))
        <*> verbosityOption  -- :: IdentityT Parser (Maybe (E Config))
        <*> annoyingFlag     -- :: IdentityT Parser (E Config)
        <*> silentFlag       -- :: IdentityT Parser (E Config)
        <*> verboseFlag      -- :: IdentityT Parser (E Config)

    defaultConfig :: Config
    defaultConfig = Config Normal ""

-- >>> :main -o an.out.put --annoying
-- Config {_verbosity = Annoying, _outputFile = "an.out.put"}
main :: IO ()
main = execParser (info options fullDesc) >>= print

outputOption :: IdentityT Parser (Maybe (E Config))
outputOption =
    IdentityT . optional . option (set outputFile <$> parseFilePath)
    $ short 'o' <> long "output" <> metavar "FILE"
        <> help "Store output in to a FILE."
  where
    parseFilePath = eitherReader $ \s ->
        if null s
            then Left "Option argument can not be empty file path."
            else Right s

verbosityOption :: IdentityT Parser (Maybe (E Config))
verbosityOption =
    IdentityT . optional . option (set verbosity <$> parseVerbosity)
    $ long "verbosity" <> metavar "LEVEL" <> help "Set verbosity to LEVEL."
  where
    verbosityToStr = map toLower . Data.showConstr . Data.toConstr
    verbosityIntValues = [(show $ fromEnum v, v) | v <- [Silent .. Annoying]]
    verbosityStrValues =
        ("default", Normal) : [(verbosityToStr v, v) | v <- [Silent .. Annoying]]

    parseVerbosityError = unwords
        [ "Verbosity can be only number from interval"
        , show $ map fromEnum [minBound, maxBound :: Verbosity]
        , "or one of the following:"
        , concat . intersperse ", " $ map fst verbosityStrValues
        ]

    parseVerbosity = eitherReader $ \s ->
        case lookup s $ verbosityIntValues ++ verbosityStrValues of
            Just v  -> Right v
            Nothing -> Left parseVerbosityError

annoyingFlag :: IdentityT Parser (E Config)
annoyingFlag = IdentityT . flag id (verbosity .~ Annoying)
    $ long "annoying" <> help "Set verbosity to maximum."

silentFlag :: IdentityT Parser (E Config)
silentFlag = IdentityT . flag id (verbosity .~ Silent)
    $ short 's' <> long "silent" <> help "Set verbosity to minimum."

verboseFlag :: IdentityT Parser (E Config)
verboseFlag = IdentityT . flag id (verbosity .~ Verbose)
    $ short 'v' <> long "verbose" <> help "Be verbose."