haskell optparse-applicative:解析具有多个字段的记录列表

haskell optparse-applicative: parsing a list of records that have multiple fields

如果我有以下类型和解析器:

data Mode = 
     Mode1 
   | Mode2 
   deriving (Show, Eq, Read)

data ThingINeedMulitpleOf = 
   Thing { _name :: String, _mode :: Mode }
   deriving (Show, Eq)

thingParser :: Parser ThingINeedMulitpleOf
thingParser = Thing <$> strArgument (metavar "NAME")
                    <*> option auto (long "mode" <> metavar "MODE")

我按以下方式构建解析器:

data Config = 
   Config ThingINeedMulitpleOf ThingINeedMulitpleOf
   deriving (Show, Eq)

loadConfig = execParser $ info (Config <$> thingParser <*> thingParser) fullDesc

然后我可以成功解析 my-exe Thing1 --mode Mode1 Thing2 --mode Mode2 但这只有在我想要两个 Things 时才有用。 我 运行 在尝试更改 Config 以支持 n Things 时遇到问题,即:

data Config = 
   Config [ThingINeedMulitpleOf]
   deriving (Show, Eq)

loadConfig = execParser $ info (Config <$> many thingParser) fullDesc

但我现在无法再解析 my-exe Thing1 --mode Mode1 Thing2 --mode Mode2,出现错误 Invalid argument 'Thing1'

有趣的是,如果 ThingINeedMulitpleOf 仅包含一个字段,则此方法有效。

如果您使用具有 Alternative 类型类的 some 函数的子解析器,您可以获得您正在寻找的效果,诚然,命令行语法略有不同。

thingSubparser :: Parser ThingINeedMulitpleOf
thingSubparser = subparser $ command "thing" (info thingParser mempty)

loadConfig :: IO Config
loadConfig = execParser $ info (Config <$> some thingSubparser) fullDesc

有了这个,你可以编写如下命令行:

my-exe thing test --mode Mode1 thing test2 --mode Mode2

产生配置对象:

Config [Thing {_name = "test", _mode = Mode1},Thing {_name = "test2", _mode = Mode2}]

我不太清楚为什么你需要子解析器并且不能只使用 some 来对抗 thingParser,但如果我不得不猜测那是因为命令给出了解析器一个定界符("thing" argument/command 名称)。