packages feed

gamgee-1.2.0: app/Main.hs

module Main (main) where

import qualified Data.Aeson                 as Aeson
import qualified Data.Time.Clock.POSIX      as Clock
import qualified Gamgee.Effects             as Eff
import qualified Gamgee.Operation           as Operation
import qualified Gamgee.Program.CommandLine as Cmd
import qualified Gamgee.Program.Effects     as Eff
import qualified Gamgee.Token               as Token
import qualified Options.Applicative        as Options
import           Polysemy                   (Embed, Member, Sem)
import qualified Polysemy                   as P
import qualified Polysemy.Error             as P
import qualified Polysemy.Input             as P
import qualified Polysemy.Output            as P
import           Relude


main :: IO ()
main = do
  command <- Options.execParser parserInfo
  case command of
    Cmd.AddToken spec    -> runAddToken spec
    Cmd.DeleteToken t    -> runDeleteToken t
    Cmd.ListTokens       -> runListTokens
    Cmd.GetOTP t mode    -> runGetOTP t mode
    Cmd.ChangePassword t -> runChangePassword t
    Cmd.GetInfo          -> runGetInfo

parserInfo :: Options.ParserInfo Cmd.Command
parserInfo = Options.info (Options.helper
                           <*> Cmd.versionOption
                           <*> Cmd.getCommand) Options.fullDesc

runAddToken :: Token.TokenSpec -> IO ()
runAddToken spec = Eff.runM_
                   $ Eff.runErrorStdErr
                   $ Eff.runCryptoRandomIO
                   $ Eff.runCrypto
                   $ Eff.runSecretInputIO
                   $ Eff.runByteStoreIO
                   $ Eff.runJSONStore
                   $ Eff.runStateJSON
                   $ Operation.addToken spec

runDeleteToken :: Token.TokenIdentifier -> IO ()
runDeleteToken t = Eff.runM_
                   $ Eff.runErrorStdErr
                   $ Eff.runByteStoreIO
                   $ Eff.runJSONStore
                   $ Eff.runStateJSON
                   $ Operation.deleteToken t

runListTokens :: IO ()
runListTokens = Eff.runM_
                $ Eff.runErrorStdErr
                $ Eff.runOutputStdOut
                $ Eff.runByteStoreIO
                $ Eff.runJSONStore
                $ Eff.runStateJSON Operation.listTokens

runGetOTP :: Token.TokenIdentifier -> Cmd.OutputMode -> IO ()
runGetOTP t o = do
  now <- Clock.getPOSIXTime
  Eff.runM_
    $ Eff.runErrorStdErr
    $ (if o == Cmd.OutputStdOut then Eff.runOutputStdOut else Eff.runOutputClipboard)
    $ Eff.runCryptoRandomIO
    $ Eff.runCrypto
    $ Eff.runSecretInputIO
    $ Eff.runByteStoreIO
    $ Eff.runJSONStore
    $ Eff.runStateJSON
    $ Eff.runTOTP
    $ Operation.getOTP t now

runChangePassword :: Token.TokenIdentifier -> IO ()
runChangePassword t = Eff.runM_
                      $ Eff.runErrorStdErr
                      $ Eff.runCryptoRandomIO
                      $ Eff.runCrypto
                      $ Eff.runSecretInputIO
                      $ Eff.runByteStoreIO
                      $ Eff.runJSONStore
                      $ Eff.runStateJSON
                      $ Eff.runTOTP
                      $ Operation.changePassword t

getConfig :: Member (Embed IO) r => Sem r (Maybe Token.Config)
getConfig = do
  res <- fmap (rightToMaybe @Eff.EffError)
         $ P.runError
         $ fmap (rightToMaybe @Eff.ByteStoreError)
         $ P.runError
         $ Eff.runByteStoreIO
         $ Eff.configStoreToByteStore Eff.jsonDecode
  return (join res)

runGetInfo :: IO ()
runGetInfo = do
  path <- Eff.configFilePath
  res <- P.runM
         $ P.runOutputMonoid (decodeUtf8 . Aeson.encode @Aeson.Value)
         $ P.runInputConst path
         $ Operation.getInfo getConfig
  putTextLn $ fst res