lib: allow multiple files of different format (#320)

When multiple files are specified with multiple -f options, we now
parse each one individually, rather than just concatenating them, so
they can have different formats.

Directives (like default year or account aliases) no longer carry over
from one file to the next. Limitation or feature ?
This commit is contained in:
Simon Michael 2016-05-18 15:09:33 -07:00
parent fdd7feefdb
commit 20bfceff2e
4 changed files with 52 additions and 20 deletions

View File

@ -124,7 +124,7 @@ tryReaders readers mrulesfile assrt path s = firstSuccessOrBestError [] readers
-- A CSV conversion rules file may also be specified for use by the CSV reader.
-- Also there is a flag specifying whether to check or ignore balance assertions in the journal.
readJournal :: Maybe StorageFormat -> Maybe FilePath -> Bool -> Maybe FilePath -> String -> IO (Either String Journal)
readJournal mformat mrulesfile assrt path s = tryReaders (readersFor (mformat, path, s)) mrulesfile assrt path s
readJournal mformat mrulesfile assrt mpath s = tryReaders (readersFor (mformat, mpath, s)) mrulesfile assrt mpath s
-- | Read a Journal from this file (or stdin if the filename is -) or give
-- an error message, using the specified data format or trying all known
@ -132,20 +132,30 @@ readJournal mformat mrulesfile assrt path s = tryReaders (readersFor (mformat, p
-- conversion of that format. Also there is a flag specifying whether
-- to check or ignore balance assertions in the journal.
readJournalFile :: Maybe StorageFormat -> Maybe FilePath -> Bool -> FilePath -> IO (Either String Journal)
readJournalFile format rulesfile assrt f = readJournalFiles format rulesfile assrt [f]
readJournalFile mformat mrulesfile assrt f =
readFileOrStdinAnyNewline f >>= readJournal mformat mrulesfile assrt (Just f)
-- | Read the given file, or standard input if the path is "-".
readFileOrStdinAnyNewline :: String -> IO String
readFileOrStdinAnyNewline f = do
requireJournalFileExists f
h <- fileHandle f
hSetNewlineMode h universalNewlineMode
hGetContents h
where
fileHandle "-" = return stdin
fileHandle f = openFile f ReadMode
-- | Call readJournalFile on each specified file path, and combine the
-- resulting journals into one. If there are any errors, the first is
-- returned, otherwise they are combined per Journal's monoid instance
-- (concatenated, basically). Currently the parse state (eg directives
-- & aliases in effect) resets at the start of each file (though the
-- final parse states from all files are combined at the end).
readJournalFiles :: Maybe StorageFormat -> Maybe FilePath -> Bool -> [FilePath] -> IO (Either String Journal)
readJournalFiles format rulesfile assrt fs = do
contents <- fmap concat $ mapM readFileAnyNewline fs
readJournal format rulesfile assrt (listToMaybe fs) contents
where
readFileAnyNewline f = do
requireJournalFileExists f
h <- fileHandle f
hSetNewlineMode h universalNewlineMode
hGetContents h
fileHandle "-" = return stdin
fileHandle f = openFile f ReadMode
readJournalFiles mformat mrulesfile assrt fs = do
(either Left (Right . mconcat) . sequence)
<$> mapM (readJournalFile mformat mrulesfile assrt) fs
-- | If the specified journal file does not exist, give a helpful error and quit.
requireJournalFileExists :: FilePath -> IO ()

View File

@ -108,7 +108,8 @@ Prompt for transactions and add them to the journal.
Many hledger users edit their journals directly with a text editor, or generate them from CSV.
For more interactive data entry, there is the `add` command,
which prompts interactively on the console for new transactions, and appends
them to the journal file (existing transactions are not changed).
them to the journal file (if there are multiple `-f FILE` options, the first file is used.)
Existing transactions are not changed.
This is the only hledger command that writes to the journal file.
To use it, just run `hledger add` and follow the prompts.

View File

@ -110,9 +110,11 @@ Both of these must be written after the command name.
## Multiple files
One may specify the `--file FILE` option multiple times. This is equivalent to
concatenating the files to standard input and passing `--file -`, except that
the add command functions normally and adds entries to the first specified file.
You can specify multiple `-f/--file FILE` options. This is like
combining all the files into one, except they can have different formats.
Also directives and aliases in one file do not affect subsequent files
(if you need that, use the [include directive](#including-other-files)
instead).
## Repeated options

View File

@ -20,7 +20,7 @@ Total:
>>>2
>>>=0
# 2. aliases in files should only apply to later files
# 2. aliases etc. in files currently don't carry over to subsequent files
hledger print -f personal.journal -f business.journal -f alias.journal -f personal.journal
>>>
2014/01/01
@ -32,8 +32,27 @@ hledger print -f personal.journal -f business.journal -f alias.journal -f person
assets:cash $-1
2014/01/02
equity:draw:personal:food $1
assets:personal:cash $-1
expenses:food $1
assets:cash $-1
>>>2
>>>=0
# 2014/01/02
# equity:draw:personal:food $1
# assets:personal:cash $-1
# 3. files can be of different formats
hledger print -f personal.journal -f a.timeclock -f b.timedot
>>>
2014/01/02
expenses:food $1
assets:cash $-1
2016/01/01 * 12:00-16:00
(a:aa) 4.00h
2016/01/01 *
(b.bb) 1.00
>>>=0