hledger/hledger-lib/Hledger/Data/OutputFormat.hs

124 lines
5.1 KiB
Haskell
Raw Normal View History

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE CPP #-}
module Hledger.Data.OutputFormat (
parseStringFormat
, formatsp
2011-06-21 01:33:26 +04:00
, formatValue
, OutputFormat(..)
, HledgerFormatField(..)
2011-06-21 01:33:26 +04:00
, tests
) where
import Numeric
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative.Compat ((<*))
#endif
import Data.Char (isPrint)
2011-06-21 01:33:26 +04:00
import Data.Maybe
import Test.HUnit
import Text.Parsec
2011-06-21 01:33:26 +04:00
import Text.Printf
import Hledger.Data.Types
2011-06-21 01:33:26 +04:00
formatValue :: Bool -> Maybe Int -> Maybe Int -> String -> String
formatValue leftJustified min max value = printf formatS value
where
l = if leftJustified then "-" else ""
min' = maybe "" show min
max' = maybe "" (\i -> "." ++ (show i)) max
formatS = "%" ++ l ++ min' ++ max' ++ "s"
parseStringFormat :: String -> Either String [OutputFormat]
2014-11-04 06:35:25 +03:00
parseStringFormat input = case (runParser (formatsp <* eof) () "(unknown)") input of
2011-06-21 01:33:26 +04:00
Left y -> Left $ show y
Right x -> Right x
{-
Parsers
-}
field :: Stream [Char] m Char => ParsecT [Char] st m HledgerFormatField
2011-06-21 01:33:26 +04:00
field = do
try (string "account" >> return AccountField)
<|> try (string "depth_spacer" >> return DepthSpacerField)
<|> try (string "date" >> return DescriptionField)
<|> try (string "description" >> return DescriptionField)
<|> try (string "total" >> return TotalField)
<|> try (many1 digit >>= (\s -> return $ FieldNo $ read s))
2011-06-21 01:33:26 +04:00
formatField :: Stream [Char] m Char => ParsecT [Char] st m OutputFormat
2011-06-21 01:33:26 +04:00
formatField = do
char '%'
leftJustified <- optionMaybe (char '-')
minWidth <- optionMaybe (many1 $ digit)
maxWidth <- optionMaybe (do char '.'; many1 $ digit) -- TODO: Can this be (char '1') *> (many1 digit)
2011-06-21 01:33:26 +04:00
char '('
f <- field
2011-06-21 01:33:26 +04:00
char ')'
return $ FormatField (isJust leftJustified) (parseDec minWidth) (parseDec maxWidth) f
2011-06-21 01:33:26 +04:00
where
parseDec s = case s of
Just text -> Just m where ((m,_):_) = readDec text
_ -> Nothing
formatLiteral :: Stream [Char] m Char => ParsecT [Char] st m OutputFormat
2011-06-21 01:33:26 +04:00
formatLiteral = do
s <- many1 c
return $ FormatLiteral s
where
isPrintableButNotPercentage x = isPrint x && (not $ x == '%')
c = (satisfy isPrintableButNotPercentage <?> "printable character")
2011-06-21 01:33:26 +04:00
<|> try (string "%%" >> return '%')
formatp :: Stream [Char] m Char => ParsecT [Char] st m OutputFormat
formatp =
2011-06-21 01:33:26 +04:00
formatField
<|> formatLiteral
formatsp :: Stream [Char] m Char => ParsecT [Char] st m [OutputFormat]
formatsp = many formatp
2011-06-21 01:33:26 +04:00
testFormat :: OutputFormat -> String -> String -> Assertion
2011-06-21 01:33:26 +04:00
testFormat fs value expected = assertEqual name expected actual
where
(name, actual) = case fs of
FormatLiteral l -> ("literal", formatValue False Nothing Nothing l)
FormatField leftJustify min max _ -> ("field", formatValue leftJustify min max value)
testParser :: String -> [OutputFormat] -> Assertion
testParser s expected = case (parseStringFormat s) of
2011-06-21 01:33:26 +04:00
Left error -> assertFailure $ show error
Right actual -> assertEqual ("Input: " ++ s) expected actual
tests = test [ formattingTests ++ parserTests ]
formattingTests = [
testFormat (FormatLiteral " ") "" " "
, testFormat (FormatField False Nothing Nothing DescriptionField) "description" "description"
, testFormat (FormatField False (Just 20) Nothing DescriptionField) "description" " description"
, testFormat (FormatField False Nothing (Just 20) DescriptionField) "description" "description"
, testFormat (FormatField True Nothing (Just 20) DescriptionField) "description" "description"
, testFormat (FormatField True (Just 20) Nothing DescriptionField) "description" "description "
, testFormat (FormatField True (Just 20) (Just 20) DescriptionField) "description" "description "
, testFormat (FormatField True Nothing (Just 3) DescriptionField) "description" "des"
2011-06-21 01:33:26 +04:00
]
parserTests = [
testParser "" []
, testParser "D" [FormatLiteral "D"]
, testParser "%(date)" [FormatField False Nothing Nothing DescriptionField]
, testParser "%(total)" [FormatField False Nothing Nothing TotalField]
, testParser "Hello %(date)!" [FormatLiteral "Hello ", FormatField False Nothing Nothing DescriptionField, FormatLiteral "!"]
, testParser "%-(date)" [FormatField True Nothing Nothing DescriptionField]
, testParser "%20(date)" [FormatField False (Just 20) Nothing DescriptionField]
, testParser "%.10(date)" [FormatField False Nothing (Just 10) DescriptionField]
, testParser "%20.10(date)" [FormatField False (Just 20) (Just 10) DescriptionField]
, testParser "%20(account) %.10(total)\n" [ FormatField False (Just 20) Nothing AccountField
2011-06-21 01:33:26 +04:00
, FormatLiteral " "
, FormatField False Nothing (Just 10) TotalField
2011-06-21 01:33:26 +04:00
, FormatLiteral "\n"
]
]