morley-1.3.0: src/Morley/CLI.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Utilities for parsing Morley types using @optparse-applicative@.
module Morley.CLI
( -- * Full parsers
parserInfo
-- * Options
, contractFileOption
, nowOption
, maxStepsOption
, dbPathOption
, txDataOption
, keyHashOption
, valueOption
, mutezOption
, addressOption
, onelineOption
, entrypointOption
, mTextOption
) where
import Named (arg)
import Options.Applicative
(footerDoc, fullDesc, header, help, helper, info, long, metavar, option, progDesc, strOption,
switch)
import qualified Options.Applicative as Opt
import Options.Applicative.Help.Pretty (Doc)
import qualified Michelson.Parser as P
import Michelson.Runtime (TxData(..))
import Michelson.Runtime.GState (genesisAddress)
import Michelson.Text (MText)
import Michelson.Untyped (EpName)
import qualified Michelson.Untyped as U
import Tezos.Address (Address)
import Tezos.Core (Mutez, Timestamp, parseTimestamp, timestampFromSeconds)
import Tezos.Crypto
import Util.CLI
import Util.Named
----------------------------------------------------------------------------
-- These options are mostly specifications of 'mkCLOptionParser'
-- for concrete types (with more specific names).
----------------------------------------------------------------------------
-- | Full parser for a client.
parserInfo
:: "usage" :! Doc
-> "description" :! String
-> "header" :! String
-> "parser" :! Opt.Parser s
-> Opt.ParserInfo s
parserInfo
(arg #usage -> usage)
(arg #description -> description)
(arg #header -> clientHeader)
(arg #parser -> parser) =
info (helper <*> parser) $
mconcat
[ fullDesc
, progDesc description
, header clientHeader
, footerDoc $ pure usage
]
-- | Parser for path to a contract code.
contractFileOption :: Opt.Parser FilePath
contractFileOption = strOption $
long "contract" <>
metavar "FILEPATH" <>
help "Path to contract file"
-- | Parser for the time returned by @NOW@ instruction.
nowOption :: Opt.Parser (Maybe Timestamp)
nowOption = optional $ option parser $
long "now" <>
metavar "TIMESTAMP" <>
help "Timestamp that you want the runtime interpreter to use (default is now)"
where
parser =
(timestampFromSeconds <$> Opt.auto) <|>
Opt.maybeReader (parseTimestamp . toText)
-- | Parser for gas limit on contract execution.
maxStepsOption :: Opt.Parser Word64
maxStepsOption = mkCLOptionParser
(Just 100500)
(#name .! "max-steps")
(#help .! "Max steps that you want the runtime interpreter to use")
-- | Parser for path to database with Morley state.
dbPathOption :: Opt.Parser FilePath
dbPathOption = Opt.strOption $
long "db" <>
metavar "FILEPATH" <>
Opt.value "db.json" <>
help "Path to DB with data which is used instead of real blockchain data" <>
Opt.showDefault
-- | Parser for transaction parameters.
txDataOption :: Opt.Parser TxData
txDataOption =
mkTxData
<$> addressOption (Just genesisAddress)
(#name .! "sender") (#help .! "Sender address")
<*> valueOption Nothing
(#name .! "parameter") (#help .! "Parameter of passed contract")
<*> mutezOption (Just minBound)
(#name .! "amount") (#help .! "Amount sent by a transaction")
<*> entrypointOption (#name .! "entrypoint") (#help .! "Entrypoint to call")
where
mkTxData :: Address -> U.Value -> Mutez -> EpName -> TxData
mkTxData addr param amount epName =
TxData
{ tdSenderAddress = addr
, tdParameter = param
, tdEntrypoint = epName
, tdAmount = amount
}
-- | Generic parser to read an option of 'KeyHash' type.
keyHashOption ::
Maybe KeyHash -> "name" :! String -> "help" :! String -> Opt.Parser KeyHash
keyHashOption = mkCLOptionParser
-- | Generic parser to read an option of 'U.Value' type.
valueOption ::
Maybe U.Value -> "name" :! String -> "help" :! String -> Opt.Parser U.Value
valueOption = mkCLOptionParser
-- | Generic parser to read an option of 'Mutez' type.
mutezOption ::
Maybe Mutez -> "name" :! String -> "help" :! String -> Opt.Parser Mutez
mutezOption = mkCLOptionParser
-- | Generic parser to read an option of 'Address' type.
addressOption ::
Maybe Address -> "name" :! String -> "help" :! String -> Opt.Parser Address
addressOption = mkCLOptionParser
-- | @--oneline@ flag.
onelineOption :: Opt.Parser Bool
onelineOption = switch (
long "oneline" <>
help "Force single line output")
-- | Generic parser to read an option of 'EpName' type.
entrypointOption :: "name" :! String -> "help" :! String -> Opt.Parser EpName
entrypointOption = mkCLOptionParser (Just U.DefEpName)
-- | Generic parser to read an option of 'MText' type.
mTextOption ::
Maybe MText -> "name" :! String -> "help" :! String -> Opt.Parser MText
mTextOption = mkCLOptionParser
----------------------------------------------------------------------------
-- 'HasCLReader' orphan instances (better to avoid)
----------------------------------------------------------------------------
-- This instance uses parser which is not in the place where 'U.Value'
-- is defined, hence it is orphan.
instance HasCLReader U.Value where
getReader = eitherReader parseValue
where
parseValue :: String -> Either String U.Value
parseValue =
first (mappend "Failed to parse value: " . displayException) .
P.parseExpandValue .
toText
getMetavar = "MICHELSON VALUE"