;check: accounts, commodities, payees, ordereddates: improve errors

Error messages for these four are now a bit fancier and more
consistent. But not yet optimised for machine readability.
Cf #1436.

Added to hledger-lib: chomp1, linesPrepend[2].
This commit is contained in:
Simon Michael 2020-12-30 16:31:13 -08:00
parent e59603a04a
commit 31ea37a785
6 changed files with 61 additions and 22 deletions

View File

@ -14,8 +14,17 @@
; salary ; salary
; liabilities ; liabilities
; debts ; debts
account assets:bank:checking
account income:salary
account income:gifts
account assets:bank:saving
account assets:cash
account expenses:food
account expenses:supplies
account liabilities:debts
commodity $
2008/01/01 income 2018/01/01 income
assets:bank:checking $1 assets:bank:checking $1
income:salary income:salary

View File

@ -150,6 +150,7 @@ import Text.Megaparsec.Custom
import Hledger.Data import Hledger.Data
import Hledger.Utils import Hledger.Utils
import Safe (headMay) import Safe (headMay)
import Text.Printf (printf)
--- ** doctest setup --- ** doctest setup
-- $setup -- $setup
@ -376,9 +377,11 @@ journalCheckPayeesDeclared j = sequence_ $ map checkpayee $ jtxns j
where where
checkpayee t checkpayee t
| p `elem` ps = Right () | p `elem` ps = Right ()
| otherwise = | otherwise = Left $
Left $ "undeclared payee \""++T.unpack p++"\"" printf "undeclared payee \"%s\"\nat: %s\n\n%s"
++ "\nin transaction at: "++showGenericSourcePos (tsourcepos t) (T.unpack p)
(showGenericSourcePos $ tsourcepos t)
(linesPrepend2 "> " " " $ chomp1 $ showTransaction t)
where where
p = transactionPayee t p = transactionPayee t
ps = journalPayeesDeclared j ps = journalPayeesDeclared j
@ -390,11 +393,13 @@ journalCheckAccountsDeclared j = sequence_ $ map checkacct $ journalPostings j
where where
checkacct Posting{paccount,ptransaction} checkacct Posting{paccount,ptransaction}
| paccount `elem` as = Right () | paccount `elem` as = Right ()
| otherwise = | otherwise = Left $
Left $ "undeclared account \""++T.unpack paccount++"\"" (printf "undeclared account \"%s\"\n" (T.unpack paccount))
++ case ptransaction of ++ case ptransaction of
Just Transaction{tsourcepos} -> "\nin transaction at: "++showGenericSourcePos tsourcepos Nothing -> ""
Nothing -> "" Just t -> printf "in transaction at: %s\n\n%s"
(showGenericSourcePos $ tsourcepos t)
(linesPrepend " " $ chomp1 $ showTransaction t)
where where
as = journalAccountNamesDeclared j as = journalAccountNamesDeclared j
@ -407,10 +412,13 @@ journalCheckCommoditiesDeclared j =
checkcommodities Posting{..} = checkcommodities Posting{..} =
case mfirstundeclaredcomm of case mfirstundeclaredcomm of
Nothing -> Right () Nothing -> Right ()
Just c -> Left $ "undeclared commodity \""++T.unpack c++"\"" Just c -> Left $
(printf "undeclared commodity \"%s\"\n" (T.unpack c))
++ case ptransaction of ++ case ptransaction of
Just Transaction{tsourcepos} -> "\nin transaction at: "++showGenericSourcePos tsourcepos Nothing -> ""
Nothing -> "" Just t -> printf "in transaction at: %s\n\n%s"
(showGenericSourcePos $ tsourcepos t)
(linesPrepend " " $ chomp1 $ showTransaction t)
where where
mfirstundeclaredcomm = mfirstundeclaredcomm =
headMay $ filter (not . (`elem` cs)) $ catMaybes $ headMay $ filter (not . (`elem` cs)) $ catMaybes $
@ -418,6 +426,7 @@ journalCheckCommoditiesDeclared j =
(map (Just . acommodity) $ amounts pamount) (map (Just . acommodity) $ amounts pamount)
cs = journalCommoditiesDeclared j cs = journalCommoditiesDeclared j
setYear :: Year -> JournalParser m () setYear :: Year -> JournalParser m ()
setYear y = modify' (\j -> j{jparsedefaultyear=Just y}) setYear y = modify' (\j -> j{jparsedefaultyear=Just y})

View File

