Allow separator to be overridden from command line
This also fixes that parsing separators on the command line and the rules file was not handled the same way.
This commit is contained in:
parent
b524a088c7
commit
02d9205af7
@ -50,6 +50,7 @@ module Hledger.Read.Common (
|
|||||||
getAccountAliases,
|
getAccountAliases,
|
||||||
clearAccountAliases,
|
clearAccountAliases,
|
||||||
journalAddFile,
|
journalAddFile,
|
||||||
|
getSpecialSeparators,
|
||||||
|
|
||||||
-- * parsers
|
-- * parsers
|
||||||
-- ** transaction bits
|
-- ** transaction bits
|
||||||
@ -162,7 +163,7 @@ data InputOpts = InputOpts {
|
|||||||
mformat_ :: Maybe StorageFormat -- ^ a file/storage format to try, unless overridden
|
mformat_ :: Maybe StorageFormat -- ^ a file/storage format to try, unless overridden
|
||||||
-- by a filename prefix. Nothing means try all.
|
-- by a filename prefix. Nothing means try all.
|
||||||
,mrules_file_ :: Maybe FilePath -- ^ a conversion rules file to use (when reading CSV)
|
,mrules_file_ :: Maybe FilePath -- ^ a conversion rules file to use (when reading CSV)
|
||||||
,separator_ :: Char -- ^ the separator to use (when reading CSV)
|
,separator_ :: Maybe Char -- ^ the separator to use (when reading CSV)
|
||||||
,aliases_ :: [String] -- ^ account name aliases to apply
|
,aliases_ :: [String] -- ^ account name aliases to apply
|
||||||
,anon_ :: Bool -- ^ do light anonymisation/obfuscation of the data
|
,anon_ :: Bool -- ^ do light anonymisation/obfuscation of the data
|
||||||
,ignore_assertions_ :: Bool -- ^ don't check balance assertions
|
,ignore_assertions_ :: Bool -- ^ don't check balance assertions
|
||||||
@ -175,14 +176,14 @@ data InputOpts = InputOpts {
|
|||||||
instance Default InputOpts where def = definputopts
|
instance Default InputOpts where def = definputopts
|
||||||
|
|
||||||
definputopts :: InputOpts
|
definputopts :: InputOpts
|
||||||
definputopts = InputOpts def def ',' def def def def True def def
|
definputopts = InputOpts def def def def def def def True def def
|
||||||
|
|
||||||
rawOptsToInputOpts :: RawOpts -> InputOpts
|
rawOptsToInputOpts :: RawOpts -> InputOpts
|
||||||
rawOptsToInputOpts rawopts = InputOpts{
|
rawOptsToInputOpts rawopts = InputOpts{
|
||||||
-- files_ = listofstringopt "file" rawopts
|
-- files_ = listofstringopt "file" rawopts
|
||||||
mformat_ = Nothing
|
mformat_ = Nothing
|
||||||
,mrules_file_ = maybestringopt "rules-file" rawopts
|
,mrules_file_ = maybestringopt "rules-file" rawopts
|
||||||
,separator_ = fromMaybe ',' (maybecharopt "separator" rawopts)
|
,separator_ = maybestringopt "separator" rawopts >>= getSpecialSeparators
|
||||||
,aliases_ = listofstringopt "alias" rawopts
|
,aliases_ = listofstringopt "alias" rawopts
|
||||||
,anon_ = boolopt "anon" rawopts
|
,anon_ = boolopt "anon" rawopts
|
||||||
,ignore_assertions_ = boolopt "ignore-assertions" rawopts
|
,ignore_assertions_ = boolopt "ignore-assertions" rawopts
|
||||||
@ -194,6 +195,14 @@ rawOptsToInputOpts rawopts = InputOpts{
|
|||||||
|
|
||||||
--- * parsing utilities
|
--- * parsing utilities
|
||||||
|
|
||||||
|
-- | Parse special separator names TAB and SPACE, or return the first
|
||||||
|
-- character. Return Nothing on empty string
|
||||||
|
getSpecialSeparators :: String -> Maybe Char
|
||||||
|
getSpecialSeparators "SPACE" = Just ' '
|
||||||
|
getSpecialSeparators "TAB" = Just '\t'
|
||||||
|
getSpecialSeparators (x:_) = Just x
|
||||||
|
getSpecialSeparators [] = Nothing
|
||||||
|
|
||||||
-- | Run a text parser in the identity monad. See also: parseWithState.
|
-- | Run a text parser in the identity monad. See also: parseWithState.
|
||||||
runTextParser, rtp
|
runTextParser, rtp
|
||||||
:: TextParser Identity a -> Text -> Either (ParseErrorBundle Text CustomErr) a
|
:: TextParser Identity a -> Text -> Either (ParseErrorBundle Text CustomErr) a
|
||||||
|
|||||||
@ -71,7 +71,7 @@ import Text.Printf (printf)
|
|||||||
|
|
||||||
import Hledger.Data
|
import Hledger.Data
|
||||||
import Hledger.Utils
|
import Hledger.Utils
|
||||||
import Hledger.Read.Common (Reader(..),InputOpts(..),amountp, statusp, genericSourcePos, finaliseJournal)
|
import Hledger.Read.Common (Reader(..),InputOpts(..),amountp, statusp, genericSourcePos, getSpecialSeparators, finaliseJournal)
|
||||||
|
|
||||||
type CSV = [Record]
|
type CSV = [Record]
|
||||||
|
|
||||||
@ -104,13 +104,13 @@ parse iopts f t = do
|
|||||||
-- better preemptively reverse them once more. XXX inefficient
|
-- better preemptively reverse them once more. XXX inefficient
|
||||||
pj' = journalReverse pj
|
pj' = journalReverse pj
|
||||||
|
|
||||||
getSeparatorFromRules :: Char -> CsvRules -> Char
|
-- | Decide which separator to get.
|
||||||
getSeparatorFromRules defaultSeparator rules =
|
-- If the external separator is provided, take it. Otherwise, look at the rules. Finally, return ','.
|
||||||
maybe defaultSeparator id (getSeparator <$> getDirective "separator" rules)
|
getSeparator :: Maybe Char -> CsvRules -> Char
|
||||||
where getSeparator :: String -> Char
|
getSeparator externalSeparator rules = head $
|
||||||
getSeparator "SPACE" = ' '
|
catMaybes [ externalSeparator
|
||||||
getSeparator "TAB" = '\t'
|
, getDirective "separator" rules >>= getSpecialSeparators
|
||||||
getSeparator x = head x
|
, Just ',']
|
||||||
|
|
||||||
-- | Read a Journal from the given CSV data (and filename, used for error
|
-- | Read a Journal from the given CSV data (and filename, used for error
|
||||||
-- messages), or return an error. Proceed as follows:
|
-- messages), or return an error. Proceed as follows:
|
||||||
@ -123,9 +123,9 @@ getSeparatorFromRules defaultSeparator rules =
|
|||||||
-- 4. if the rules file didn't exist, create it with the default rules and filename
|
-- 4. if the rules file didn't exist, create it with the default rules and filename
|
||||||
-- 5. return the transactions as a Journal
|
-- 5. return the transactions as a Journal
|
||||||
-- @
|
-- @
|
||||||
readJournalFromCsv :: Char -> Maybe FilePath -> FilePath -> Text -> IO (Either String Journal)
|
readJournalFromCsv :: Maybe Char -> Maybe FilePath -> FilePath -> Text -> IO (Either String Journal)
|
||||||
readJournalFromCsv _ Nothing "-" _ = return $ Left "please use --rules-file when reading CSV from stdin"
|
readJournalFromCsv _ Nothing "-" _ = return $ Left "please use --rules-file when reading CSV from stdin"
|
||||||
readJournalFromCsv separator mrulesfile csvfile csvdata =
|
readJournalFromCsv commandLineSeparator mrulesfile csvfile csvdata =
|
||||||
handle (\(e::IOException) -> return $ Left $ show e) $ do
|
handle (\(e::IOException) -> return $ Left $ show e) $ do
|
||||||
|
|
||||||
-- make and throw an IO exception.. which we catch and convert to an Either above ?
|
-- make and throw an IO exception.. which we catch and convert to an Either above ?
|
||||||
@ -156,7 +156,7 @@ readJournalFromCsv separator mrulesfile csvfile csvdata =
|
|||||||
records <- (either throwerr id .
|
records <- (either throwerr id .
|
||||||
dbg2 "validateCsv" . validateCsv rules skiplines .
|
dbg2 "validateCsv" . validateCsv rules skiplines .
|
||||||
dbg2 "parseCsv")
|
dbg2 "parseCsv")
|
||||||
`fmap` parseCsv (getSeparatorFromRules separator rules) parsecfilename csvdata
|
`fmap` parseCsv (getSeparator commandLineSeparator rules) parsecfilename csvdata
|
||||||
dbg1IO "first 3 csv records" $ take 3 records
|
dbg1IO "first 3 csv records" $ take 3 records
|
||||||
|
|
||||||
-- identify header lines
|
-- identify header lines
|
||||||
|
|||||||
@ -527,7 +527,7 @@ $ ./hledger-csv
|
|||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|
||||||
# 25. specify alternative delimiter in rules
|
# 25. specify alternative whitespace delimiter in rules
|
||||||
<
|
<
|
||||||
2009/10/01 Flubber Co 50 123
|
2009/10/01 Flubber Co 50 123
|
||||||
|
|
||||||
@ -540,6 +540,39 @@ separator TAB
|
|||||||
$ ./hledger-csv
|
$ ./hledger-csv
|
||||||
2009/10/01 Flubber Co
|
2009/10/01 Flubber Co
|
||||||
(assets:myacct) $50 = $123
|
(assets:myacct) $50 = $123
|
||||||
|
|
||||||
|
>=0
|
||||||
|
|
||||||
|
# 26. specify char delimiter in rules
|
||||||
|
<
|
||||||
|
2009/10/01;Flubber Co;50;123
|
||||||
|
|
||||||
|
RULES
|
||||||
|
fields date, description, amount, balance
|
||||||
|
currency $
|
||||||
|
account1 (assets:myacct)
|
||||||
|
separator ;
|
||||||
|
|
||||||
|
$ ./hledger-csv
|
||||||
|
2009/10/01 Flubber Co
|
||||||
|
(assets:myacct) $50 = $123
|
||||||
|
|
||||||
|
>=0
|
||||||
|
|
||||||
|
# 27. command line delimiter overrides configuration file
|
||||||
|
<
|
||||||
|
2009/10/01 Flubber Co 50 123
|
||||||
|
|
||||||
|
RULES
|
||||||
|
fields date, description, amount, balance
|
||||||
|
currency $
|
||||||
|
account1 (assets:myacct)
|
||||||
|
separator ;
|
||||||
|
|
||||||
|
$ ./hledger-csv --separator 'TAB'
|
||||||
|
2009/10/01 Flubber Co
|
||||||
|
(assets:myacct) $50 = $123
|
||||||
|
|
||||||
>=0
|
>=0
|
||||||
|
|
||||||
## 25. A single unbalanced posting with number other than 1 also should not generate a balancing posting.
|
## 25. A single unbalanced posting with number other than 1 also should not generate a balancing posting.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user