{-|

The @commodities@ command lists commodity/currency symbols.

-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module Hledger.Cli.Commands.Commodities (
  commoditiesmode
 ,commodities
) where

import Data.Map qualified as M
import Data.Set qualified as S
import Data.Text.IO qualified as T
import System.Console.CmdArgs.Explicit

import Hledger
import Hledger.Cli.CliOptions
import Data.List.Extra (nubSort)
import Data.List ((\\))


-- | Command line options for this command.
commoditiesmode :: Mode RawOpts
commoditiesmode = CommandHelpStr
-> [Flag RawOpts]
-> [(CommandHelpStr, [Flag RawOpts])]
-> [Flag RawOpts]
-> ([Arg RawOpts], Maybe (Arg RawOpts))
-> Mode RawOpts
hledgerCommandMode
  $(embedFileRelative "Hledger/Cli/Commands/Commodities.txt")
  [[CommandHelpStr]
-> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
flagNone [CommandHelpStr
"used"]         (CommandHelpStr -> RawOpts -> RawOpts
setboolopt CommandHelpStr
"used")       CommandHelpStr
"list commodities used"
  ,[CommandHelpStr]
-> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
flagNone [CommandHelpStr
"declared"]     (CommandHelpStr -> RawOpts -> RawOpts
setboolopt CommandHelpStr
"declared")   CommandHelpStr
"list commodities declared"
  ,[CommandHelpStr]
-> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
flagNone [CommandHelpStr
"undeclared"]   (CommandHelpStr -> RawOpts -> RawOpts
setboolopt CommandHelpStr
"undeclared") CommandHelpStr
"list commodities used but not declared"
  ,[CommandHelpStr]
-> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
flagNone [CommandHelpStr
"unused"]       (CommandHelpStr -> RawOpts -> RawOpts
setboolopt CommandHelpStr
"unused")     CommandHelpStr
"list commodities declared but not used"
  ,[CommandHelpStr]
-> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
flagNone [CommandHelpStr
"find"]         (CommandHelpStr -> RawOpts -> RawOpts
setboolopt CommandHelpStr
"find")       CommandHelpStr
"list the first commodity matched by the first argument (a case-insensitive infix regexp)"
  ]
  [(CommandHelpStr, [Flag RawOpts])
generalflagsgroup2]
  [Flag RawOpts]
confflags
  ([], Arg RawOpts -> Maybe (Arg RawOpts)
forall a. a -> Maybe a
Just (Arg RawOpts -> Maybe (Arg RawOpts))
-> Arg RawOpts -> Maybe (Arg RawOpts)
forall a b. (a -> b) -> a -> b
$ CommandHelpStr -> Arg RawOpts
argsFlag CommandHelpStr
"[QUERY..]")

commodities :: CliOpts -> Journal -> IO ()
commodities :: CliOpts -> Journal -> IO ()
commodities opts :: CliOpts
opts@CliOpts{rawopts_ :: CliOpts -> RawOpts
rawopts_=RawOpts
rawopts, reportspec_ :: CliOpts -> ReportSpec
reportspec_=ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query}} Journal
j = do
  let
    filt :: [CommoditySymbol] -> [CommoditySymbol]
filt = (CommoditySymbol -> Bool) -> [CommoditySymbol] -> [CommoditySymbol]
forall a. (a -> Bool) -> [a] -> [a]
filter (Query -> CommoditySymbol -> Bool
matchesCommodity Query
query)
    used :: [CommoditySymbol]
used       = CommandHelpStr -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Show a => CommandHelpStr -> a -> a
dbg5 CommandHelpStr
"used"       ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Set CommoditySymbol -> [CommoditySymbol]
forall a. Set a -> [a]
S.toList (Set CommoditySymbol -> [CommoditySymbol])
-> Set CommoditySymbol -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Journal -> Set CommoditySymbol
journalCommoditiesFromPriceDirectives Journal
j Set CommoditySymbol -> Set CommoditySymbol -> Set CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> Journal -> Set CommoditySymbol
journalCommoditiesFromTransactions Journal
j
    declared' :: [CommoditySymbol]
