I'm trying to build a CLI food journal app.
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.
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. Not sure yet.
The question is: How do I setup the parseArgs function
parseArgs :: IO JournalCommand
parseArgs = execParser ...
and parser function
parser :: Parser JournalCommand
parser = ...
so that I'd be able to parse user input into JournalCommand and then return the data to relevant functions.
I know I can fmap a data type like this
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. I'm not completely sure.
In optparse-applicative there's the
Parsertype, but also theParserInfotype which represents a "completed" parser holding extra information like header, footer, description, etc... and which is ready to be run withexecParser. We go fromParsertoParserInfoby way of theinfofunction which adds the extra information as modifiers.Now, when writing a parser with subcommands, each subcommand must have its own
ParserInfovalue (implying that it can have its own local help and description).We pass each of these
ParserInfovalues to thecommandfunction (along with the name we want the subcommand to have) and then we combine the[Mod CommandFields JournalCommand]list usingmconcatand pass the result tosubparser. This will give us the top-levelParser. We need to useinfoagain to provide the top-level description and get the finalParserInfo.An example that uses a simplified version of your type: