{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
{- | CLI application harness.

-}
module Console.BnbStaking.Main
    ( run
    , getArgs
    , Args(..)
    ) where
import           Control.Monad                  ( filterM )
import           Data.Maybe                     ( fromMaybe )
import           Data.Time                      ( localDay
                                                , toGregorian
                                                , utcToLocalZonedTime
                                                , zonedTimeToLocalTime
                                                )
import           Data.Version                   ( showVersion )
import           Network.HTTP.Req               ( defaultHttpConfig
                                                , runReq
                                                )
import           System.Console.CmdArgs         ( (&=)
                                                , Data
                                                , Typeable
                                                , argPos
                                                , cmdArgs
                                                , def
                                                , explicit
                                                , help
                                                , helpArg
                                                , name
                                                , program
                                                , summary
                                                , typ
                                                )

import           Console.BnbStaking.Api         ( Reward(rRewardTime)
                                                , getAllRewards
                                                )
import           Console.BnbStaking.CoinTracking
                                                ( makeCoinTrackingImport )
import           Console.BnbStaking.Csv         ( makeCsvContents )
import           Paths_bnb_staking_csvs         ( version )

import qualified Data.ByteString.Lazy.Char8    as LBC
import qualified Data.Text                     as T

-- | Run the executable - parsing args, making queries, & printing the
-- results.
run :: Args -> IO ()
run :: Args -> IO ()
run Args {Bool
String
Maybe Integer
Maybe String
argYear :: Args -> Maybe Integer
argCoinTracking :: Args -> Bool
argOutputFile :: Args -> Maybe String
argPubKey :: Args -> String
argYear :: Maybe Integer
argCoinTracking :: Bool
argOutputFile :: Maybe String
argPubKey :: String
..} = do
    [Reward]
rewards <- HttpConfig -> Req [Reward] -> IO [Reward]
forall (m :: * -> *) a. MonadIO m => HttpConfig -> Req a -> m a
runReq HttpConfig
defaultHttpConfig (Req [Reward] -> IO [Reward]) -> Req [Reward] -> IO [Reward]
forall a b. (a -> b) -> a -> b
$ Text -> Req [Reward]
forall (m :: * -> *). MonadHttp m => Text -> m [Reward]
getAllRewards (Text -> Req [Reward]) -> Text -> Req [Reward]
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
argPubKey
    let outputFile :: String
outputFile = String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe String
"-" Maybe String
argOutputFile
    if Bool
argCoinTracking
        then String -> String -> [Reward] -> IO ()
makeCoinTrackingImport String
outputFile String
argPubKey [Reward]
rewards
        else do
            [Reward]
filteredRewards <- IO [Reward]
-> (Integer -> IO [Reward]) -> Maybe Integer -> IO [Reward]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
                ([Reward] -> IO [Reward]
forall (m :: * -> *) a. Monad m => a -> m a
return [Reward]
rewards)
                (\Integer
year -> (Reward -> IO Bool) -> [Reward] -> IO [Reward]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM
                    (\Reward
reward -> do
                        ZonedTime
rewardTime <- UTCTime -> IO ZonedTime
utcToLocalZonedTime (UTCTime -> IO ZonedTime) -> UTCTime -> IO ZonedTime
forall a b. (a -> b) -> a -> b
$ Reward -> UTCTime
rRewardTime Reward
reward
                        Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return
                            (Bool -> IO Bool) -> (LocalTime -> Bool) -> LocalTime -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\(Integer
y, Int
_, Int
_) -> Integer
y Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
year)
                            ((Integer, Int, Int) -> Bool)
-> (LocalTime -> (Integer, Int, Int)) -> LocalTime -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> (Integer, Int, Int)
toGregorian
                            (Day -> (Integer, Int, Int))
-> (LocalTime -> Day) -> LocalTime -> (Integer, Int, Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LocalTime -> Day
localDay
                            (LocalTime -> IO Bool) -> LocalTime -> IO Bool
forall a b. (a -> b) -> a -> b
$ ZonedTime -> LocalTime
zonedTimeToLocalTime ZonedTime
rewardTime
                    )
                    [Reward]
rewards
                )
                Maybe Integer
argYear
            ByteString
output <- [Reward] -> IO ByteString
makeCsvContents [Reward]
filteredRewards
            if String
outputFile String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"-"
                then ByteString -> IO ()
LBC.putStr ByteString
output
                else String -> ByteString -> IO ()
LBC.writeFile String
outputFile ByteString
output


-- | CLI arguments supported by the executable.
data Args = Args
    { Args -> String
argPubKey       :: String
    -- ^ BinanceChain account's pubkey.
    , Args -> Maybe String
argOutputFile   :: Maybe String
    -- ^ Optional output file. 'Nothing' or @'Just' "-"@ will print to
    -- 'System.IO.stdout'.
    , Args -> Bool
argCoinTracking :: Bool
    -- ^ Flag to enable writing/printing files formatted for CoinTracking
    -- Bulk Imports.
    , Args -> Maybe Integer
argYear         :: Maybe Integer
    -- ^ Year for limiting the output.
    }
    deriving (Int -> Args -> ShowS
[Args] -> ShowS
Args -> String
(Int -> Args -> ShowS)
-> (Args -> String) -> ([Args] -> ShowS) -> Show Args
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Args] -> ShowS
$cshowList :: [Args] -> ShowS
show :: Args -> String
$cshow :: Args -> String
showsPrec :: Int -> Args -> ShowS
$cshowsPrec :: Int -> Args -> ShowS
Show, ReadPrec [Args]
ReadPrec Args
Int -> ReadS Args
ReadS [Args]
(Int -> ReadS Args)
-> ReadS [Args] -> ReadPrec Args -> ReadPrec [Args] -> Read Args
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Args]
$creadListPrec :: ReadPrec [Args]
readPrec :: ReadPrec Args
$creadPrec :: ReadPrec Args
readList :: ReadS [Args]
$creadList :: ReadS [Args]
readsPrec :: Int -> ReadS Args
$creadsPrec :: Int -> ReadS Args
Read, Args -> Args -> Bool
(Args -> Args -> Bool) -> (Args -> Args -> Bool) -> Eq Args
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Args -> Args -> Bool
$c/= :: Args -> Args -> Bool
== :: Args -> Args -> Bool
$c== :: Args -> Args -> Bool
Eq, Typeable Args
DataType
Constr
Typeable Args
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Args -> c Args)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Args)
-> (Args -> Constr)
-> (Args -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Args))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Args))
-> ((forall b. Data b => b -> b) -> Args -> Args)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r)
-> (forall u. (forall d. Data d => d -> u) -> Args -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> Args -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Args -> m Args)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Args -> m Args)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Args -> m Args)
-> Data Args
Args -> DataType
Args -> Constr
(forall b. Data b => b -> b) -> Args -> Args
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Args -> c Args
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Args
forall a.
Typeable a
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> Args -> u
forall u. (forall d. Data d => d -> u) -> Args -> [u]
forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r
forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Args -> m Args
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Args -> m Args
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Args
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Args -> c Args
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Args)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Args)
$cArgs :: Constr
$tArgs :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> Args -> m Args
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Args -> m Args
gmapMp :: (forall d. Data d => d -> m d) -> Args -> m Args
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Args -> m Args
gmapM :: (forall d. Data d => d -> m d) -> Args -> m Args
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Args -> m Args
gmapQi :: Int -> (forall d. Data d => d -> u) -> Args -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Args -> u
gmapQ :: (forall d. Data d => d -> u) -> Args -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> Args -> [u]
gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r
$cgmapQr :: forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r
gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r
$cgmapQl :: forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Args -> r
gmapT :: (forall b. Data b => b -> b) -> Args -> Args
$cgmapT :: (forall b. Data b => b -> b) -> Args -> Args
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Args)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Args)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c Args)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Args)
dataTypeOf :: Args -> DataType
$cdataTypeOf :: Args -> DataType
toConstr :: Args -> Constr
$ctoConstr :: Args -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Args
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Args
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Args -> c Args
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Args -> c Args
$cp1Data :: Typeable Args
Data, Typeable)

