cli, addons: reduce boilerplate a little with hledgerCommandMode helper
This commit is contained in:
		
							parent
							
								
									9f8e96d189
								
							
						
					
					
						commit
						b7092f278b
					
				| @ -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++")") | ||||||
|   |] |   ,flagReq ["chart-items"]  (\s opts -> Right $ setopt "chart-items" s opts) "N" ("number of accounts to show (default: "++show defchartitems++")") | ||||||
|   ,modeGroupFlags = Group { |   ,flagReq ["chart-size"]  (\s opts -> Right $ setopt "chart-size" s opts) "WIDTHxHEIGHT" ("image size (default: "++defchartsize++")") | ||||||
|      groupNamed = [generalflagsgroup1] |   ]  [generalflagsgroup1] | ||||||
|     ,groupUnnamed = [ |   [] | ||||||
|          flagReq ["chart-output","o"]  (\s opts -> Right $ setopt "chart-output" s opts) "IMGFILE" ("output filename (default: "++defchartoutput++")") |   ([], Just $ argsFlag "[QUERY]") | ||||||
|         ,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++")") |  | ||||||
|         ] |  | ||||||
|     ,groupHidden = [] |  | ||||||
|     } |  | ||||||
|   ,modeArgs = ([], Just $ argsFlag "[QUERY] --add-posting \"ACCT  AMTEXPR\" ...") |  | ||||||
|   } |  | ||||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ||||||
| 
 | 
 | ||||||
| data ChartOpts = ChartOpts { | data ChartOpts = ChartOpts { | ||||||
|  | |||||||
| @ -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 () | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -16,18 +16,16 @@ 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, | ||||||
| eg at the beginning of the year. | eg at the beginning of the year. | ||||||
| @ -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 () | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -26,16 +26,12 @@ 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, | ||||||
| but adds one or more specified postings to any transactions matching QUERY. | but adds one or more specified postings to any transactions matching QUERY. | ||||||
| @ -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) { |            "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." | ||||||
|     groupUnnamed = [ |   ,flagNone ["diff"] (setboolopt "diff") "generate diff suitable as an input for patch tool" | ||||||
|        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." |   [generalflagsgroup1] | ||||||
|       ,flagNone ["diff"] (setboolopt "diff") "generate diff suitable as an input for patch tool" |   [] | ||||||
|       ] |   ([], 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 | ||||||
|  | |||||||
| @ -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 = [ | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user