1 {-# LANGUAGE BangPatterns #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE FlexibleInstances #-}
4 {-# LANGUAGE MultiParamTypeClasses #-}
5 {-# LANGUAGE NamedFieldPuns #-}
6 {-# LANGUAGE OverloadedStrings #-}
7 {-# LANGUAGE ScopedTypeVariables #-}
8 {-# LANGUAGE TupleSections #-}
9 {-# LANGUAGE TypeFamilies #-}
10 module Hcompta.CLI.Command.Tags where
12 import Control.Monad (liftM, forM_)
13 import Control.Monad.IO.Class (liftIO)
14 import Control.Monad.Trans.Except (runExceptT)
15 import qualified Data.Either
16 import qualified Data.Foldable
17 import Data.Functor.Compose (Compose(..))
18 import Data.Monoid ((<>))
19 import qualified Data.Map.Strict as Data.Map
20 import Data.Map.Strict (Map)
21 -- import Data.Text (Text)
22 import System.Console.GetOpt
26 import System.Environment as Env (getProgName)
27 import System.Exit (exitSuccess)
28 import qualified System.IO as IO
30 import Hcompta.Account (Account)
31 import qualified Hcompta.CLI.Args as Args
32 import qualified Hcompta.CLI.Context as Context
33 import Hcompta.CLI.Context (Context)
34 import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
35 import qualified Hcompta.CLI.Write as Write
36 import qualified Hcompta.Filter as Filter
37 import qualified Hcompta.Filter.Read as Filter.Read
38 import qualified Hcompta.Format.Ledger as Ledger
39 import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
40 import qualified Hcompta.Format.Ledger.Read as Ledger.Read
41 import qualified Hcompta.Lib.Leijen as W
42 import Hcompta.Lib.Consable (Consable(..))
43 import qualified Hcompta.Tag as Tag
47 { ctx_input :: [FilePath]
48 , ctx_filter_transaction :: Filter.Simplified
50 (Filter.Filter_Transaction
58 , ctx_filter_transaction = mempty
63 bin <- Env.getProgName
64 let pad = replicate (length bin) ' '
67 , " "++bin++" tags [-i JOURNAL_FILE]"
68 , " "++pad++" [-t TRANSACTION_FILTER]"
69 , " "++pad++" [JOURNAL_FILE] [...]"
71 , usageInfo "OPTIONS" options
74 options :: Args.Options Ctx
77 (NoArg (\_context _ctx -> do
78 usage >>= IO.hPutStr IO.stderr
81 , Option "i" ["input"]
82 (ReqArg (\s _context ctx -> do
83 return $ ctx{ctx_input=s:ctx_input ctx}) "FILE")
84 "read data from given file, multiple uses merge the data as would a concatenation do"
85 , Option "t" ["transaction-filter"]
86 (ReqArg (\s context ctx -> do
87 ctx_filter_transaction <-
88 liftM ((ctx_filter_transaction ctx <>) . Filter.simplify) $
89 liftIO $ Filter.Read.read Filter.Read.filter_transaction s
91 Left ko -> Write.fatal context $ ko
93 Write.debug context $ "filter: transaction: " ++ show ok
95 return $ ctx{ctx_filter_transaction}) "FILTER")
96 "filter at transaction level, multiple uses are merged with a logical AND"
99 run :: Context.Context -> [String] -> IO ()
100 run context args = do
101 (ctx, inputs) <- Args.parse context usage options (nil, args)
103 liftM Data.Either.partitionEithers $ do
104 CLI.Ledger.paths context $ ctx_input ctx ++ inputs
107 liftIO $ runExceptT $ Ledger.Read.file
108 (Ledger.Read.context (ctx_filter_transaction ctx) Ledger.journal)
111 Left ko -> return $ Left (path, ko)
112 Right ok -> return $ Right ok
113 case read_journals of
114 (errs@(_:_), _journals) ->
115 forM_ errs $ \(_path, err) -> do
116 Write.fatal context $ err
118 let files = ledger_tags ctx journals
119 style_color <- Write.with_color context IO.stdout
120 W.displayIO IO.stdout $ do
121 W.renderPretty style_color 1.0 maxBound $ do
122 doc_tags context ctx files
126 -> [ Ledger.Journal (Tags Ledger.Transaction) ]
127 -> Tags Ledger.Transaction
130 (flip $ Ledger.Journal.fold
131 (\Ledger.Journal{Ledger.journal_transactions=ts} ->
138 -> Tags Ledger.Transaction
140 doc_tags _context _ctx =
141 Data.Map.foldlWithKey
143 doc <> foldMap (\s -> W.dullyellow (W.toDoc () s) <> ":") p <>
144 " (" <> (W.toDoc () (Data.Map.size vs)) <> ")" <>
146 Data.Map.foldlWithKey
148 doc' <> W.line <> W.dullred (W.toDoc () v) <>
149 " (" <> (W.toDoc () vn) <> ")"
157 -- * Requirements' interface
159 -- ** Class 'Posting'
161 class Posting p where
162 posting_account :: p -> Account
164 instance Posting Ledger.Posting where
165 posting_account = Ledger.posting_account
167 -- ** Class 'Transaction'
170 ( Posting (Transaction_Posting t)
171 , Foldable (Transaction_Postings t)
173 => Transaction t where
174 type Transaction_Posting t
175 type Transaction_Postings t :: * -> *
176 -- transaction_postings :: t -> Transaction_Postings t (Transaction_Posting t)
177 transaction_tags :: t -> Map Tag.Path [Tag.Value]
179 instance Transaction Ledger.Transaction where
180 type Transaction_Posting Ledger.Transaction = Ledger.Posting
181 type Transaction_Postings Ledger.Transaction = Compose [] (Compose (Map Account) [])
182 transaction_tags = Ledger.transaction_tags
186 data Transaction t => Tags t
188 { tags :: Map Tag.Path (Map Tag.Value Integer)
192 instance Transaction t => Monoid (Tags t) where
196 { tags = Data.Map.unionWith
197 (Data.Map.unionWith (+))
201 instance Transaction t => Consable () Tags t where
205 Data.Map.mergeWithKey
207 Data.Map.unionWith (+) x1 $
208 Data.Map.fromListWith (+) $ (, 1) <$> x2)
209 id ((Data.Map.fromListWith (+) . ((, 1) <$>)) <$>)
210 (tags ts) -- Map Tag.Path (Map Tag.Value Integer)
211 (transaction_tags t) -- Map Tag.Path [Tag.Value]
214 ( Filter.Transaction t
219 (Filter.Filter_Transaction t)))