简体   繁体   English

使用 OptParse-Applicative 将用户选项解析为自定义数据类型

[英]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 functionparser功能

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM