packages feed

gamgee-1.1.0: app/Gamgee/Program/CommandLine.hs

-- | Command line argument parsing
module Gamgee.Program.CommandLine
    ( Command (..)
    , OutputMode (..)
    , getCommand
    , versionOption
    ) where

import qualified Data.Version        as Version
import qualified Gamgee.Token        as Token
import           Options.Applicative
import           Paths_gamgee        (version)
import           Relude


data OutputMode = OutputStdOut
                | OutputClipboard
                deriving stock (Eq)

data Command = AddToken Token.TokenSpec
             | DeleteToken Token.TokenIdentifier
             | ListTokens
             | GetOTP Token.TokenIdentifier OutputMode
             | GetInfo

getCommand :: Parser Command
getCommand = hsubparser (
    command "list" (info listTokens $ progDesc "List the names of all known tokens")
    <> command "add" (info addToken $ progDesc "Add a new token")
    <> command "delete" (info deleteToken $ progDesc "Delete a token")
    <> command "info" (info getInfo $ progDesc "Print information about this Gamgee installation")
  )
  <|> getOTPOperation

listTokens :: Parser Command
listTokens = pure ListTokens

addToken :: Parser Command
addToken = AddToken <$> tokenSpec

tokenSpec :: Parser Token.TokenSpec
tokenSpec = Token.TokenSpec
            <$> pure Token.TOTP
            <*> strOption (long "label" <> short 'l'
                           <> help "Label of the token")
            <*> (Token.TokenSecretPlainText <$> strOption (long "secret" <> short 's'
                                                           <> help "Secret of the token"))
            <*> strOption (long "issuer" <> short 'i' <> value (Token.TokenIssuer "")
                           <> help "Issuer of the token")
            <*> option (eitherReader algoReader) (long "algorithm" <> short 'a' <> value Token.AlgorithmSHA1
                                                  <> help "HMAC algorithm - SHA1 (default), SHA256, or SHA512")
            <*> option (eitherReader digitsReader) (long "digits" <> short 'd' <> value Token.Digits6
                                                    <> help "Number of digits in the OTP - 6 (default) or 8")
            <*> (Token.TokenPeriod <$> option auto (long "period" <> short 'p' <> value 30
                                                    <> help "OTP validity in seconds. Default - 30."))

deleteToken :: Parser Command
deleteToken = DeleteToken <$> strOption (long "label" <> short 'l' <> help "Label of the token")

algoReader :: String -> Either String Token.TokenAlgorithm
algoReader "SHA1"   = Right Token.AlgorithmSHA1
algoReader "SHA256" = Right Token.AlgorithmSHA256
algoReader "SHA512" = Right Token.AlgorithmSHA512
algoReader s        = Left $ "Invalid algorithm: " ++ s

digitsReader :: String -> Either String Token.TokenDigits
digitsReader "6" = Right Token.Digits6
digitsReader "8" = Right Token.Digits8
digitsReader s   = Left $ "Invalid number of digits: " ++ s ++ ". Must be 6 or 8."

versionOption :: Parser (a -> a)
versionOption = infoOption (Version.showVersion version) (long "version" <> help "Show version")

getOTPOperation :: Parser Command
getOTPOperation = GetOTP
                  <$> strArgument (help "Get a one-time password" <> metavar "TOKEN-LABEL")
                  <*> flag OutputClipboard OutputStdOut
                      (long "stdout" <> help "Send the OTP to stdout instead of clipboard")

getInfo :: Parser Command
getInfo = pure GetInfo