try only as many data readers as needed for a successful read

This commit is contained in:
Simon Michael 2012-03-23 17:13:30 +00:00
parent 6eb7ad28e1
commit fdb3677129

View File

@ -23,10 +23,8 @@ module Hledger.Read (
) )
where where
import Control.Monad.Error import Control.Monad.Error
import Data.Either (partitionEithers)
import Data.List import Data.List
import Data.Maybe import Data.Maybe
import Safe (headDef)
import System.Directory (doesFileExist, getHomeDirectory) import System.Directory (doesFileExist, getHomeDirectory)
import System.Environment (getEnv) import System.Environment (getEnv)
import System.Exit (exitFailure) import System.Exit (exitFailure)
@ -60,7 +58,7 @@ readers = [
] ]
-- | All the data formats we can read. -- | All the data formats we can read.
formats = map rFormat readers -- formats = map rFormat readers
-- | Get the default journal file path specified by the environment. -- | Get the default journal file path specified by the environment.
-- Like ledger, we look first for the LEDGER_FILE environment -- Like ledger, we look first for the LEDGER_FILE environment
@ -96,36 +94,47 @@ readerForFormat s | null rs = Nothing
-- conversion rules may be provided for better conversion of that -- conversion rules may be provided for better conversion of that
-- format, and/or a file path for better error messages. -- format, and/or a file path for better error messages.
readJournal :: Maybe Format -> Maybe CsvReader.CsvRules -> Maybe FilePath -> String -> IO (Either String Journal) readJournal :: Maybe Format -> Maybe CsvReader.CsvRules -> Maybe FilePath -> String -> IO (Either String Journal)
readJournal format rules path s = do readJournal format _ path s =
let readerstotry = case format of Nothing -> readers let readerstotry = case format of Nothing -> readers
Just f -> case readerForFormat f of Just r -> [r] Just f -> case readerForFormat f of Just r -> [r]
Nothing -> [] Nothing -> []
(errors, journals) <- partitionEithers `fmap` mapM (tryReader s path) readerstotry -- XXX lazify in firstSuccessOrBestError $ map tryReader readerstotry
case journals of j:_ -> return $ Right j where
_ -> return $ Left $ bestErrorMsg errors s path path' = fromMaybe "(string)" path
where tryReader :: Reader -> IO (Either String Journal)
path' = fromMaybe "(string)" path tryReader r = do -- printf "trying %s reader\n" (rFormat r)
tryReader :: String -> Maybe FilePath -> Reader -> IO (Either String Journal) (runErrorT . (rParser r) path') s
tryReader s path r = do -- printf "trying to read %s format\n" (rFormat r)
(runErrorT . (rParser r) path') s
-- unknown format -- if no reader succeeds, we return the error of the first;
bestErrorMsg :: [String] -> String -> Maybe FilePath -> String -- ideally it would be the error of the most likely intended
bestErrorMsg [] _ path = printf "could not parse %sdata%s" fmts pathmsg -- reader, based on file suffix and/or data sniffing.
where fmts = case formats of firstSuccessOrBestError :: [IO (Either String Journal)] -> IO (Either String Journal)
[] -> "" firstSuccessOrBestError [] = return $ Left "no readers found"
[f] -> f ++ " " firstSuccessOrBestError attempts = firstSuccessOrBestError' attempts
fs -> intercalate ", " (init fs) ++ " or " ++ last fs ++ " " where
pathmsg = case path of firstSuccessOrBestError' [] = head attempts
Nothing -> "" firstSuccessOrBestError' (a:as) = do
Just p -> " in "++p r <- a
-- one or more errors - report (the most appropriate ?) one case r of Right j -> return $ Right j
bestErrorMsg es s path = printf "could not parse %s data%s\n%s" (rFormat r) pathmsg e Left _ -> firstSuccessOrBestError' as
where (r,e) = headDef (head readers, head es) $ filter detects $ zip readers es
detects (r,_) = (rDetector r) path' s -- -- unknown format
pathmsg = case path of -- bestErrorMsg :: [String] -> String -> Maybe FilePath -> String
Nothing -> "" -- bestErrorMsg [] _ path = printf "could not parse %sdata%s" fmts pathmsg
Just p -> " in "++p -- where fmts = case formats of
-- [] -> ""
-- [f] -> f ++ " "
-- fs -> intercalate ", " (init fs) ++ " or " ++ last fs ++ " "
-- pathmsg = case path of
-- Nothing -> ""
-- Just p -> " in "++p
-- -- one or more errors - report (the most appropriate ?) one
-- bestErrorMsg es s path = printf "could not parse %s data%s\n%s" (rFormat r) pathmsg e
-- where (r,e) = headDef (head readers, head es) $ filter detects $ zip readers es
-- detects (r,_) = (rDetector r) path' s
-- pathmsg = case path of
-- Nothing -> ""
-- Just p -> " in "++p
-- | Read a Journal from this file (or stdin if the filename is -) or -- | Read a Journal from this file (or stdin if the filename is -) or
-- give an error message, using the specified data format or trying -- give an error message, using the specified data format or trying