1 {-# LANGUAGE FlexibleInstances #-}
2 {-# LANGUAGE MultiParamTypeClasses #-}
3 {-# LANGUAGE NamedFieldPuns #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 module Hcompta.CLI.Format.Ledger where
8 import Prelude hiding (error)
9 import Control.Exception (tryJust)
10 import Control.Monad (guard)
11 import qualified Data.List
12 import qualified Data.Text.Lazy as TL
13 import System.Environment as Env (getEnv)
14 import System.IO.Error (isDoesNotExistError)
15 import qualified Text.Parsec as Parsec
16 import qualified Text.Parsec.Error as Parsec.Error
18 import qualified Hcompta.Lib.Leijen as W
19 import Hcompta.Lib.Leijen (ToDoc(..), (<>))
20 import qualified Hcompta.Lib.Parsec as Lib.Parsec
21 import qualified Hcompta.Calc.Balance as Calc.Balance
22 import qualified Hcompta.CLI.Context as Context
23 import Hcompta.CLI.Context (Context)
24 import qualified Hcompta.CLI.Lib.Shakespeare.Leijen as I18N
25 import qualified Hcompta.CLI.Write as Write
26 import qualified Hcompta.Format.Ledger.Read as Ledger.Read
27 import qualified Hcompta.Format.Ledger.Write as Ledger.Write
28 import qualified Hcompta.Model.Amount as Amount
30 -- | Return 'FilePath's containing 'Hcompta.Format.Ledger.Journal's:
32 -- * either those given if any,
33 -- * or the one in LEDGER_FILE environment variable if any,
34 -- * or the one in LEDGER environment variable if any.
35 paths :: Context.Context -> [FilePath] -> IO [FilePath]
37 tryJust (guard . isDoesNotExistError) $ Env.getEnv "LEDGER_FILE"
39 Right ok -> return [ok]
41 tryJust (guard . isDoesNotExistError) $ Env.getEnv "LEDGER"
43 Right ok -> return [ok]
45 let langs = Context.langs context
47 I18N.renderMessage Context.App langs
48 Write.I18N_no_ledger_file_given
49 paths _context ps = return ps
51 instance ToDoc Context Parsec.SourcePos where
52 toDoc context pos = do
53 let langs = Context.langs context
54 I18N.renderMessage Context.App langs $ do
55 case Parsec.sourceName pos of
57 (Parsec.sourceLine pos)
58 (Parsec.sourceColumn pos)
59 path -> Write.I18N_in_file path
60 (Parsec.sourceLine pos)
61 (Parsec.sourceColumn pos)
63 instance ToDoc Context [Lib.Parsec.Error Ledger.Read.Error] where
64 toDoc context errors =
66 (flip map) errors $ (\error ->
68 Lib.Parsec.Error_At pos errs -> W.vsep $
72 Lib.Parsec.Error_Parser err ->
74 [ toDoc context (Parsec.errorPos err)
76 (Parsec.Error.errorMessages err)
79 : (flip Data.List.map)
80 (uniq $ Parsec.Error.errorMessages err)
82 I18N.renderMessage Context.App langs $
84 Parsec.Error.SysUnExpect msg -> Write.I18N_sysunexpect msg
85 Parsec.Error.UnExpect msg -> Write.I18N_unexpect msg
86 Parsec.Error.Expect msg -> Write.I18N_expect msg
87 Parsec.Error.Message msg -> Write.I18N_message msg
91 Lib.Parsec.Error_Custom pos err -> W.vsep $
94 Ledger.Read.Error_year_or_day_is_missing ->
95 I18N.renderMessage Context.App langs $
96 Write.I18N_year_or_day_is_missing
97 Ledger.Read.Error_invalid_day (y, m, d) ->
98 I18N.renderMessage Context.App langs $
99 Write.I18N_invalid_day y m d
100 Ledger.Read.Error_invalid_time_of_day (h, m, s) ->
101 I18N.renderMessage Context.App langs $
102 Write.I18N_invalid_time_of_day h m s
103 Ledger.Read.Error_transaction_not_equilibrated tr unit_sums ->
104 i18n_transaction_not_equilibrated tr unit_sums
105 Write.I18N_the_following_transaction_is_not_equilibrated_because
106 Ledger.Read.Error_virtual_transaction_not_equilibrated tr unit_sums ->
107 i18n_transaction_not_equilibrated tr unit_sums
108 Write.I18N_the_following_virtual_transaction_is_not_equilibrated_because
109 Ledger.Read.Error_reading_file file_path exn -> W.vsep $
110 [ I18N.renderMessage Context.App langs $
111 Write.I18N_failed_to_read_file file_path
112 , W.text $ TL.pack $ show exn
114 Ledger.Read.Error_including_file file_path errs -> W.vsep $
115 [ I18N.renderMessage Context.App langs $
116 Write.I18N_failed_to_include_file file_path
122 langs = Context.langs context
123 i18n_transaction_not_equilibrated tr unit_sums msg =
125 [ I18N.renderMessage Context.App langs msg
126 , W.vsep $ Data.List.map
127 (\Calc.Balance.Unit_Sum{Calc.Balance.unit_sum_amount} ->
128 I18N.renderMessage Context.App langs $
129 Write.I18N_unit_sums_up_to_the_non_null_amount
130 (Amount.unit unit_sum_amount)
134 , Ledger.Write.transaction tr
136 showErrorMessages :: [Parsec.Error.Message] -> W.Doc
137 showErrorMessages msgs
138 | null msgs = i18n $ Write.I18N_unknown
139 | otherwise = W.vsep $ -- clean $
140 [showSysUnExpect, showUnExpect, showExpect, showMessages]
142 i18n = I18N.renderMessage Context.App langs
143 (sysUnExpect,msgs1) = span ((Parsec.Error.SysUnExpect "") ==) msgs
144 (unExpect,msgs2) = span ((Parsec.Error.UnExpect "") ==) msgs1
145 (expect,messages) = span ((Parsec.Error.Expect "") ==) msgs2
147 showExpect = showMany (Just (i18n . Write.I18N_expect)) expect
148 showUnExpect = showMany (Just (i18n . Write.I18N_unexpect)) unExpect
150 | not (null unExpect) || null sysUnExpect = W.empty
151 | null firstMsg = i18n $ Write.I18N_sysunexpect_end_of_input
152 | otherwise = i18n $ Write.I18N_sysunexpect firstMsg
154 firstMsg = Parsec.Error.messageString (head sysUnExpect)
156 showMessages = showMany Nothing messages
159 showMany :: (Maybe (W.Doc -> W.Doc)) -> [Parsec.Error.Message] -> W.Doc
161 case clean (map Parsec.Error.messageString msgs_) of
165 Nothing -> commasOr ms
166 Just p -> p $ commasOr ms
168 commasOr :: [String] -> W.Doc
169 commasOr [] = W.empty
170 commasOr [m] = W.text $ TL.pack m
171 commasOr ms = commaSep (init ms)
172 <> (W.space <> i18n Write.I18N_or <> W.space)
173 <> (W.bold $ W.dullblack $ W.text $ TL.pack $ last ms)
174 commaSep = W.intercalate (W.comma <> W.space)
175 (W.bold . W.dullblack . W.text . TL.pack)
178 clean = Data.List.nub . filter (not . null)