    {-|

    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, [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{reportspec_ :: CliOpts -> ReportSpec
    reportspec_ = ReportSpec{_rsQuery :: ReportSpec -> Query
    _rsQuery = Query
    query}} Journal
    j = do
      let
        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
          ]

      (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
    $ (CommoditySymbol -> Bool) -> [CommoditySymbol] -> [CommoditySymbol]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Query -> CommoditySymbol -> Bool
    matchesCommodity Query
    query) ([CommoditySymbol] -> [CommoditySymbol])
    -> [CommoditySymbol] -> [CommoditySymbol]
    forall a b. (a -> b) -> a -> b
    $
        case CliOpts -> Maybe UsedOrDeclared
    usedOrDeclaredFromOpts CliOpts
    opts of
          Maybe UsedOrDeclared
    Nothing         -> [CommoditySymbol]
    all'
          Just UsedOrDeclared
    Used       -> [CommoditySymbol]
    used
          Just UsedOrDeclared
    Declared   -> [CommoditySymbol]
    declared'
          Just UsedOrDeclared
    Undeclared -> [CommoditySymbol]
    undeclared
          Just UsedOrDeclared
    Unused     -> [CommoditySymbol]
    unused