From 20bfceff2e06c4ab33429f32d46b4d6beb58def2 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Wed, 18 May 2016 15:09:33 -0700 Subject: [PATCH] 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 ? --- hledger-lib/Hledger/Read.hs | 36 ++++++++++++++++++++++------------- hledger/doc/commands.m4.md | 3 ++- hledger/doc/options.m4.md | 8 +++++--- tests/cli/multiple-files.test | 25 +++++++++++++++++++++--- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/hledger-lib/Hledger/Read.hs b/hledger-lib/Hledger/Read.hs index 4a2314f96..e970aef2d 100644 --- a/hledger-lib/Hledger/Read.hs +++ b/hledger-lib/Hledger/Read.hs @@ -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 () diff --git a/hledger/doc/commands.m4.md b/hledger/doc/commands.m4.md index 60595ea67..9089057bf 100644 --- a/hledger/doc/commands.m4.md +++ b/hledger/doc/commands.m4.md @@ -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. diff --git a/hledger/doc/options.m4.md b/hledger/doc/options.m4.md index fd91c79f1..86cb07e09 100644 --- a/hledger/doc/options.m4.md +++ b/hledger/doc/options.m4.md @@ -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 diff --git a/tests/cli/multiple-files.test b/tests/cli/multiple-files.test index b94e314c1..d53c73fbc 100644 --- a/tests/cli/multiple-files.test +++ b/tests/cli/multiple-files.test @@ -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