[英]Parsing user options into custom data types with OptParse-Applicative
I'm trying to build a CLI food journal app.我正在尝试构建一个 CLI 食品日志应用程序。
And this is the data type I want the user input to be parsed in.这是我希望解析用户输入的数据类型。
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)
and because there's a lot of overlap I have a total of 8 functions with Parser a
type.并且因为有很多重叠,所以我总共有 8 个函数与
Parser a
类型。
Functions like these像这样的功能
-- | Search Query
aQueryParser :: Parser String
aQueryParser = strOption
( long "search"
<> short 's'
<> help "Search for a term in the database"
)
The idea if to ultimately have a function like this这个想法是否最终具有这样的功能
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
...
...
where MT
is some monad transformer that can handle error
+ IO
.其中
MT
是一些可以处理error
+ IO
monad 转换器。 Not sure yet.还不确定。
The question is: How do I setup the parseArgs
function问题是:如何设置
parseArgs
函数
parseArgs :: IO JournalCommand
parseArgs = execParser ...
and parser
function和
parser
功能
parser :: Parser JournalCommand
parser = ...
so that I'd be able to parse user input into JournalCommand
and then return the data to relevant functions.这样我就可以将用户输入解析到
JournalCommand
,然后将数据返回给相关函数。
I know I can fmap
a data type like this我知道我可以
fmap
的数据类型
data JournalDisplay { jdFromDate :: UTCTime
, jdToDate :: UTCTime
, jdResultNumber :: Maybe Int
}
as作为
JournalDisplay
<$>
fromDateParser
<*>
toDateParser
<*>
optional resultNumberParser
But I'm not sure how to go about doing that with my original data structure.但我不确定如何使用我的原始数据结构来做到这一点。
I think I need to have a list like this [Mod CommandFields JournalCommand]
which I may be able to pass into subparser
function by concatenating the Mod
list.我想我需要一个像这样的列表
[Mod CommandFields JournalCommand]
,我可以通过连接Mod
列表将它传递给子subparser
函数。 I'm not completely sure.我不完全确定。
In optparse-applicative there's the Parser
type, but also the ParserInfo
type which represents a "completed" parser holding extra information like header, footer, description, etc... and which is ready to be run with execParser
.在 optparse-applicative 中,有
Parser
类型,还有ParserInfo
类型,它表示一个“完成”的解析器,其中包含页眉、页脚、描述等额外信息……并且准备好与execParser
一起运行。 We go from Parser
to ParserInfo
by way of the info
function which adds the extra information as modifiers.我们通过添加额外信息作为修饰符的
info
函数从Parser
转到ParserInfo
。
Now, when writing a parser with subcommands, each subcommand must have its own ParserInfo
value (implying that it can have its own local help and description).现在,当使用子命令编写解析器时,每个子命令都必须有自己的
ParserInfo
值(意味着它可以有自己的本地帮助和描述)。
We pass each of these ParserInfo
values to the 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
.我们将这些
ParserInfo
值中的每一个传递给command
函数(以及我们希望子命令具有的名称),然后我们使用mconcat
组合[Mod CommandFields JournalCommand]
mconcat
[Mod CommandFields JournalCommand]
列表并将结果传递给subparser
。 This will give us the top-level Parser
.这将为我们提供顶级
Parser
。 We need to use info
again to provide the top-level description and get the final ParserInfo
.我们需要再次使用
info
来提供顶级描述并获得最终的ParserInfo
。
An example that uses a simplified version of your type:使用您类型的简化版本的示例:
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")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.