{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Hcompta.CLI.Command.Journal where
import Control.Monad ({-foldM,-} liftM)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except (runExceptT)
import qualified Data.Either
import qualified Data.Foldable
import System.Console.GetOpt
( ArgDescr(..)
, OptDescr(..)
, usageInfo )
import Data.Monoid ((<>))
import Prelude hiding (foldr)
import System.Environment as Env (getProgName)
import System.Exit (exitWith, ExitCode(..))
import qualified System.IO as IO
import qualified Hcompta.CLI.Args as Args
import qualified Hcompta.CLI.Context as Context
import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
import qualified Hcompta.CLI.Write as Write
-- import qualified Hcompta.Date as Date
import qualified Hcompta.Filter as Filter
import qualified Hcompta.Filter.Read as Filter.Read
-- import qualified Hcompta.Filter.Reduce as Filter.Reduce
import qualified Hcompta.Format.Ledger as Ledger
import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
import qualified Hcompta.Format.Ledger.Read as Ledger.Read
import qualified Hcompta.Format.Ledger.Write as Ledger.Write
import qualified Hcompta.Lib.Leijen as W
import qualified Hcompta.Journal as Journal
data Ctx
= Ctx
{ ctx_input :: [FilePath]
, ctx_align :: Bool
, ctx_reduce_date :: Bool
, ctx_filter_transaction :: Filter.Simplified
(Filter.Filter_Bool
(Filter.Filter_Transaction
Ledger.Transaction))
} deriving (Show)
nil :: Ctx
nil =
Ctx
{ ctx_input = []
, ctx_align = True
, ctx_reduce_date = True
, ctx_filter_transaction = mempty
}
usage :: IO String
usage = do
bin <- Env.getProgName
return $unlines $
[ "SYNTAX "
, " "++bin++" journal"
, " [-i JOURNAL_FILE]"
, " [-t TRANSACTION_FILTER]"
, " [JOURNAL_FILE] [...]"
, ""
, usageInfo "OPTIONS" options
]
options :: Args.Options Ctx
options =
[ Option "h" ["help"]
(NoArg (\_context _ctx -> do
usage >>= IO.hPutStr IO.stderr
exitWith ExitSuccess))
"show this help"
, Option "i" ["input"]
(ReqArg (\s _context ctx -> do
return $ ctx{ctx_input=s:ctx_input ctx}) "FILE")
"read data from given file, multiple uses merge the data as would a concatenation do"
, Option "" ["align"]
(OptArg (\arg context ctx -> do
ctx_align <- case arg of
Nothing -> return $ True
Just "yes" -> return $ True
Just "no" -> return $ False
Just _ -> Write.fatal context $
W.text "--align option expects \"yes\", or \"no\" as value"
return $ ctx{ctx_align})
"[yes|no]")
"align output"
{- NOTE: not used so far.
, Option "" ["reduce-date"]
(OptArg (\arg context ctx -> do
ctx_reduce_date <- case arg of
Nothing -> return $ True
Just "yes" -> return $ True
Just "no" -> return $ False
Just _ -> Write.fatal context $
W.text "--reduce-date option expects \"yes\", or \"no\" as value"
return $ ctx{ctx_reduce_date})
"[yes|no]")
"use advanced date reducer to speed up filtering"
-}
, Option "t" ["transaction-filter"]
(ReqArg (\s context ctx -> do
ctx_filter_transaction <-
liftM ((ctx_filter_transaction ctx <>) . Filter.simplify) $
liftIO $ Filter.Read.read Filter.Read.filter_transaction s
>>= \f -> case f of
Left ko -> Write.fatal context $ ko
Right ok -> return ok
return $ ctx{ctx_filter_transaction}) "FILTER")
"filter at transaction level, multiple uses are merged with a logical AND"
]
run :: Context.Context -> [String] -> IO ()
run context args = do
(ctx, inputs) <- Args.parse context usage options (nil, args)
read_journals <- do
CLI.Ledger.paths context $ ctx_input ctx ++ inputs
>>= do
mapM $ \path -> do
liftIO $ runExceptT $ Ledger.Read.file
(Ledger.Read.context (ctx_filter_transaction ctx) Ledger.journal)
path
>>= \x -> case x of
Left ko -> return $ Left (path, ko)
Right ok -> return $ Right ok
>>= return . Data.Either.partitionEithers
case read_journals of
(errs@(_:_), _journals) ->
(flip mapM_) errs $ \(_path, err) -> do
Write.fatal context $ err
([], journals) -> do
Write.debug context $ "filter: transaction: " ++ show (ctx_filter_transaction ctx)
{- NOTE: not used so far
let reducer_date =
if ctx_reduce_date ctx
then Filter.Reduce.bool_date <$> ctx_filter_transaction ctx
else mempty
Write.debug context $ "filter: transaction: reducer: " ++ show reducer_date
-}
style_color <- Write.with_color context IO.stdout
let sty = Ledger.Write.Style
{ Ledger.Write.style_align = ctx_align ctx
, Ledger.Write.style_color
}
let journal = ledger_journal ctx journals
Ledger.Write.put sty IO.stdout $ do
Ledger.Write.transactions journal
ledger_journal
:: Ctx
-> [Ledger.Journal (Journal.Journal Ledger.Transaction)
]
-> Journal.Journal Ledger.Transaction
ledger_journal _ctx journals =
Data.Foldable.foldl'
(flip $ Ledger.Journal.fold
(\Ledger.Journal{Ledger.journal_transactions=j} ->
mappend j))
mempty journals