declared'  = CommandHelpStr -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Show a => CommandHelpStr -> a -> a
dbg5 CommandHelpStr
"declared"   ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Map CommoditySymbol Commodity -> [CommoditySymbol]
forall k a. Map k a -> [k]
M.keys (Map CommoditySymbol Commodity -> [CommoditySymbol])
-> Map CommoditySymbol Commodity -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Journal -> Map CommoditySymbol Commodity
jdeclaredcommodities Journal
j
    unused :: [CommoditySymbol]
unused     = CommandHelpStr -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Show a => CommandHelpStr -> a -> a
dbg5 CommandHelpStr
"unused"     ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ [CommoditySymbol]
declared' [CommoditySymbol] -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Eq a => [a] -> [a] -> [a]
\\ [CommoditySymbol]
used
    undeclared :: [CommoditySymbol]
undeclared = CommandHelpStr -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Show a => CommandHelpStr -> a -> a
dbg5 CommandHelpStr
"undeclared" ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ [CommoditySymbol]
used     [CommoditySymbol] -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Eq a => [a] -> [a] -> [a]
\\ [CommoditySymbol]
declared'
    all' :: [CommoditySymbol]
all'       = CommandHelpStr -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Show a => CommandHelpStr -> a -> a
dbg5 CommandHelpStr
"all"        ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ [CommoditySymbol] -> [CommoditySymbol]
forall a. Ord a => [a] -> [a]
nubSort ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ [[CommoditySymbol]] -> [CommoditySymbol]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [
       Journal -> [CommoditySymbol]
journalCommoditiesDeclared Journal
j
      ,(PriceDirective -> CommoditySymbol)
-> [PriceDirective] -> [CommoditySymbol]
forall a b. (a -> b) -> [a] -> [b]
map PriceDirective -> CommoditySymbol
pdcommodity ([PriceDirective] -> [CommoditySymbol])
-> [PriceDirective] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Journal -> [PriceDirective]
jpricedirectives Journal
j          -- gets the first symbol from P directives
      ,(Amount -> CommoditySymbol) -> [Amount] -> [CommoditySymbol]
forall a b. (a -> b) -> [a] -> [b]
map Amount -> CommoditySymbol
acommodity (Set Amount -> [Amount]
forall a. Set a -> [a]
S.toList (Set Amount -> [Amount]) -> Set Amount -> [Amount]
forall a b. (a -> b) -> a -> b
$ Journal -> Set Amount
journalAmounts Journal
j)  -- includes the second symbol from P directives
      ]
    found :: CommoditySymbol
found = CommandHelpStr -> CommoditySymbol -> CommoditySymbol
forall a. Show a => CommandHelpStr -> a -> a
dbg5 CommandHelpStr
"found" (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ RawOpts -> CommandHelpStr -> [CommoditySymbol] -> CommoditySymbol
findMatchedByArgument RawOpts
rawopts CommandHelpStr
"commodity" [CommoditySymbol]
all'

  (CommoditySymbol -> IO ()) -> [CommoditySymbol] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ CommoditySymbol -> IO ()
T.putStrLn ([CommoditySymbol] -> IO ()) -> [CommoditySymbol] -> IO ()
forall a b. (a -> b) -> a -> b
$
    case CliOpts -> Maybe DeclarablesSelector
declarablesSelectorFromOpts CliOpts
opts of
      Maybe DeclarablesSelector
Nothing         -> [CommoditySymbol] -> [CommoditySymbol]
filt [CommoditySymbol]
all'
      Just DeclarablesSelector
Used       -> [CommoditySymbol] -> [CommoditySymbol]
filt [CommoditySymbol]
used
      Just DeclarablesSelector
Declared   -> [CommoditySymbol] -> [CommoditySymbol]
filt [CommoditySymbol]
declared'
      Just DeclarablesSelector
Undeclared -> [CommoditySymbol] -> [CommoditySymbol]
filt [CommoditySymbol]
undeclared
      Just DeclarablesSelector
Unused     -> [CommoditySymbol] -> [CommoditySymbol]
filt [CommoditySymbol]
unused
      Just DeclarablesSelector
Find       -> [CommoditySymbol
found]