【问题标题】:Parsing user options into custom data types with OptParse-Applicative使用 OptParse-Applicative 将用户选项解析为自定义数据类型
【发布时间】:2019-07-24 01:27:01
【问题描述】:

我正在尝试构建一个 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,然后将数据返回给相关函数。

我知道我可以fmap 这样的数据类型

data JournalDisplay { jdFromDate     :: UTCTime
                    , jdToDate       :: UTCTime
                    , jdResultNumber :: Maybe Int
                    }

作为

JournalDisplay
<$>
fromDateParser
<*>
toDateParser
<*>
optional resultNumberParser

但我不确定如何使用我的原始数据结构来做到这一点。

我想我需要一个像[Mod CommandFields JournalCommand] 这样的列表,我可以通过连接Mod 列表将其传递给subparser 函数。我不完全确定。

【问题讨论】:

    标签: haskell optparse-applicative


    【解决方案1】:

    在 optparse-applicative 中有 Parser 类型,还有 ParserInfo 类型,它表示一个“已完成”的解析器,它包含额外的信息,如页眉、页脚、描述等......并且可以运行execParser。 我们通过info 函数从Parser 转到ParserInfo,该函数将额外信息添加为修饰符。

    现在,当使用子命令编写解析器时,每个子命令必须有自己的ParserInfo 值(意味着它可以有自己的本地帮助和描述)。

    我们将每个ParserInfo 值传递给command 函数(连同我们希望子命令具有的名称),然后我们使用mconcat 组合[Mod CommandFields JournalCommand] 列表并将结果传递给@987654325 @。这将为我们提供顶级Parser。我们需要再次使用info 来提供顶层描述并得到最终的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") 
    

    【讨论】:

    • 完美运行!感谢您提供非常详细且易于理解的答案!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多