packages feed

csv-to-qif-0.3: main/Main.hs

-----------------------------------------------------------------------------
--
-- Module      :  Main
-- Copyright   :  (c) Ingolf Wagner
-- License     :  BSD3
--
-- Maintainer  :  Ingolf Wagner <palipalo9@googlemail.com>
-- Stability   :  unstable
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------

module Main (main) where

import QifOptions
import Qifer
import Parser
import Data.List.Split

import System.Environment
import System.Console.GetOpt
import System.IO
import Data.Maybe
import System.Exit





-- | the famous main method
main = do
    args <- getArgs

    -- Parse options, getting a list of option actions
    let (actions, nonOptions, errors) = getOpt RequireOrder options args

    -- Here we thread startOptions through all supplied option actions
    opts <- foldl (>>=) (return startOptions) actions

    let Options { optInput    = input
                , optOutput   = output   } = opts

    checkArguments opts
    let rules = rule opts

    parseResult <- parseCSVFromFile (optSkip opts) (optInput opts) (optSeparator opts)
    case parseResult of
        Left error -> do
            hPutStrLn stderr $ error
            exitFailure
        Right csv  -> do

            let actions     = toTransactions rules csv

            updatedActions  <- case (optUpdater opts) of
                    Nothing   ->
                        return $ actions
                    Just file -> do
                        updaterConfig <- readUpdaterFile file
                        return $ update updaterConfig actions

            let qif = transToQif updatedActions

            withFile (optOutput opts) WriteMode (\h ->
                mapM_ ( hPutStrLn h) qif)


-- | reads updater file
readUpdaterFile :: String -> IO [(String,String)]
readUpdaterFile fileName = do
    content <- readFile fileName
    return $ tupler . onlyTuple . splitter . lines $ content
    where
        tupler    = map (\(x:(y:[])) -> (x,y))
        onlyTuple = filter (\l -> length l == 2)
        splitter  = map (\l -> splitOn "<->" l)




-- | this function is unsafe call it after checkArguments
-- @todo : make me safer
rule :: Options -> Rule
rule opts = Rule {
    dateField = fromJust $ optDate opts
    , balanceField = fromJust $ optBalance opts
    , textField = optLongText opts
    , descField = optText opts
    }

-- | checks input arguments for minimum of configuration
checkArguments :: Options -> IO ()
checkArguments opts = do
    case errors of
        [] ->
            return ()
        _  -> do
            mapM_ putStrLn  errors
            exitFailure
            return ()
    where
        errors = catMaybes list
        list =  [ (check optDate "need date column")
                , (check optBalance "need balance column")
                , (checkL optText "need text columns")
                , (checkL optLongText "need long text columns")
                , (checkL optInput "need input file")
                ]
        check getter text = case (getter opts) of
                                Nothing -> Just text
                                Just _  -> Nothing
        checkL getter text = case (getter opts) of
                                []      -> Just text
                                _       -> Nothing