@ -21,6 +21,7 @@ module Hledger.Utils.String (
lstrip, lstrip,
rstrip, rstrip,
chomp, chomp,
chomp1,
singleline, singleline,
elideLeft, elideLeft,
elideRight, elideRight,
@ -37,6 +38,8 @@ module Hledger.Utils.String (
padright, padright,
cliptopleft, cliptopleft,
fitto, fitto,
linesPrepend,
linesPrepend2,
-- * wide-character-aware layout -- * wide-character-aware layout
charWidth, charWidth,
strWidth, strWidth,
@ -86,10 +89,14 @@ lstrip = dropWhile isSpace
rstrip :: String -> String rstrip :: String -> String
rstrip = reverse . lstrip . reverse rstrip = reverse . lstrip . reverse
-- | Remove trailing newlines/carriage returns. -- | Remove all trailing newlines/carriage returns.
chomp :: String -> String chomp :: String -> String
chomp = reverse . dropWhile (`elem` "\r\n") . reverse chomp = reverse . dropWhile (`elem` "\r\n") . reverse
-- | Remove all trailing newline/carriage returns, leaving just one trailing newline.
chomp1 :: String -> String
chomp1 = (++"\n") . chomp
-- | Remove consecutive line breaks, replacing them with single space -- | Remove consecutive line breaks, replacing them with single space
singleline :: String -> String singleline :: String -> String
singleline = unwords . filter (/="") . (map strip) . lines singleline = unwords . filter (/="") . (map strip) . lines
@ -343,3 +350,14 @@ stripAnsi s = either err id $ regexReplace ansire "" s
where where
err = error "stripAnsi: invalid replacement pattern" -- PARTIAL, shouldn't happen err = error "stripAnsi: invalid replacement pattern" -- PARTIAL, shouldn't happen
ansire = toRegex' "\ESC\\[([0-9]+;)*([0-9]+)?[ABCDHJKfmsu]" -- PARTIAL, should succeed ansire = toRegex' "\ESC\\[([0-9]+;)*([0-9]+)?[ABCDHJKfmsu]" -- PARTIAL, should succeed
-- | Add a prefix to each line of a string.
linesPrepend :: String -> String -> String
linesPrepend prefix = unlines . map (prefix++) . lines
-- | Add a prefix to the first line of a string,
-- and a different prefix to the remaining lines.
linesPrepend2 :: String -> String -> String -> String
linesPrepend2 prefix1 prefix2 s =
unlines $ (prefix1++l) : map (prefix2++) ls
where l:ls = lines s

View File

@ -79,7 +79,7 @@ runCheck copts@CliOpts{rawopts_} j (check,args) =
Payees -> Payees ->
case journalCheckPayeesDeclared j of case journalCheckPayeesDeclared j of
Right () -> return () Right () -> return ()
Left err -> hPutStrLn stderr err >> exitFailure Left err -> hPutStrLn stderr ("Error: "++err) >> exitFailure
where where
-- Hack: append the provided args to the raw opts, -- Hack: append the provided args to the raw opts,
-- in case the check can use them (like checkdates --unique). -- in case the check can use them (like checkdates --unique).

View File

@ -37,13 +37,15 @@ checkdates CliOpts{rawopts_=rawopts,reportspec_=rspec} j = do
FoldAcc{fa_previous=Nothing} -> return () FoldAcc{fa_previous=Nothing} -> return ()
FoldAcc{fa_error=Nothing} -> return () FoldAcc{fa_error=Nothing} -> return ()
FoldAcc{fa_error=Just error, fa_previous=Just previous} -> do FoldAcc{fa_error=Just error, fa_previous=Just previous} -> do
putStrLn $ printf let
("Error: transaction's date is not in date order%s,\n" uniquestr = if unique then " and/or not unique" else ""
++ "at %s:\n\n%sPrevious transaction's date was: %s") positionstr = showGenericSourcePos $ tsourcepos error
(if unique then " and/or not unique" else "") txn1str = linesPrepend " " $ showTransaction previous
(showGenericSourcePos $ tsourcepos error) txn2str = linesPrepend2 "> " " " $ showTransaction error
(showTransaction error) printf "Error: transaction date is out of order%s\nat %s:\n\n%s"
(show $ date previous) uniquestr
positionstr
(txn1str ++ txn2str)
exitFailure exitFailure
data FoldAcc a b = FoldAcc data FoldAcc a b = FoldAcc

View File

@ -28,6 +28,7 @@ checkdupesmode = hledgerCommandMode
checkdupes _opts j = do checkdupes _opts j = do
let dupes = checkdupes' $ accountsNames j let dupes = checkdupes' $ accountsNames j
when (not $ null dupes) $ do when (not $ null dupes) $ do
-- XXX make output more like Checkdates.hs, Check.hs etc.
mapM_ render dupes mapM_ render dupes
exitFailure exitFailure