hledger/Utils.hs
Simon Michael a2b8faa4d6 big refactoring, do filtering afresh in each command
We now do data filtering/massage as late as possible, not just once at
startup. This should work better for multiple commands, as with web or ui.
The basic benchmark seems at least as good as before thanks to laziness.
2009-12-21 05:23:07 +00:00

88 lines
3.6 KiB
Haskell

{-|
Utilities for top-level modules and ghci. See also "Ledger.IO" and
"Ledger.Utils".
-}
module Utils
where
import Control.Monad.Error
import Ledger
import Options (Opt(..),ledgerFilePathFromOpts) -- ,optsToFilterSpec)
import System.Directory (doesFileExist)
import System.IO (stderr)
import System.IO.UTF8 (hPutStrLn)
import System.Exit
import System.Cmd (system)
import System.Info (os)
import System.Time (ClockTime,getClockTime)
-- | Parse the user's specified ledger file and run a hledger command on
-- it, or report a parse error. This function makes the whole thing go.
withLedgerDo :: [Opt] -> [String] -> String -> ([Opt] -> [String] -> Ledger -> IO ()) -> IO ()
withLedgerDo opts args cmdname cmd = do
-- We kludgily read the file before parsing to grab the full text, unless
-- it's stdin, or it doesn't exist and we are adding. We read it strictly
-- to let the add command work.
f <- ledgerFilePathFromOpts opts
let f' = if f == "-" then "/dev/null" else f
fileexists <- doesFileExist f
let creating = not fileexists && cmdname == "add"
t <- getCurrentLocalTime
tc <- getClockTime
txt <- if creating then return "" else strictReadFile f'
let runcmd = cmd opts args . mkLedger opts f tc txt
if creating
then runcmd nulljournal
else (runErrorT . parseLedgerFile t) f >>= either parseerror runcmd
where parseerror e = hPutStrLn stderr e >> exitWith (ExitFailure 1)
mkLedger :: [Opt] -> FilePath -> ClockTime -> String -> Journal -> Ledger
mkLedger opts f tc txt j = nullledger{journaltext=txt,journal=j'}
where j' = (canonicaliseAmounts costbasis j){filepath=f,filereadtime=tc}
costbasis=CostBasis `elem` opts
-- | Get a Ledger from the given string and options, or raise an error.
ledgerFromStringWithOpts :: [Opt] -> String -> IO Ledger
ledgerFromStringWithOpts opts s = do
tc <- getClockTime
j <- journalFromString s
return $ mkLedger opts "" tc s j
-- -- | Read a Ledger from the given file, or give an error.
-- readLedgerWithOpts :: [Opt] -> [String] -> FilePath -> IO Ledger
-- readLedgerWithOpts opts args f = do
-- t <- getCurrentLocalTime
-- readLedger f
-- -- | Convert a Journal to a canonicalised, cached and filtered Ledger
-- -- based on the command-line options/arguments and a reference time.
-- filterAndCacheLedgerWithOpts :: [Opt] -> [String] -> LocalTime -> String -> Journal -> Ledger
-- filterAndCacheLedgerWithOpts opts args = filterAndCacheLedger . optsToFilterSpec opts args
-- | Attempt to open a web browser on the given url, all platforms.
openBrowserOn :: String -> IO ExitCode
openBrowserOn u = trybrowsers browsers u
where
trybrowsers (b:bs) u = do
e <- system $ printf "%s %s" b u
case e of
ExitSuccess -> return ExitSuccess
ExitFailure _ -> trybrowsers bs u
trybrowsers [] u = do
putStrLn $ printf "Sorry, I could not start a browser (tried: %s)" $ intercalate ", " browsers
putStrLn $ printf "Please open your browser and visit %s" u
return $ ExitFailure 127
browsers | os=="darwin" = ["open"]
| os=="mingw32" = ["start","firefox","safari","opera","iexplore"]
| otherwise = ["sensible-browser","firefox"]
-- jeffz: write a ffi binding for it using the Win32 package as a basis
-- start by adding System/Win32/Shell.hsc and follow the style of any
-- other module in that directory for types, headers, error handling and
-- what not.
-- ::ShellExecute(NULL, "open", "www.somepage.com", NULL, NULL, SW_SHOWNORMAL);
-- ::ShellExecute(NULL, "open", "firefox.exe", "www.somepage.com" NULL, SW_SHOWNORMAL);