cli, addons: reduce boilerplate a little with hledgerCommandMode helper

This commit is contained in:
Simon Michael 2017-01-25 17:10:10 -08:00
parent 9f8e96d189
commit b7092f278b
10 changed files with 113 additions and 92 deletions

View File

@ -43,28 +43,20 @@ defchartitems = 10
defchartsize = "600x400" defchartsize = "600x400"
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode :: Mode RawOpts cmdmode = hledgerCommandMode
cmdmode = (defAddonCommandMode "hledger-chart") { [here| chart
modeHelp = [here| Generate a pie chart for the top account balances with the same sign,
generate a pie chart for the top account balances with the same sign,
in SVG format. in SVG format.
Based on the old hledger-chart package, this is not yet useful. Based on the old hledger-chart package, this is not yet useful.
It's supposed to show only balances of one sign, but this might be broken. It's supposed to show only balances of one sign, but this might be broken.
|] |]
,modeHelpSuffix=lines [here| [flagReq ["chart-output","o"] (\s opts -> Right $ setopt "chart-output" s opts) "IMGFILE" ("output filename (default: "++defchartoutput++")")
|]
,modeGroupFlags = Group {
groupNamed = [generalflagsgroup1]
,groupUnnamed = [
flagReq ["chart-output","o"] (\s opts -> Right $ setopt "chart-output" s opts) "IMGFILE" ("output filename (default: "++defchartoutput++")")
,flagReq ["chart-items"] (\s opts -> Right $ setopt "chart-items" s opts) "N" ("number of accounts to show (default: "++show defchartitems++")") ,flagReq ["chart-items"] (\s opts -> Right $ setopt "chart-items" s opts) "N" ("number of accounts to show (default: "++show defchartitems++")")
,flagReq ["chart-size"] (\s opts -> Right $ setopt "chart-size" s opts) "WIDTHxHEIGHT" ("image size (default: "++defchartsize++")") ,flagReq ["chart-size"] (\s opts -> Right $ setopt "chart-size" s opts) "WIDTHxHEIGHT" ("image size (default: "++defchartsize++")")
] ] [generalflagsgroup1]
,groupHidden = [] []
} ([], Just $ argsFlag "[QUERY]")
,modeArgs = ([], Just $ argsFlag "[QUERY] --add-posting \"ACCT AMTEXPR\" ...")
}
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
data ChartOpts = ChartOpts { data ChartOpts = ChartOpts {

View File

@ -13,24 +13,19 @@ import Hledger.Cli
import Text.Printf import Text.Printf
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode = cmdmode = hledgerCommandMode
let m = defAddonCommandMode "check-dates" [here| check-dates
in m {
modeHelp = [here|
Check that transactions' dates are monotonically increasing. Check that transactions' dates are monotonically increasing.
With --date2, checks secondary dates instead. With --date2, checks secondary dates instead.
With --strict, dates must also be unique. With --strict, dates must also be unique.
With a query, only matched transactions' dates are checked. With a query, only matched transactions' dates are checked.
Reads the default journal file, or another specified with -f. Reads the default journal file, or another specified with -f.
FLAGS
|] |]
,modeHelpSuffix=lines [here| [flagNone ["strict"] (\opts -> setboolopt "strict" opts) "makes date comparing strict"]
|] [generalflagsgroup1]
,modeGroupFlags = (modeGroupFlags m) { []
groupUnnamed = [ ([], Just $ argsFlag "[QUERY]")
flagNone ["strict"] (\opts -> setboolopt "strict" opts) "makes date comparing strict"
]
}
}
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
main :: IO () main :: IO ()

View File

@ -19,8 +19,8 @@ import Data.String.Here
import qualified Data.Text as T import qualified Data.Text as T
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode = (defAddonCommandMode "dupes") { cmdmode = hledgerCommandMode
modeHelp = [here| [here| dupes
Reports duplicates in the account tree: account names having the same leaf Reports duplicates in the account tree: account names having the same leaf
but different prefixes. In other words, two or more leaves that are but different prefixes. In other words, two or more leaves that are
categorized differently. categorized differently.
@ -28,9 +28,10 @@ Reads the default journal file, or another specified as an argument.
http://stefanorodighiero.net/software/hledger-dupes.html http://stefanorodighiero.net/software/hledger-dupes.html
|] |]
,modeHelpSuffix=lines [here| []
|] [generalflagsgroup1]
} []
([], Nothing)
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
main = do main = do

View File

@ -16,17 +16,15 @@ import Hledger.Cli
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode :: Mode RawOpts cmdmode :: Mode RawOpts
cmdmode = (defAddonCommandMode "equity") { cmdmode = hledgerCommandMode
modeHelp = [here| [here| equity
Print a "closing balances" transaction that brings all accounts (or with Print a "closing balances" transaction that brings all accounts (or with
query arguments, just the matched accounts) to a zero balance, followed by an query arguments, just the matched accounts) to a zero balance, followed by an
opposite "opening balances" transaction that restores the balances from zero. opposite "opening balances" transaction that restores the balances from zero.
Such transactions can be useful, eg, for bringing account balances across Such transactions can be useful, eg, for bringing account balances across
file boundaries. file boundaries.
|] FLAGS
,modeHelpSuffix=lines [here|
The opening balances transaction is useful to carry over The opening balances transaction is useful to carry over
asset/liability balances if you choose to start a new journal file, asset/liability balances if you choose to start a new journal file,
@ -57,10 +55,11 @@ Open question: how to handle txns spanning a file boundary ? Eg:
``` ```
This command might or might not have some connection to the concept of This command might or might not have some connection to the concept of
"closing the books" in accounting. "closing the books" in accounting.
|] |]
,modeArgs = ([], Just $ argsFlag "[QUERY]") []
} [generalflagsgroup1]
[]
([], Just $ argsFlag "[QUERY]")
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
main :: IO () main :: IO ()

View File

@ -14,21 +14,14 @@ import Control.Monad
import Hledger.Cli import Hledger.Cli
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode = cmdmode = hledgerCommandMode
let m = defAddonCommandMode "hledger-prices" [here| prices
in m {
modeHelp = [here|
Print all prices from the journal. Print all prices from the journal.
|] |]
,modeHelpSuffix=lines [here| [flagNone ["costs"] (setboolopt "costs") "print transaction prices from postings instead of market prices"]
|] [generalflagsgroup1]
,modeArgs = ([], Nothing) []
,modeGroupFlags = (modeGroupFlags m) { ([], Nothing)
groupUnnamed = [
flagNone ["costs"] (setboolopt "costs") "print transaction prices from postings instead of market prices"
]
}
}
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
showPrice :: MarketPrice -> String showPrice :: MarketPrice -> String

View File

@ -13,11 +13,11 @@ import Data.String.Here
import Hledger.Cli import Hledger.Cli
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode = (defAddonCommandMode "print-unique") { cmdmode = hledgerCommandMode
modeHelp = [here| [here| print-unique
Remove transactions which reuse an already-seen description. Remove transactions which reuse an already-seen description.
|]
,modeHelpSuffix=lines [here| FLAGS
Example: Example:
```shell ```shell
@ -31,9 +31,11 @@ $ LEDGER_FILE=unique.journal hledger print-unique
2015/01/01 test 2015/01/01 test
(acct:one) 1 (acct:one) 1
``` ```
|] |]
} []
[generalflagsgroup1]
[]
([], Nothing)
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
main = do main = do

View File

@ -21,16 +21,17 @@ import Hledger.Cli.CliOptions
import Hledger.Cli ( withJournalDo, postingsReportAsText ) import Hledger.Cli ( withJournalDo, postingsReportAsText )
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode = (defAddonCommandMode "register-match") { cmdmode = hledgerCommandMode
modeHelp = [here| [here| register-match
A helper for ledger-autosync. This prints the one posting whose transaction A helper for ledger-autosync. This prints the one posting whose transaction
description is closest to DESC, in the style of the register command. description is closest to DESC, in the style of the register command.
If there are multiple equally good matches, it shows the most recent. If there are multiple equally good matches, it shows the most recent.
Query options (options, not arguments) can be used to restrict the search space. Query options (options, not arguments) can be used to restrict the search space.
|] |]
,modeHelpSuffix=lines [here| []
|] [generalflagsgroup1]
} []
([], Nothing)
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
main = do main = do

View File

@ -26,15 +26,11 @@ import qualified Data.Algorithm.Diff as D
import Hledger.Data.AutoTransaction (runModifierTransaction) import Hledger.Data.AutoTransaction (runModifierTransaction)
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
cmdmode = cmdmode = hledgerCommandMode
let m = (defAddonCommandMode "hledger-rewrite") [here| rewrite
in m {
modeHelp = [here|
Print all transactions, adding custom postings to the matched ones. Print all transactions, adding custom postings to the matched ones.
|] FLAGS
,modeHelpSuffix=lines [here|
This is a start at a generic rewriter of transaction entries. This is a start at a generic rewriter of transaction entries.
It reads the default journal and prints the transactions, like print, It reads the default journal and prints the transactions, like print,
@ -153,24 +149,19 @@ See also:
https://github.com/simonmichael/hledger/issues/99 https://github.com/simonmichael/hledger/issues/99
|] |]
,modeArgs = ([], Just $ argsFlag "[QUERY] --add-posting \"ACCT AMTEXPR\" ...") [flagReq ["add-posting"] (\s opts -> Right $ setopt "add-posting" s opts) "'ACCT AMTEXPR'"
,modeGroupFlags = (modeGroupFlags m) {
groupUnnamed = [
flagReq ["add-posting"] (\s opts -> Right $ setopt "add-posting" s opts) "'ACCT AMTEXPR'"
"add a posting to ACCT, which may be parenthesised. AMTEXPR is either a literal amount, or *N which means the transaction's first matched amount multiplied by N (a decimal number). Two spaces separate ACCT and AMTEXPR." "add a posting to ACCT, which may be parenthesised. AMTEXPR is either a literal amount, or *N which means the transaction's first matched amount multiplied by N (a decimal number). Two spaces separate ACCT and AMTEXPR."
,flagNone ["diff"] (setboolopt "diff") "generate diff suitable as an input for patch tool" ,flagNone ["diff"] (setboolopt "diff") "generate diff suitable as an input for patch tool"
] ]
} [generalflagsgroup1]
} []
([], Just $ argsFlag "[QUERY] --add-posting \"ACCT AMTEXPR\" ...")
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
-- TODO regex matching and interpolating matched name in replacement -- TODO regex matching and interpolating matched name in replacement
-- TODO interpolating match groups in replacement -- TODO interpolating match groups in replacement
-- TODO allow using this on unbalanced entries, eg to rewrite while editing -- TODO allow using this on unbalanced entries, eg to rewrite while editing
outputflags :: [Flag RawOpts]
outputflags = [flagNone ["diff"] (setboolopt "diff") "generate diff suitable as an input for patch tool"]
main :: IO () main :: IO ()
main = do main = do
opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} <- getHledgerCliOpts cmdmode opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} <- getHledgerCliOpts cmdmode

