packages feed

solana-staking-csvs-0.1.3.0: src/Console/SolanaStaking/Csv.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE RecordWildCards #-}

-- | Types responsible for CSV generation.
module Console.SolanaStaking.Csv
    ( makeCsvContents
    , ExportData (..)
    , toExportData
    ) where

import Data.Csv
    ( DefaultOrdered (..)
    , EncodeOptions (..)
    , ToNamedRecord (..)
    , defaultEncodeOptions
    , encodeDefaultOrderedByNameWith
    , namedRecord
    , (.=)
    )
import Data.Time (defaultTimeLocale, formatTime)
import Data.Time.Clock.POSIX (POSIXTime, posixSecondsToUTCTime)

import Console.SolanaStaking.Api
    ( StakeReward (..)
    , StakingAccount (..)
    , StakingPubKey (..)
    , renderLamports
    )

import Data.ByteString.Lazy qualified as LBS
import Data.Text qualified as T


-- | Represents a single row of CSV data.
data ExportData = ExportData
    { edTime :: T.Text
    , edAmount :: T.Text
    , edStakeAccount :: T.Text
    , edEpoch :: Integer
    }
    deriving (Show, Read, Eq, Ord)


-- | Remove the @ed@ prefixes from the field names.
instance ToNamedRecord ExportData where
    toNamedRecord ExportData {..} =
        namedRecord
            [ "time" .= edTime
            , "amount" .= edAmount
            , "stakeAccount" .= edStakeAccount
            , "epoch" .= edEpoch
            ]


-- | Column order is @time, amount, stakeAccount, epoch@.
instance DefaultOrdered ExportData where
    headerOrder _ = ["time", "amount", "stakeAccount", "epoch"]


-- | Convert an Account & Reward into a CSV row.
toExportData :: (StakingAccount, StakeReward) -> ExportData
toExportData (StakingAccount {..}, StakeReward {..}) =
    ExportData
        { edTime = formatTimestamp srTimestamp
        , edAmount = renderLamports srAmount
        , edStakeAccount = fromStakingPubKey saPubKey
        , edEpoch = srEpoch
        }
  where
    formatTimestamp :: POSIXTime -> T.Text
    formatTimestamp =
        T.pack . formatTime defaultTimeLocale "%F %T%Z" . posixSecondsToUTCTime


-- | Build the CSV contents with a header row.
makeCsvContents :: [(StakingAccount, StakeReward)] -> LBS.ByteString
makeCsvContents =
    encodeDefaultOrderedByNameWith defaultEncodeOptions {encUseCrLf = False}
        . map toExportData