1 {-# LANGUAGE MagicHash #-}
2 {-# LANGUAGE NamedFieldPuns #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 module Hcompta.Format.Ledger.Amount.Write where
7 import Data.Char (Char)
8 import Data.Decimal (DecimalRaw(..))
9 import Data.Eq (Eq(..))
10 import qualified Data.List as List
11 import Data.Maybe (Maybe(..), fromMaybe, maybe)
12 import Data.Ord (Ord(..))
13 import qualified Data.Text as Text
14 import qualified Data.Text.Lazy as TL
15 import Data.Tuple (fst)
16 import GHC.Exts (Int(..))
17 import GHC.Integer.Logarithms (integerLogBase#)
18 import Prelude (($), (.), Num(..), Show(..), flip, fromIntegral, id)
20 import qualified Hcompta.Amount as Amount
21 import Hcompta.Format.Ledger.Amount (Amount(..))
22 import qualified Hcompta.Format.Ledger.Amount as Ledger.Amount
23 import qualified Hcompta.Format.Ledger.Amount.Style as Amount.Style
24 import Hcompta.Format.Ledger.Quantity (Quantity)
25 import qualified Hcompta.Format.Ledger.Quantity as Ledger.Quantity
26 import qualified Hcompta.Format.Ledger.Unit as Ledger.Unit
27 import Hcompta.Lib.Leijen (Doc, (<>))
28 import qualified Hcompta.Lib.Leijen as W
29 import qualified Hcompta.Unit as Unit
32 amount :: Amount.Style.Styled Amount -> Doc
34 ( sty@(Amount.Style.Style
35 { Amount.Style.unit_side
36 , Amount.Style.unit_spaced
39 let unt = Amount.amount_unit amt in
41 Just Amount.Style.Side_Left ->
44 Just True | unt /= Unit.unit_empty -> W.space
47 <> quantity (sty, Amount.amount_quantity amt)
49 (Just Amount.Style.Side_Right) ->
51 Just True | unt /= Unit.unit_empty -> W.space
56 Just True | unt /= Unit.unit_empty -> W.space
61 amount_length :: Ledger.Amount.Styled Amount -> Int
62 amount_length (sty@(Amount.Style.Style { Amount.Style.unit_spaced }), amt) =
63 let unt = Amount.amount_unit amt in
64 Ledger.Unit.unit_length unt
65 + (case unit_spaced of { Just True | unt /= Unit.unit_empty -> 1; _ -> 0 })
66 + quantity_length sty (Amount.amount_quantity amt)
69 unit :: Ledger.Unit.Unit -> Doc
70 unit = W.yellow . W.strict_text . Unit.unit_text
73 quantity :: Ledger.Amount.Styled Quantity -> Doc
76 { Amount.Style.fractioning
77 , Amount.Style.grouping_integral
78 , Amount.Style.grouping_fractional
82 let num = show $ abs $ n
83 let sign = W.bold $ W.yellow $ W.strict_text (if n < 0 then "-" else "")
85 True -> sign <> do W.bold $ W.blue $ (W.strict_text $ Text.pack num)
87 let num_len = List.length num
90 [ List.replicate (fromIntegral e + 1 - num_len) '0'
92 -- , replicate (fromIntegral precision - fromIntegral e) '0'
94 let (int, frac) = List.splitAt (max 1 (num_len - fromIntegral e)) padded
95 let default_fractioning =
97 del_grouping_sep grouping_integral $
98 del_grouping_sep grouping_fractional $
102 W.text (TL.pack $ maybe id
103 (\g -> List.reverse . group g . List.reverse)
104 grouping_integral $ int) <> do
105 (W.yellow $ W.char (fromMaybe default_fractioning fractioning)) <> do
106 W.text (TL.pack $ maybe id group grouping_fractional frac)
108 group :: Amount.Style.Grouping -> [Char] -> [Char]
109 group (Amount.Style.Grouping sep sizes_) =
110 List.concat . List.reverse .
111 List.map List.reverse . fst .
113 (flip (\digit x -> case x of
114 ([], sizes) -> ([[digit]], sizes)
115 (digits:groups, []) -> ((digit:digits):groups, [])
116 (digits:groups, curr_sizes@(size:sizes)) ->
117 if List.length digits < size
118 then ( (digit:digits):groups, curr_sizes)
119 else ([digit]:[sep]:digits:groups, if List.null sizes then curr_sizes else sizes)
122 del_grouping_sep grouping =
124 Just (Amount.Style.Grouping sep _) -> List.delete sep
127 quantity_length :: Ledger.Amount.Style -> Ledger.Quantity.Quantity -> Int
128 quantity_length Amount.Style.Style
129 { Amount.Style.grouping_integral
130 , Amount.Style.grouping_fractional
132 let Decimal e n = qty in
133 let sign_len = if n < 0 then 1 else 0 in
134 let fractioning_len = if e > 0 then 1 else 0 in
135 let num_len = if n == 0 then 0 else (1 +) $ I# (integerLogBase# 10 (abs n)) in
136 let pad_left_len = max 0 (fromIntegral e + 1 - num_len) in
137 -- let pad_right_len = max 0 (fromIntegral precision - fromIntegral e) in
138 let padded_len = pad_left_len + num_len {-+ pad_right_len-} in
139 let int_len = max 1 (num_len - fromIntegral e) in
140 let frac_len = max 0 (padded_len - int_len) in
144 + maybe 0 (group int_len) grouping_integral
145 + maybe 0 (group frac_len) grouping_fractional
148 group :: Int -> Amount.Style.Grouping -> Int
149 group num_len (Amount.Style.Grouping _sep sizes_) =
152 else loop 0 num_len sizes_
154 loop :: Int -> Int -> [Int] -> Int
159 let l = len - size in
161 else loop (pad + 1) l sizes
163 let l = len - size in
165 else loop (pad + 1) l sizes