View File

@ -20,7 +20,8 @@ module Hledger.Cli.CliOptions (
generalflagsgroup3, generalflagsgroup3,
defMode, defMode,
defCommandMode, defCommandMode,
defAddonCommandMode, quickAddonCommandMode,
hledgerCommandMode,
argsFlag, argsFlag,
showModeUsage, showModeUsage,
withAliases, withAliases,
@ -206,11 +207,13 @@ defCommandMode names = defMode {
,modeValue=[("command", headDef "" names)] ,modeValue=[("command", headDef "" names)]
} }
-- | A cmdargs mode suitable for a hledger add-on command with the given name. -- | A cmdargs mode representing the hledger add-on command with the given name.
-- Like defCommandMode, but adds a appropriate short help message if the addon name -- Like defCommandMode, but adds a appropriate short help message if the addon name
-- is recognised, and includes hledger's common input/reporting/help flags as default. -- is recognised, and includes hledger's common input/reporting/help flags as default.
defAddonCommandMode :: Name -> Mode RawOpts -- Just used by hledger for generating the commands list I think (or possibly for
defAddonCommandMode name = (defCommandMode [name]) { -- invoking the addons as well ?)
quickAddonCommandMode :: Name -> Mode RawOpts
quickAddonCommandMode name = (defCommandMode [name]) {
modeHelp = fromMaybe "" $ lookup (stripAddonExtension name) standardAddonsHelp modeHelp = fromMaybe "" $ lookup (stripAddonExtension name) standardAddonsHelp
,modeGroupFlags = Group { ,modeGroupFlags = Group {
groupUnnamed = [] groupUnnamed = []
@ -219,6 +222,50 @@ defAddonCommandMode name = (defCommandMode [name]) {
} }
} }
-- | A template for a command's CLI help, influencing the content and layout
-- of the usage text generated by a cmdargs mode.
-- It is a multiline string structured like so:
-- The first line defines the command name (first word) and aliases (any other words).
-- From the second line up to a line containing just "FLAGS", or the end, is the preamble,
-- displayed above the flags list generated by cmdargs. Short help goes here.
-- Any lines after the FLAGS line are the postamble, displayed below the flags list.
-- Long help/full manual goes here.
type HelpTemplate = String
-- | Parse a help template into command names, help preamble, and help postamble lines.
parseHelpTemplate :: HelpTemplate -> Maybe ([Name], String, [String])
parseHelpTemplate t =
case lines t of
[] -> Nothing
(l:ls) -> Just (names, preamble, postamblelines)
where
names = words l
(preamblels, postamblels) = break (== "FLAGS") ls
preamble = unlines $ reverse $ dropWhile null $ reverse preamblels
postamblelines = dropWhile null $ drop 1 postamblels
-- | Build a cmdarg mode suitable for a hledger add-on command,
-- from a help template and flag/argument specifications.
-- Reduces boilerplate a little, though the complicated cmdargs
-- flag and argument specs are still required.
-- See the addons in bin/ for examples of usage.
hledgerCommandMode :: HelpTemplate -> [Flag RawOpts] -> [(Help, [Flag RawOpts])]
-> [Flag RawOpts] -> ([Arg RawOpts], Maybe (Arg RawOpts)) -> Mode RawOpts
hledgerCommandMode tmpl ungroupedflags groupedflags hiddenflags args =
case parseHelpTemplate tmpl of
Nothing -> error' $ "Could not parse help template:\n"++tmpl++"\n"
Just (names, preamble, postamblelines) ->
(defCommandMode names) {
modeHelp = preamble
,modeHelpSuffix = postamblelines
,modeGroupFlags = Group {
groupUnnamed = ungroupedflags
,groupNamed = groupedflags
,groupHidden = hiddenflags
}
,modeArgs = args
}
-- | Built-in descriptions for some of the known addons. -- | Built-in descriptions for some of the known addons.
standardAddonsHelp :: [(String,String)] standardAddonsHelp :: [(String,String)]
standardAddonsHelp = [ standardAddonsHelp = [

View File

@ -99,7 +99,7 @@ mainmode addons = defMode {
]) ])
] ]
++ case addons of [] -> [] ++ case addons of [] -> []
cs -> [("\nAdd-on commands", map defAddonCommandMode cs)] cs -> [("\nAdd-on commands", map quickAddonCommandMode cs)]
-- modes in the unnamed group, shown first without a heading: -- modes in the unnamed group, shown first without a heading:
,groupUnnamed = [ ,groupUnnamed = [
helpmode helpmode