.gitignore
[comptalang.git] / cli / Hcompta / CLI / Command / Journals.hs
index 2328235ee46ffb0aae78ce6bb2cfa8652ec2e289..aab5ffcc03d0d0a6453939b281e9af1d8c547db1 100644 (file)
@@ -1,51 +1,63 @@
 {-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
 module Hcompta.CLI.Command.Journals where
 
-import           Control.Monad (Monad(..), forM_, liftM, mapM)
+import           Control.Arrow ((+++))
+import           Control.Monad (Monad(..), liftM, mapM)
 import           Control.Monad.IO.Class (liftIO)
-import           Control.Monad.Trans.Except (runExceptT)
 import           Data.Either (Either(..), partitionEithers)
 import           Data.Foldable (Foldable(..))
+import           Data.Function (($), (.), const)
 import           Data.List ((++))
+import           Data.Maybe (Maybe(..))
 import           Data.Monoid (Monoid(..), (<>))
 import           Data.String (String)
-import           Text.Show (Show)
-import           Prelude (($), Bounded(..), FilePath, IO, flip, unlines)
+import           Prelude (Bounded(..), FilePath, IO, unlines)
 import           System.Console.GetOpt
                   ( ArgDescr(..)
                   , OptDescr(..)
                   , usageInfo )
-import           System.Environment as Env (getProgName)
+import qualified System.Environment as Env
 import           System.Exit (exitSuccess)
 import qualified System.IO as IO
+import           Text.Show (Show)
 
-import           Hcompta.Chart (Chart)
 import qualified Hcompta.CLI.Args as Args
 import qualified Hcompta.CLI.Context as C
-import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
+import qualified Hcompta.CLI.Env as CLI.Env
+import qualified Hcompta.CLI.Format as Format
+import           Hcompta.CLI.Format (Format(..), Formats)
+import           Hcompta.CLI.Format.Ledger ()
+import           Hcompta.CLI.Format.JCC ()
 import qualified Hcompta.CLI.Lang as Lang
 import qualified Hcompta.CLI.Write as Write
+-- import qualified Hcompta.Lib.Parsec as R
+import qualified Hcompta.Format.JCC as JCC
 import qualified Hcompta.Format.Ledger as Ledger
-import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
-import qualified Hcompta.Format.Ledger.Read as Ledger.Read
-import           Hcompta.Lib.Consable (Consable(..))
 import qualified Hcompta.Lib.Leijen as W
 
-data Ctx
- =   Ctx
- { ctx_input              :: [FilePath]
+data Context
+ =   Context
+ { ctx_input        :: [FilePath]
+ , ctx_input_format :: Formats
  } deriving (Show)
 
-nil :: Ctx
-nil =
-       Ctx
-        { ctx_input = []
+context :: Context
+context =
+       Context
+        { ctx_input        = []
+        , ctx_input_format = mempty
         }
 
 usage :: C.Context -> IO String
@@ -62,7 +74,7 @@ usage c = do
                , usageInfo (C.translate c Lang.Section_Options) (options c)
                ]
 
-options :: C.Context -> Args.Options Ctx
+options :: C.Context -> Args.Options Context
 options c =
        [ Option "h" ["help"]
         (NoArg (\_ctx -> do
@@ -74,60 +86,89 @@ options c =
                return $ ctx{ctx_input=s:ctx_input ctx}) $
                C.translate c Lang.Type_File_Journal) $
                C.translate c Lang.Help_Option_Input
+       , Option "if" ["input-format"]
+        (OptArg (\arg ctx -> do
+               ctx_input_format <- case arg of
+                Nothing       -> return $ Format_JCC ()
+                Just "jcc"    -> return $ Format_JCC ()
+                Just "ledger" -> return $ Format_Ledger ()
+                Just _        -> Write.fatal c $
+                       W.text "--input-format option expects \"jcc\", or \"ledger\" as value"
+               return $ ctx{ctx_input_format})
+         "[jcc|ledger]")
+         "input format"
        ]
 
 run :: C.Context -> [String] -> IO ()
 run c args = do
-       (ctx, inputs) <-
-               Args.parse c usage options (nil, args)
-       read_journals <-
-               liftM Data.Either.partitionEithers $ do
-               CLI.Ledger.paths c $ ctx_input ctx ++ inputs
-               >>= do
-                       mapM $ \path -> do
-                               liftIO $ runExceptT $ Ledger.Read.file
-                                (Ledger.Read.context () Ledger.journal)
-                                path
-                               >>= \x -> case x of
-                                Left  ko -> return $ Left (path, ko)
-                                Right ok -> return $ Right ok
-       case read_journals of
-        (errs@(_:_), _journals) ->
-               forM_ errs $ \(_path, err) -> do
-                       Write.fatal c $ err
+       (ctx, inputs) <- Args.parse c usage options (context, args)
+       input_paths <- CLI.Env.paths c $ ctx_input ctx ++ inputs
+       read_journals <- mapM (liftIO . journal_read ctx) input_paths
+       case partitionEithers read_journals of
+        (errs@(_:_), _journals) -> Write.fatals c errs
         ([], journals) -> do
-               let files = ledger_journals ctx journals
-               style_color <- Write.with_color c IO.stdout
+               with_color <- Write.with_color c IO.stdout
                W.displayIO IO.stdout $ do
-               W.renderPretty style_color 1.0 maxBound $ do
-               doc_journals c ctx files
+               W.renderPretty with_color 1.0 maxBound $
+                       W.toDoc () $
+                       mconcat journals
+
+-- * Class 'Journal'
+
+class
+ ( Format.Journal_Monoid (j m)
+ , Format.Journal_Read j
+ ) => Journal j m where
+       journal_files :: j m -> [FilePath]
+
+-- JCC
+instance Journal JCC.Journal Journals_JCC where
+       journal_files j = [JCC.journal_file j]
 
-newtype Journals t = Journals ()
+-- Ledger
+instance Journal Ledger.Journal Journals_Ledger where
+       journal_files = Ledger.journal_files
+
+type Journals_JCC    = ()
+type Journals_Ledger = ()
+
+-- * Type 'Journals'
+
+newtype Journals =
+       Journals [FilePath]
  deriving (Show)
-instance Monoid (Journals t) where
-       mempty = Journals ()
-       mappend _ _ = mempty
-
-instance Consable () Journals t where
-       mcons () _t !_js = mempty
-
-ledger_journals
- :: Ctx
- -> [ Ledger.Journal (Journals (Chart, Ledger.Transaction)) ]
- -> [FilePath]
-ledger_journals _ctx =
-       Data.Foldable.foldl'
-        (flip $ Ledger.Journal.fold
-                (\Ledger.Journal{Ledger.journal_file=f} ->
-                       mappend [f]))
-        mempty
-
-doc_journals
- :: C.Context
- -> Ctx
- -> [FilePath]
- -> W.Doc
-doc_journals _context _ctx =
-       foldr
-        (\file doc -> doc <> W.toDoc () file <> W.line)
-        W.empty
+instance Monoid Journals where
+       mempty = Journals []
+       mappend (Journals x) (Journals y) =
+               Journals (mappend x y)
+       mconcat = foldl' mappend mempty
+instance W.ToDoc () Journals where
+       toDoc () (Journals files) =
+               foldr
+                (\file doc -> doc <> W.toDoc () file <> W.line)
+                W.empty files
+
+type Journal_Read_Cons txn = txn -> ()
+journal_read
+ :: Context -> FilePath
+ -> IO (Either (Format.Message W.Doc) Journals)
+journal_read ctx =
+       case ctx_input_format ctx of
+        Format_JCC () ->
+               let wrap (j::JCC.Journal Journals_JCC) =
+                       Format.journal_fold journals_cons j mempty in
+               let cons :: Journal_Read_Cons (JCC.Charted JCC.Transaction)
+                        = const () in
+               liftM ((+++) Format.Message wrap) .
+               Format.journal_read cons
+        Format_Ledger () ->
+               let wrap (j::Ledger.Journal Journals_Ledger) =
+                       Format.journal_fold journals_cons j mempty in
+               let cons :: Journal_Read_Cons (Ledger.Charted Ledger.Transaction)
+                        = const () in
+               liftM ((+++) Format.Message wrap) .
+               Format.journal_read cons
+
+journals_cons :: Journal j m => j m -> Journals -> Journals
+journals_cons j !(Journals files) =
+       Journals (journal_files j ++ files)