-- | Parse the CLI arguments with 'System.Console.CmdArgs'.
getArgs :: IO Args
getArgs :: IO Args
getArgs = Args -> IO Args
forall a. Data a => a -> IO a
cmdArgs Args
argSpec

argSpec :: Args
argSpec :: Args
argSpec =
    Args :: String -> Maybe String -> Bool -> Maybe Integer -> Args
Args
            { argPubKey :: String
argPubKey       = String
forall a. Default a => a
def String -> Ann -> String
forall val. Data val => val -> Ann -> val
&= Int -> Ann
argPos Int
0 String -> Ann -> String
forall val. Data val => val -> Ann -> val
&= String -> Ann
typ String
"ACCOUNT_PUBKEY"
            , argOutputFile :: Maybe String
argOutputFile   =
                Maybe String
forall a. Maybe a
Nothing
                Maybe String -> Ann -> Maybe String
forall val. Data val => val -> Ann -> val
&= String -> Ann
help String
"File to write the export to. Default: stdout"
                Maybe String -> Ann -> Maybe String
forall val. Data val => val -> Ann -> val
&= Ann
explicit
                Maybe String -> Ann -> Maybe String
forall val. Data val => val -> Ann -> val
&= String -> Ann
name String
"output-file"
                Maybe String -> Ann -> Maybe String
forall val. Data val => val -> Ann -> val
&= String -> Ann
name String
"o"
                Maybe String -> Ann -> Maybe String
forall val. Data val => val -> Ann -> val
&= String -> Ann
typ String
"FILE"
            , argCoinTracking :: Bool
argCoinTracking = Bool
False
                                Bool -> Ann -> Bool
forall val. Data val => val -> Ann -> val
&= String -> Ann
help String
"Generate a CoinTracking Import file."
                                Bool -> Ann -> Bool
forall val. Data val => val -> Ann -> val
&= Ann
explicit
                                Bool -> Ann -> Bool
forall val. Data val => val -> Ann -> val
&= String -> Ann
name String
"cointracking"
            , argYear :: Maybe Integer
argYear         = Maybe Integer
forall a. Maybe a
Nothing
                                Maybe Integer -> Ann -> Maybe Integer
forall val. Data val => val -> Ann -> val
&= String -> Ann
help String
"Limit to given year"
                                Maybe Integer -> Ann -> Maybe Integer
forall val. Data val => val -> Ann -> val
&= Ann
explicit
                                Maybe Integer -> Ann -> Maybe Integer
forall val. Data val => val -> Ann -> val
&= String -> Ann
name String
"y"
                                Maybe Integer -> Ann -> Maybe Integer
forall val. Data val => val -> Ann -> val
&= String -> Ann
name String
"year"
                                Maybe Integer -> Ann -> Maybe Integer
forall val. Data val => val -> Ann -> val
&= String -> Ann
typ String
"YYYY"
            }
        Args -> Ann -> Args
forall val. Data val => val -> Ann -> val
&= String -> Ann
summary
               (  String
"bnb-staking-csvs v"
               String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Version -> String
showVersion Version
version
               String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
", Pavan Rikhi 2021"
               )
        Args -> Ann -> Args
forall val. Data val => val -> Ann -> val
&= String -> Ann
program String
"bnb-staking-csvs"
        Args -> Ann -> Args
forall val. Data val => val -> Ann -> val
&= [Ann] -> Ann
helpArg [String -> Ann
name String
"h"]
        Args -> Ann -> Args
forall val. Data val => val -> Ann -> val
&= String -> Ann
help String
"Generate CSV Exports of you BinanceChain Staking Rewards."