-- | Pretty print monetary amounts like: -- -- * /$ 34.50/ -- -- * /USD 3,456.29/ -- -- * /€ 32 433 938.23/ -- -- Using default printing settings -- -- >>> prettyPrint (Amount USD 2342.2) -- "USD 2,342.20" -- >>> prettyPrint (Amount EUR 45827.346) -- "EUR 45,827.35" -- -- Using custom printing settings -- -- >>> prettyPrintWith (defaultConfig { useCurrencySymbol = True }) (Amount USD 2342.2) -- "$ 2,342.20" -- >>> prettyPrintWith (defaultConfig { useCurrencySymbol = True }) (Amount EUR 2342.2) -- "€ 2,342.20" -- >>> prettyPrintWith (defaultConfig { showDecimals = False }) (Amount USD 25.50) -- "USD 25" -- -- For more printing settings see 'PrettyConfig' module Data.Currency.Pretty ( -- * Pretty printing prettyPrint , prettyPrintWith -- * Configuration , PrettyConfig(..) , defaultConfig , module Data.Currency.Amounts ) where import Text.Printf import Data.Currency.Amounts import Data.Monoid ((<>)) -- | Pretty print a monetary amount using 'defaultConfig' prettyPrint :: (Currency c) => Amount c -> String prettyPrint = prettyPrintWith defaultConfig -- | Pretty print a monetary amount with a custom 'PrettyConfig' configuration prettyPrintWith :: (Currency c) => PrettyConfig -> Amount c -> String prettyPrintWith cnf (Amount currency amount) = prefixSymbol currency cnf $ prefixCode currency cnf $ changeDecimalSep currency cnf $ largeAmountSeparate currency cnf $ toDecimalString currency cnf amount prefixSymbol :: (Currency c) => c -> PrettyConfig -> String -> String prefixSymbol currency cnf val | useCurrencySymbol cnf = symbol currency <> " " <> val | otherwise = val prefixCode :: (Currency c) => c -> PrettyConfig -> String -> String prefixCode currency cnf val | useCurrencySymbol cnf = val | suffixIsoCode cnf = val <> " " <> isoCode currency | otherwise = isoCode currency <> " " <> val changeDecimalSep :: (Currency c) => c -> PrettyConfig -> String -> String changeDecimalSep currency cnf = replaceFst '.' (decimalSeparator cnf) where replaceFst :: Char -> Char -> String -> String replaceFst c c' [] = [] replaceFst c c' (s:ss) | s == c = c' : ss | otherwise = s : replaceFst c c' ss largeAmountSeparate :: (Currency c) => c -> PrettyConfig -> String -> String largeAmountSeparate currency cnf amount | compactFourDigitAmounts cnf = if length integer <= 4 then amount else separated ++ decimal | otherwise = separated ++ decimal where (integer, decimal) = span (/= '.') amount separated = reverse $ intersperseN 3 (largeAmountSeparator cnf) $ reverse integer toDecimalString :: (Currency c) => c -> PrettyConfig -> Double -> String toDecimalString currency cnf amount | showDecimals cnf = printf format amount | otherwise = takeWhile (/= '.') $ printf "%.1f" amount where format = "%." <> show (decimalDigits currency) <> "f" intersperseN :: Eq a => Int -> a -> [a] -> [a] intersperseN n s ss | null remainder = ss | otherwise = (take ++ [s]) ++ intersperseN n s remainder where (take, remainder) = splitAt n ss data PrettyConfig = PrettyConfig { showDecimals :: Bool -- | Print four digits amounts as -- /USD 1000,00/ instead of /USD 1,000.00/ , compactFourDigitAmounts :: Bool -- | Replace the currency ISO code with its symbol to produce -- /$ 23.50/ instead of /USD 23.50/ , useCurrencySymbol :: Bool -- | Use the currency ISO code as suffix to produce -- /23.50 USD/ instead of /USD 23.50/ , suffixIsoCode :: Bool , largeAmountSeparator :: Char , decimalSeparator :: Char } deriving (Show) -- | Default 'PrettyConfig' used in 'prettyPrint' -- -- * Show decimals -- -- * Compact four digit amounts -- -- * Use ISO code -- -- * Separate large amounts with comma -- -- * Separate decimals with dot defaultConfig :: PrettyConfig defaultConfig = PrettyConfig { showDecimals = True , compactFourDigitAmounts = True , useCurrencySymbol = False , suffixIsoCode = False , largeAmountSeparator = ',' , decimalSeparator = '.' }