diff --git a/hledger-lib/Hledger/Read.hs b/hledger-lib/Hledger/Read.hs index ff5b0d0ea..530c26483 100644 --- a/hledger-lib/Hledger/Read.hs +++ b/hledger-lib/Hledger/Read.hs @@ -109,6 +109,7 @@ module Hledger.Read ( -- * Misc journalStrictChecks, saveLatestDates, + saveLatestDatesForFiles, -- * Re-exported JournalReader.tmpostingrulep, @@ -132,7 +133,7 @@ import Data.Default (def) import Data.Foldable (asum) import Data.List (group, sort, sortBy) import Data.List.NonEmpty (nonEmpty) -import Data.Maybe (fromMaybe) +import Data.Maybe (catMaybes, fromMaybe) import Data.Ord (comparing) import Data.Semigroup (sconcat) import Data.Text (Text) @@ -243,15 +244,17 @@ readJournal iopts@InputOpts{strict_} mpath txt = do -- readJournalFile :: InputOpts -> PrefixedFilePath -> ExceptT String IO Journal readJournalFile iopts@InputOpts{new_, new_save_} prefixedfile = do - (j,latestdates) <- readJournalFileAndLatestDates iopts prefixedfile + (j, mlatestdates) <- readJournalFileAndLatestDates iopts prefixedfile when (new_ && new_save_) $ liftIO $ - saveLatestDates latestdates (snd $ splitReaderPrefix prefixedfile) + case mlatestdates of + Nothing -> return () + Just (LatestDatesForFile f ds) -> saveLatestDates ds f return j --- The implementation of readJournalFile, but with --new, --- also returns the latest transaction date(s) read. --- Used by readJournalFiles, to save those at the end. -readJournalFileAndLatestDates :: InputOpts -> PrefixedFilePath -> ExceptT String IO (Journal,LatestDates) +-- The implementation of readJournalFile. +-- With --new, it also returns the latest transaction date(s) read from each file. +-- readJournalFiles uses this to update .latest files only after a successful read of all. +readJournalFileAndLatestDates :: InputOpts -> PrefixedFilePath -> ExceptT String IO (Journal, Maybe LatestDatesForFile) readJournalFileAndLatestDates iopts prefixedfile = do let (mfmt, f) = splitReaderPrefix prefixedfile @@ -266,9 +269,9 @@ readJournalFileAndLatestDates iopts prefixedfile = do then do ds <- liftIO $ previousLatestDates f let (newj, newds) = journalFilterSinceLatestDates ds j - return (newj, newds) + return (newj, Just $ LatestDatesForFile f newds) else - return (j, []) + return (j, Nothing) -- | Read a Journal from each specified file path (using @readJournalFile@) -- and combine them into one; or return the first error message. @@ -285,20 +288,20 @@ readJournalFileAndLatestDates iopts prefixedfile = do readJournalFiles :: InputOpts -> [PrefixedFilePath] -> ExceptT String IO Journal readJournalFiles iopts@InputOpts{strict_,new_,new_save_} prefixedfiles = do let iopts' = iopts{strict_=False, new_save_=False} - (j,latestdates) <- + (j, latestdatesforfiles) <- traceOrLogAt 6 ("readJournalFiles: "++show prefixedfiles) $ readJournalFilesAndLatestDates iopts' prefixedfiles when strict_ $ liftEither $ journalStrictChecks j - when (new_ && new_save_) $ liftIO $ - mapM_ (saveLatestDates latestdates . snd . splitReaderPrefix) prefixedfiles + when (new_ && new_save_) $ liftIO $ saveLatestDatesForFiles latestdatesforfiles return j -- The implementation of readJournalFiles, but with --new, -- also returns the latest transaction date(s) read in each file. -- Used by the import command, to save those at the end. -readJournalFilesAndLatestDates :: InputOpts -> [PrefixedFilePath] -> ExceptT String IO (Journal,LatestDates) -readJournalFilesAndLatestDates iopts = - fmap (maybe def sconcat . nonEmpty) . mapM (readJournalFileAndLatestDates iopts) +readJournalFilesAndLatestDates :: InputOpts -> [PrefixedFilePath] -> ExceptT String IO (Journal, [LatestDatesForFile]) +readJournalFilesAndLatestDates iopts pfs = do + (js, lastdates) <- unzip <$> mapM (readJournalFileAndLatestDates iopts) pfs + return (maybe def sconcat $ nonEmpty js, catMaybes lastdates) -- | Run the extra -s/--strict checks on a journal, -- returning the first error message if any of them fail. @@ -371,6 +374,9 @@ newJournalContent = do -- and how many transactions there were on that date. type LatestDates = [Day] +-- The path of an input file, and its current "LatestDates". +data LatestDatesForFile = LatestDatesForFile FilePath LatestDates + -- | Get all instances of the latest date in an unsorted list of dates. -- Ie, if the latest date appears once, return it in a one-element list, -- if it appears three times (anywhere), return three of it. @@ -383,6 +389,10 @@ latestDates = {-# HLINT ignore "Avoid reverse" #-} saveLatestDates :: LatestDates -> FilePath -> IO () saveLatestDates dates f = T.writeFile (latestDatesFileFor f) $ T.unlines $ map showDate dates +-- | Save each file's latest dates. +saveLatestDatesForFiles :: [LatestDatesForFile] -> IO () +saveLatestDatesForFiles = mapM_ (\(LatestDatesForFile f ds) -> saveLatestDates ds f) + -- | What were the latest transaction dates seen the last time this -- journal file was read ? If there were multiple transactions on the -- latest date, that number of dates is returned, otherwise just one. diff --git a/hledger/Hledger/Cli/Commands/Import.hs b/hledger/Hledger/Cli/Commands/Import.hs index db0372bf9..bf9638e3a 100644 --- a/hledger/Hledger/Cli/Commands/Import.hs +++ b/hledger/Hledger/Cli/Commands/Import.hs @@ -54,10 +54,10 @@ importcmd opts@CliOpts{rawopts_=rawopts,inputopts_=iopts} j = do case inputfiles of [] -> error' "please provide one or more input files as arguments" -- PARTIAL: fs -> do - enewjandlatestdates <- runExceptT $ readJournalFilesAndLatestDates iopts' fs - case enewjandlatestdates of + enewjandlatestdatesforfiles <- runExceptT $ readJournalFilesAndLatestDates iopts' fs + case enewjandlatestdatesforfiles of Left err -> error' err - Right (newj, latestdates) -> + Right (newj, latestdatesforfiles) -> case sortOn tdate $ jtxns newj of -- with --dry-run the output should be valid journal format, so messages have ; prepended [] -> do @@ -71,23 +71,27 @@ importcmd opts@CliOpts{rawopts_=rawopts,inputopts_=iopts} j = do newts -> do if dryrun then do - -- first show imported txns + -- show txns to be imported printf "; would import %d new transactions from %s:\n\n" (length newts) inputstr mapM_ (T.putStr . showTransaction) newts + -- then check the whole journal with them added, if in strict mode when (strict_ iopts) $ strictChecks else do -- first check the whole journal with them added, if in strict mode when (strict_ iopts) $ strictChecks - -- then add (append) the transactions to the main journal file + + -- then append the transactions to the main journal file. -- XXX This writes unix line endings (\n), some at least, -- even if the file uses dos line endings (\r\n), which could leave -- mixed line endings in the file. See also writeFileWithBackupIfChanged. foldM_ (`journalAddTransaction` opts) j newts -- gets forced somehow.. (how ?) + printf "imported %d new transactions from %s to %s\n" (length newts) inputstr (journalFilePath j) - -- and finally update the .latest files - mapM_ (saveLatestDates latestdates . snd . splitReaderPrefix) fs + + -- and if we got this far, update each file's .latest file + saveLatestDatesForFiles latestdatesforfiles where -- add the new transactions to the journal in memory and check the whole thing