使用 OptParse-Applicative 将用户选项解析为自定义数据类型
Parsing user options into custom data types with OptParse-Applicative
我正在尝试构建一个 CLI 美食日记应用程序。
这是我希望在其中解析用户输入的数据类型。
data JournalCommand =
JournalSearch Query DataTypes Ingridents BrandOwnder PageNumber
| JournalReport Query DataTypes Ingridents BrandOwnder PageNumber ResultNumber
| JournalDisplay FromDate ToDate ResultNumber
| JournalStoreSearch Query DataTypes Ingridents BrandOwnder PageNumber ResultNumber StoreFlag
| JournalStoreCustom CustomEntry OnDate StoreFlag
| JournalDelete FromDate ToDate ResultNumber
| JournalEdit CustomEntry ResultNumber
deriving (Show, Eq)
因为有很多重叠,我总共有 8 个 Parser a
类型的函数。
像这样的函数
-- | Search Query
aQueryParser :: Parser String
aQueryParser = strOption
( long "search"
<> short 's'
<> help "Search for a term in the database"
)
如果最终有这样的功能的想法
runJournal :: JournalCommand -> MT SomeError IO ()
runJournal = \case
JournalSearch q d i b p
-> runSearch q d i b p
JournalReport q d i b p r
-> runSearchAndReport q d i b p r
...
...
其中 MT
是一些可以处理 error
+ IO
的 monad 转换器。还不确定。
问题是:如何设置 parseArgs
函数
parseArgs :: IO JournalCommand
parseArgs = execParser ...
和parser
函数
parser :: Parser JournalCommand
parser = ...
这样我就可以将用户输入解析为 JournalCommand
,然后 return 将数据解析为相关函数。
我知道我可以fmap
这样的数据类型
data JournalDisplay { jdFromDate :: UTCTime
, jdToDate :: UTCTime
, jdResultNumber :: Maybe Int
}
作为
JournalDisplay
<$>
fromDateParser
<*>
toDateParser
<*>
optional resultNumberParser
但我不确定如何使用我的原始数据结构来做到这一点。
我想我需要一个像这样的列表 [Mod CommandFields JournalCommand]
,我可以通过连接 Mod
列表将其传递给 subparser
函数。我不太确定。
在 optparse-applicative 中有 Parser
类型,还有 ParserInfo
type which represents a "completed" parser holding extra information like header, footer, description, etc... and which is ready to be run with execParser
。
我们通过添加额外信息作为修饰符的 info
函数从 Parser
到 ParserInfo
。
现在,当用子命令编写解析器时,每个子命令必须有自己的 ParserInfo
值(意味着它可以有自己的本地帮助和描述)。
我们将每个 ParserInfo
值传递给 command
function (along with the name we want the subcommand to have) and then we combine the [Mod CommandFields JournalCommand]
list using mconcat
and pass the result to subparser
。这将为我们提供 top-level Parser
。我们需要再次使用 info
来提供 top-level 描述并得到最终的 ParserInfo
.
使用您的类型的简化版本的示例:
data JournalCommand =
JournalSearch String String
| JournalReport String
deriving (Show, Eq)
journalParserInfo :: O.ParserInfo JournalCommand
journalParserInfo =
let searchParserInfo :: O.ParserInfo JournalCommand
searchParserInfo =
O.info
(JournalSearch
<$> strArgument (metavar "ARG1" <> help "This is arg 1")
<*> strArgument (metavar "ARG2" <> help "This is arg 2"))
(O.fullDesc <> O.progDesc "desc 1")
reportParserInfo :: O.ParserInfo JournalCommand
reportParserInfo =
O.info
(JournalReport
<$> strArgument (metavar "ARG3" <> help "This is arg 3"))
(O.fullDesc <> O.progDesc "desc 2")
toplevel :: O.Parser JournalCommand
toplevel = O.subparser (mconcat [
command "search" searchParserInfo,
command "journal" reportParserInfo
])
in O.info toplevel (O.fullDesc <> O.progDesc "toplevel desc")
我正在尝试构建一个 CLI 美食日记应用程序。
这是我希望在其中解析用户输入的数据类型。
data JournalCommand =
JournalSearch Query DataTypes Ingridents BrandOwnder PageNumber
| JournalReport Query DataTypes Ingridents BrandOwnder PageNumber ResultNumber
| JournalDisplay FromDate ToDate ResultNumber
| JournalStoreSearch Query DataTypes Ingridents BrandOwnder PageNumber ResultNumber StoreFlag
| JournalStoreCustom CustomEntry OnDate StoreFlag
| JournalDelete FromDate ToDate ResultNumber
| JournalEdit CustomEntry ResultNumber
deriving (Show, Eq)
因为有很多重叠,我总共有 8 个 Parser a
类型的函数。
像这样的函数
-- | Search Query
aQueryParser :: Parser String
aQueryParser = strOption
( long "search"
<> short 's'
<> help "Search for a term in the database"
)
如果最终有这样的功能的想法
runJournal :: JournalCommand -> MT SomeError IO ()
runJournal = \case
JournalSearch q d i b p
-> runSearch q d i b p
JournalReport q d i b p r
-> runSearchAndReport q d i b p r
...
...
其中 MT
是一些可以处理 error
+ IO
的 monad 转换器。还不确定。
问题是:如何设置 parseArgs
函数
parseArgs :: IO JournalCommand
parseArgs = execParser ...
和parser
函数
parser :: Parser JournalCommand
parser = ...
这样我就可以将用户输入解析为 JournalCommand
,然后 return 将数据解析为相关函数。
我知道我可以fmap
这样的数据类型
data JournalDisplay { jdFromDate :: UTCTime
, jdToDate :: UTCTime
, jdResultNumber :: Maybe Int
}
作为
JournalDisplay
<$>
fromDateParser
<*>
toDateParser
<*>
optional resultNumberParser
但我不确定如何使用我的原始数据结构来做到这一点。
我想我需要一个像这样的列表 [Mod CommandFields JournalCommand]
,我可以通过连接 Mod
列表将其传递给 subparser
函数。我不太确定。
在 optparse-applicative 中有 Parser
类型,还有 ParserInfo
type which represents a "completed" parser holding extra information like header, footer, description, etc... and which is ready to be run with execParser
。
我们通过添加额外信息作为修饰符的 info
函数从 Parser
到 ParserInfo
。
现在,当用子命令编写解析器时,每个子命令必须有自己的 ParserInfo
值(意味着它可以有自己的本地帮助和描述)。
我们将每个 ParserInfo
值传递给 command
function (along with the name we want the subcommand to have) and then we combine the [Mod CommandFields JournalCommand]
list using mconcat
and pass the result to subparser
。这将为我们提供 top-level Parser
。我们需要再次使用 info
来提供 top-level 描述并得到最终的 ParserInfo
.
使用您的类型的简化版本的示例:
data JournalCommand =
JournalSearch String String
| JournalReport String
deriving (Show, Eq)
journalParserInfo :: O.ParserInfo JournalCommand
journalParserInfo =
let searchParserInfo :: O.ParserInfo JournalCommand
searchParserInfo =
O.info
(JournalSearch
<$> strArgument (metavar "ARG1" <> help "This is arg 1")
<*> strArgument (metavar "ARG2" <> help "This is arg 2"))
(O.fullDesc <> O.progDesc "desc 1")
reportParserInfo :: O.ParserInfo JournalCommand
reportParserInfo =
O.info
(JournalReport
<$> strArgument (metavar "ARG3" <> help "This is arg 3"))
(O.fullDesc <> O.progDesc "desc 2")
toplevel :: O.Parser JournalCommand
toplevel = O.subparser (mconcat [
command "search" searchParserInfo,
command "journal" reportParserInfo
])
in O.info toplevel (O.fullDesc <> O.progDesc "toplevel desc")