module Penny.Brenner
( FitAcct(..)
, Config(..)
, Su.S3(..)
, L.Radix(..)
, L.PeriodGrp(..)
, L.CommaGrp(..)
, Y.Translator(..)
, L.Side(..)
, L.SpaceBetween(..)
, usePayeeOrDesc
, brennerMain
, ofxParser
, ofxPrepassParser
) where
import qualified Penny.Brenner.Types as Y
import qualified Data.Text as X
import qualified Data.Version as V
import qualified Penny.Liberty as Ly
import qualified Penny.Lincoln as L
import qualified Penny.Lincoln.Builders as Bd
import qualified Penny.Brenner.Clear as C
import qualified Penny.Brenner.Database as D
import qualified Penny.Brenner.Import as I
import qualified Penny.Brenner.Info as Info
import qualified Penny.Brenner.Merge as M
import qualified Penny.Brenner.OFX as O
import qualified Penny.Brenner.Print as P
import qualified Data.Sums as Su
import qualified System.Console.MultiArg as MA
import System.Environment (getProgName)
import qualified System.Exit as Exit
brennerMain
:: V.Version
-> Config
-> IO ()
brennerMain v cf = do
let cf' = convertConfig cf
pn <- getProgName
ei <- MA.modesIO (globalOpts v) (preProcessor cf')
case ei of
Left showHelp -> putStr (showHelp pn) >> Exit.exitSuccess
Right g -> g
type GetHelp = MA.ProgName -> String
globalOpts
:: V.Version
-> MA.Opts GetHelp Y.FitAcctName
globalOpts v = MA.optsHelpVersion help (Ly.version v)
[ MA.OptSpec ["fit-account"] "f"
(MA.OneArg (return . Y.FitAcctName . X.pack))
]
preProcessor
:: Y.Config
-> [Y.FitAcctName]
-> Either String
(Either (IO ())
[MA.Mode (MA.ProgName -> String) (IO ())])
preProcessor cf args = do
mayFi <- case args of
[] -> return $ Y.defaultFitAcct cf
_ ->
let pdct a = Y.fitAcctName a == s
s = last args
toFilter = case Y.defaultFitAcct cf of
Nothing -> Y.moreFitAccts cf
Just d -> d : Y.moreFitAccts cf
in case filter pdct toFilter of
[] -> Left $
"financial institution account "
++ (X.unpack . Y.unFitAcctName $ s) ++ " not configured."
c:[] -> return $ Just c
_ -> Left $
"more than one financial institution account "
++ "named " ++ (X.unpack . Y.unFitAcctName $ s)
++ " configured."
return . Right $ allModes cf mayFi
allModes
:: Y.Config
-> Maybe Y.FitAcct
-> [MA.Mode (MA.ProgName -> String) (IO ())]
allModes c a =
[ C.mode a
, I.mode a
, Info.mode c
, M.mode a
, P.mode a
, D.mode a ]
help
:: String
-> String
help n = unlines ls
where
ls = [ "usage: " ++ n ++ " [global-options]"
++ " COMMAND [local-options]"
++ " ARGS..."
, ""
, "where COMMAND is one of:"
, "import, merge, clear, database, print, info"
, ""
, "For help on an individual command and its"
++ " local options, use "
, n ++ " COMMAND --help"
, ""
, "Global Options:"
, "-f, --fit-account ACCOUNT"
, " use the given financial institution account"
, " (use the \"info\" command to see which are available)."
, " If this option does not appear,"
, " the default account is used if there is one."
]
data FitAcct = FitAcct
{ fitAcctName :: String
, fitAcctDesc :: String
, dbLocation :: String
, pennyAcct :: String
, defaultAcct :: String
, currency :: String
, qtySpec :: Su.S3 L.Radix L.PeriodGrp L.CommaGrp
, translator :: Y.Translator
, side :: L.Side
, spaceBetween :: L.SpaceBetween
, parser :: ( Y.ParserDesc
, Y.FitFileLocation -> IO (Either String [Y.Posting]))
, toLincolnPayee :: Y.Desc -> Y.Payee -> L.Payee
}
convertFitAcct :: FitAcct -> Y.FitAcct
convertFitAcct (FitAcct fn fd db ax df cy gs tl sd sb ps tlp) = Y.FitAcct
{ Y.fitAcctName = Y.FitAcctName . X.pack $ fn
, Y.fitAcctDesc = Y.FitAcctDesc . X.pack $ fd
, Y.dbLocation = Y.DbLocation . X.pack $ db
, Y.pennyAcct = Y.PennyAcct . Bd.account . X.pack $ ax
, Y.defaultAcct = Y.DefaultAcct . Bd.account . X.pack $ df
, Y.currency = Y.Currency . L.Commodity . X.pack $ cy
, Y.qtySpec = gs
, Y.translator = tl
, Y.side = sd
, Y.spaceBetween = sb
, Y.parser = ps
, Y.toLincolnPayee = tlp
}
data Config = Config
{ defaultFitAcct :: Maybe FitAcct
, moreFitAccts :: [FitAcct]
}
convertConfig :: Config -> Y.Config
convertConfig (Config d m) = Y.Config
{ Y.defaultFitAcct = fmap convertFitAcct d
, Y.moreFitAccts = map convertFitAcct m
}
usePayeeOrDesc :: Y.Desc -> Y.Payee -> L.Payee
usePayeeOrDesc (Y.Desc d) (Y.Payee p) = L.Payee $
if X.null p then d else p
ofxParser :: (Y.ParserDesc, Y.ParserFn)
ofxParser = O.parser
ofxPrepassParser :: (String -> String) -> (Y.ParserDesc, Y.ParserFn)
ofxPrepassParser = O.prepassParser