hledger-lib 1.50.1 → 1.50.2
raw patch · 5 files changed
+52/−40 lines, 5 filesdep ~hashtablesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: hashtables
API changes (from Hackage documentation)
+ Hledger.Read.RulesReader: getRulesFile :: FilePath -> Maybe FilePath -> FilePath
+ Hledger.Read.RulesReader: readRules :: FilePath -> ExceptT String IO CsvRules
+ Hledger.Read.RulesReader: rulesEncoding :: CsvRules -> ExceptT String IO (Maybe DynEncoding)
- Hledger.Read.RulesReader: readJournalFromCsv :: Maybe (Either CsvRules FilePath) -> FilePath -> Handle -> Maybe SepFormat -> ExceptT String IO Journal
+ Hledger.Read.RulesReader: readJournalFromCsv :: CsvRules -> FilePath -> Text -> Maybe SepFormat -> ExceptT String IO Journal
Files
- CHANGES.md +2/−0
- Hledger/Read/CsvReader.hs +6/−3
- Hledger/Read/RulesReader.hs +38/−32
- Hledger/Utils/IO.hs +2/−1
- hledger-lib.cabal +4/−4
CHANGES.md view
@@ -17,6 +17,8 @@ For user-visible changes, see the hledger package changelog. +# 1.50.2 2025-09-26+ # 1.50.1 2025-09-16 # 1.50 2025-09-03
Hledger/Read/CsvReader.hs view
@@ -33,7 +33,8 @@ import Hledger.Data import Hledger.Utils import Hledger.Read.Common (aliasesFromOpts, Reader(..), InputOpts(..), journalFinalise)-import Hledger.Read.RulesReader (readJournalFromCsv)+import Hledger.Read.RulesReader (readJournalFromCsv, getRulesFile, rulesEncoding, readRules)+import Control.Monad.Trans (lift) --- ** doctest setup -- $setup@@ -60,8 +61,10 @@ -- This does not check balance assertions. parse :: SepFormat -> InputOpts -> FilePath -> Handle -> ExceptT String IO Journal parse sep iopts f h = do- let mrulesfile = mrules_file_ iopts- readJournalFromCsv (Right <$> mrulesfile) f h (Just sep)+ rules <- readRules $ getRulesFile f (mrules_file_ iopts)+ mencoding <- rulesEncoding rules+ csvtext <- lift $ readHandlePortably' mencoding h+ readJournalFromCsv rules f csvtext (Just sep) -- apply any command line account aliases. Can fail with a bad replacement pattern. >>= liftEither . journalApplyAliases (aliasesFromOpts iopts) -- journalFinalise assumes the journal's items are
Hledger/Read/RulesReader.hs view
@@ -29,13 +29,12 @@ -- * Reader reader, -- * Misc.- readJournalFromCsv,- -- readRulesFile,- -- parseCsvRules,- -- validateCsvRules,- -- CsvRules, dataFileFor, rulesFileFor,+ getRulesFile,+ readRules,+ rulesEncoding,+ readJournalFromCsv, parseBalanceAssertionType, -- * Tests tests_RulesReader,@@ -59,7 +58,7 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.Csv as Cassava import qualified Data.Csv.Parser.Megaparsec as CassavaMegaparsec-import Data.Encoding (encodingFromStringExplicit)+import Data.Encoding (encodingFromStringExplicit, DynEncoding) import Data.Either (fromRight) import Data.Functor ((<&>)) import Data.List (elemIndex, mapAccumL, nub, sortOn)@@ -168,7 +167,7 @@ -- gives: file pattern, data cleaning/generating command, archive flag -- XXX higher-than usual logging priority for file reading (normally 6 or 7), to bypass excessive noise from elsewhere- rules <- readRulesFile $ dbg1 "reading rules file" rulesfile+ rules <- readRules $ dbg1 "reading rules file" rulesfile let msourcearg = getDirective "source" rules -- Nothing -> error' $ rulesfile ++ " source rule must specify a file pattern or a command"@@ -219,7 +218,7 @@ (Nothing, _) -> return () -- 5. read raw, cleaned or generated data- -- needs: file pattern, data file, data command+ -- needs: file pattern, data file, optional data file encoding, data command -- gives: clean data (possibly empty) mexistingdatafile <- maybe (return Nothing) (\f -> liftIO $ do@@ -233,9 +232,10 @@ return "" -- file found, and maybe a data cleaning command- (_, Just f, mc) -> -- trace "file found" $ + (_, Just f, mc) -> do -- trace "file found" $+ mencoding <- rulesEncoding rules liftIO $ do- raw <- openFileOrStdin f >>= readHandlePortably+ raw <- openFileOrStdin f >>= readHandlePortably' mencoding maybe (return raw) (\c -> runCommandAsFilter rulesfile (dbg0Msg ("running: "++c) c) raw) mc -- no file pattern, but a data generating command@@ -247,12 +247,11 @@ error' $ rulesfile ++ " source rule must specify a file pattern or a command" -- 6. convert the clean data to a (possibly empty) journal- -- needs: clean data, rules, rules file, data file if any+ -- needs: clean data, rules, data file if any -- gives: journal j <- do- cleandatah <- liftIO $ inputToHandle cleandata- readJournalFromCsv (Just $ Left rules) (fromMaybe "(cmd)" mdatafile) cleandatah Nothing+ readJournalFromCsv rules (fromMaybe "(cmd)" mdatafile) cleandata Nothing -- apply any command line account aliases. Can fail with a bad replacement pattern. >>= liftEither . journalApplyAliases (aliasesFromOpts iopts) -- journalFinalise assumes the journal's items are@@ -389,15 +388,37 @@ rulesFileFor :: FilePath -> FilePath rulesFileFor = (++ ".rules") +-- | Return the given rules file path, or if none is given,+-- the default rules file for the given csv file;+-- or if the csv file is "-", raise an error.+getRulesFile :: FilePath -> Maybe FilePath -> FilePath+getRulesFile csvfile mrulesfile =+ case mrulesfile of+ Nothing | csvfile == "-" ->+ error' "please use --rules when reading CSV from stdin" -- PARTIAL+ -- XXX is this bad ? everything else here uses ExceptT+ Nothing -> rulesFileFor csvfile+ Just f -> f+ -- | An exception-throwing IO action that reads and validates -- the specified CSV rules file (which may include other rules files).-readRulesFile :: FilePath -> ExceptT String IO CsvRules-readRulesFile f =+readRules :: FilePath -> ExceptT String IO CsvRules+readRules f = liftIO (do dbg6IO "using conversion rules file" f readFilePortably f >>= expandIncludes (takeDirectory f) ) >>= either throwError return . parseAndValidateCsvRules f +-- | Read the encoding specified by the @encoding@ rule, if any.+-- Or throw an error if an unrecognised encoding is specified.+rulesEncoding :: CsvRules -> ExceptT String IO (Maybe DynEncoding)+rulesEncoding rules = do+ case T.unpack <$> getDirective "encoding" rules of+ Nothing -> return Nothing+ Just encstr -> case encodingFromStringExplicit $ dbg4 "encoding name" encstr of+ Nothing -> throwError $ "Invalid encoding: " <> encstr+ Just enc -> return . Just $ dbg4 "encoding" enc+ -- | Inline all files referenced by include directives in this hledger CSV rules text, recursively. -- Included file paths may be relative to the directory of the provided file path. -- Unlike with journal files, this is done as a pre-parse step to simplify the CSV rules parser.@@ -1167,26 +1188,11 @@ -- -- 4. Return the transactions as a Journal. ---readJournalFromCsv :: Maybe (Either CsvRules FilePath) -> FilePath -> Handle -> Maybe SepFormat -> ExceptT String IO Journal-readJournalFromCsv Nothing "-" h _ = lift (hClose h) *> throwError "please use --rules when reading CSV from stdin"-readJournalFromCsv merulesfile csvfile csvhandle sep = do+readJournalFromCsv :: CsvRules -> FilePath -> Text -> Maybe SepFormat -> ExceptT String IO Journal+readJournalFromCsv rules csvfile csvtext sep = do -- for now, correctness is the priority here, efficiency not so much - rules <- case merulesfile of- Just (Left rs) -> return rs- Just (Right rulesfile) -> readRulesFile rulesfile- Nothing -> readRulesFile $ rulesFileFor csvfile dbg6IO "csv rules" rules-- -- read csv while being aware of the encoding- mencoding <- do- -- XXX higher-than usual debug level for file reading to bypass excessive noise from elsewhere, normally 6 or 7- case T.unpack <$> getDirective "encoding" rules of- Just rawenc -> case encodingFromStringExplicit $ dbg4 "raw-encoding" rawenc of- Just enc -> return . Just $ dbg4 "encoding" enc- Nothing -> throwError $ "Invalid encoding: " <> rawenc- Nothing -> return Nothing- csvtext <- lift $ readHandlePortably' mencoding csvhandle -- convert the csv data to lines and remove all empty/blank lines let csvlines1 = dbg9 "csvlines1" $ filter (not . T.null . T.strip) $ dbg9 "csvlines0" $ T.lines csvtext
Hledger/Utils/IO.hs view
@@ -341,7 +341,8 @@ noencoding = map toLower enc == "ascii" msg = unlines $ [ rstrip $ show ex- , "Some text could not be decoded with the system text encoding, " <> enc <> "."+ , "Some text could not be decoded with the system's text encoding, " <> enc+ , "(or, the text encoding specified by CSV rules)." ] ++ if noencoding then [
hledger-lib.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: hledger-lib-version: 1.50.1+version: 1.50.2 synopsis: A library providing the core functionality of hledger description: This library contains hledger's core functionality. It is used by most hledger* packages so that they support the same@@ -148,7 +148,7 @@ , extra >=1.7.11 , file-embed >=0.0.10 , filepath- , hashtables >=1.2.3.1+ , hashtables >=1.2.3.1 && <1.3 || >=1.4.0 , lucid , megaparsec >=7.0.0 && <9.8 , microlens >=0.4@@ -208,7 +208,7 @@ , extra >=1.7.11 , file-embed >=0.0.10 , filepath- , hashtables >=1.2.3.1+ , hashtables >=1.2.3.1 && <1.3 || >=1.4.0 , lucid , megaparsec >=7.0.0 && <9.8 , microlens >=0.4@@ -269,7 +269,7 @@ , extra >=1.7.11 , file-embed >=0.0.10 , filepath- , hashtables >=1.2.3.1+ , hashtables >=1.2.3.1 && <1.3 || >=1.4.0 , hledger-lib , lucid , megaparsec >=7.0.0 && <9.8