penny-lib-0.4.0.0: Penny/Cabin/Balance/Parser.hs
module Penny.Cabin.Balance.Parser (
Error(..)
, ParseOpts(..)
, parseOptions
) where
import qualified Data.Text as X
import Control.Applicative ((<|>), many, Applicative, pure)
import Control.Monad ((>=>))
import qualified Control.Monad.Exception.Synchronous as Ex
import qualified Penny.Cabin.Colors as Col
import qualified Penny.Cabin.Colors.DarkBackground as DB
import qualified Penny.Cabin.Colors.LightBackground as LB
import qualified Penny.Cabin.Chunk as Chk
import qualified Penny.Cabin.Options as CO
import qualified Penny.Copper.Commodity as CC
import qualified Penny.Copper.DateTime as CD
import qualified Penny.Lincoln as L
import qualified Penny.Shield as S
import System.Console.MultiArg.Prim (Parser)
import qualified System.Console.MultiArg.Combinator as C
import qualified Text.Parsec as Parsec
data ParseOpts = ParseOpts {
drCrColors :: Col.DrCrColors
, baseColors :: Col.BaseColors
, colorPref :: Chk.Colors
, showZeroBalances :: CO.ShowZeroBalances
, convert :: Maybe (L.Commodity, L.DateTime)
}
data Error = BadColorName String
| BadBackground String
| BadCommodity String
| BadDate String
deriving Show
processColorArg ::
S.Runtime
-> String
-> Maybe Chk.Colors
processColorArg rt x
| x == "yes" = return Chk.Colors8
| x == "no" = return Chk.Colors0
| x == "auto" = return (CO.maxCapableColors rt)
| x == "256" = return Chk.Colors256
| otherwise = Nothing
parseOpt :: [String] -> [Char] -> C.ArgSpec a -> Parser a
parseOpt ss cs a = C.parseOption [C.OptSpec ss cs a]
color :: Parser (S.Runtime
-> ParseOpts
-> Ex.Exceptional Error ParseOpts)
color = parseOpt ["color"] "" (C.OneArg f)
where
f a1 rt op = case processColorArg rt a1 of
Nothing -> Ex.throw . BadColorName $ a1
Just c -> return (op { colorPref = c })
processBackgroundArg ::
String
-> Maybe (Col.DrCrColors, Col.BaseColors)
processBackgroundArg x
| x == "light" = return (LB.drCrColors, LB.baseColors)
| x == "dark" = return (DB.drCrColors, DB.baseColors)
| otherwise = Nothing
background :: Parser (ParseOpts -> Ex.Exceptional Error ParseOpts)
background = parseOpt ["background"] "" (C.OneArg f)
where
f a1 op = case processBackgroundArg a1 of
Nothing -> Ex.throw . BadBackground $ a1
Just (dc, base) ->
return op { drCrColors = dc
, baseColors = base }
parseShowZeroBalances :: Parser (ParseOpts -> ParseOpts)
parseShowZeroBalances = parseOpt opt "" (C.NoArg f)
where
opt = ["show-zero-balances"]
f op =
op {showZeroBalances = CO.ShowZeroBalances True }
hideZeroBalances :: Parser (ParseOpts -> ParseOpts)
hideZeroBalances = parseOpt ["hide-zero-balances"] "" (C.NoArg f)
where
f op =
op {showZeroBalances = CO.ShowZeroBalances False }
convertLong ::
Parser (CD.DefaultTimeZone
-> ParseOpts
-> Ex.Exceptional Error ParseOpts)
convertLong = parseOpt ["convert"] "" (C.TwoArg f)
where
f a1 a2 dtz op = do
cty <- case Parsec.parse CC.lvl1Cmdty "" (X.pack a1) of
Left _ -> Ex.throw . BadCommodity $ a1
Right g -> return g
let parseDate = CD.dateTime dtz
dt <- case Parsec.parse parseDate "" (X.pack a2) of
Left _ -> Ex.throw . BadDate $ a2
Right g -> return g
let op' = op { convert = Just (cty, dt) }
return op'
convertShort :: Parser (S.Runtime
-> ParseOpts
-> Ex.Exceptional Error ParseOpts)
convertShort = parseOpt [] ['c'] (C.OneArg f)
where
f a1 rt op = do
cty <- case Parsec.parse CC.lvl1Cmdty "" (X.pack a1) of
Left _ -> Ex.throw . BadCommodity $ a1
Right g -> return g
let dt = S.currentTime rt
op' = op { convert = Just (cty, dt) }
return op'
parseOptions :: Parser (S.Runtime
-> CD.DefaultTimeZone
-> ParseOpts
-> Ex.Exceptional Error ParseOpts)
parseOptions = do
fns <- many parseOption
let f rt dtz o1 =
let fns' = map (\fn -> fn rt dtz) fns
in foldl (>=>) return fns' o1
return f
parseOption :: Parser (S.Runtime
-> CD.DefaultTimeZone
-> ParseOpts
-> Ex.Exceptional Error ParseOpts)
parseOption =
(do { f <- color; return (\rt _ o -> f rt o )})
<|> wrap background
<|> wrap (impurify parseShowZeroBalances)
<|> wrap (impurify hideZeroBalances)
<|> (do { f <- convertLong; return (\_ dtz o -> f dtz o )})
<|> (do { f <- convertShort; return (\rt _ o -> f rt o )})
where
wrap p = do
f <- p
return (\_ _ op -> f op)
impurify ::
(Applicative m, Functor f)
=> f (a -> a)
-> f (a -> m a)
impurify = fmap (\f -> pure . f)