From 0a889366ce1b419621e715913c45ff4e6a66bf22 Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Tue, 12 Mar 2024 12:54:51 +1100 Subject: [PATCH 1/9] use getopt for cli args this allows long and short options --- go.mod | 1 + go.sum | 2 ++ pokesay.go | 34 +++++++++++++++++++--------------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index d40ed6c4..ac79a551 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/pborman/getopt/v2 v2.1.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect diff --git a/go.sum b/go.sum index 12f3c94d..c8bf6679 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA= +github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= diff --git a/pokesay.go b/pokesay.go index a179bdd2..8c4ce31e 100644 --- a/pokesay.go +++ b/pokesay.go @@ -2,10 +2,10 @@ package main import ( "embed" - "flag" "fmt" "strings" + "github.com/pborman/getopt/v2" "github.com/tmck-code/pokesay/src/pokedex" "github.com/tmck-code/pokesay/src/pokesay" "github.com/tmck-code/pokesay/src/timer" @@ -29,33 +29,37 @@ var ( CategoryRoot string = "build/assets/categories" MetadataRoot string = "build/assets/metadata" CowDataRoot string = "build/assets/cows" + + verbose bool ) func parseFlags() pokesay.Args { // list operations - listCategories := flag.Bool("list-categories", false, "list all available categories") - listNames := flag.Bool("list-names", false, "list all available names") + listCategories := getopt.BoolLong("list-categories", 'L', "list all available categories") + listNames := getopt.BoolLong("list-names", 'l', "list all available names") // selection/filtering - category := flag.String("category", "", "choose a pokemon from a specific category") - name := flag.String("name", "", "choose a pokemon from a specific name") - width := flag.Int("width", 80, "the max speech bubble width") + category := getopt.StringLong("category", 'c', "", "choose a pokemon from a specific category") + name := getopt.StringLong("name", 'n', "", "choose a pokemon from a specific name") + width := getopt.IntLong("width", 'w', 80, "the max speech bubble width") + + getopt.FlagLong(&verbose, "verbose", 'v', "print verbose output", "verbose") // speech bubble options - noWrap := flag.Bool("no-wrap", false, "disable text wrapping (fastest)") - tabWidth := flag.Int("tab-width", 4, "replace any tab characters with N spaces") - noTabSpaces := flag.Bool("no-tab-spaces", false, "do not replace tab characters (fastest)") - fastest := flag.Bool("fastest", false, "run with the fastest possible configuration (-nowrap -notabspaces)") + noWrap := getopt.BoolLong("no-wrap", 'W', "disable text wrapping Long(fastest)") + tabWidth := getopt.IntLong("tab-width", 't', 4, "replace any tab characters with N spaces") + noTabSpaces := getopt.BoolLong("no-tab-spaces", 's', "do not replace tab characters Long(fastest)") + fastest := getopt.BoolLong("fastest", 'f', "run with the fastest possible configuration Long(-nowrap -notabspaces)") // info box options - japaneseName := flag.Bool("japanese-name", false, "print the japanese name") - noCategoryInfo := flag.Bool("no-category-info", false, "do not print pokemon categories") - drawInfoBorder := flag.Bool("info-border", false, "draw a border around the info line") + japaneseName := getopt.BoolLong("japanese-name", 'j', "print the japanese name") + noCategoryInfo := getopt.BoolLong("no-category-info", 'C', "do not print pokemon categories") + drawInfoBorder := getopt.BoolLong("info-border", 'b', "draw a border around the info line") // other option - unicodeBorders := flag.Bool("unicode-borders", false, "use unicode characters to draw the border around the speech box (and info box if -info-border is enabled)") + unicodeBorders := getopt.BoolLong("unicode-borders", 'u', "use unicode characters to draw the border around the speech box Long(and info box if -info-border is enabled)") - flag.Parse() + getopt.Parse() var args pokesay.Args if *fastest { From a60b2f3e0653b5b5ab8d2ad46a251b25a4c2f037 Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Tue, 19 Mar 2024 23:06:18 +1100 Subject: [PATCH 2/9] print usage info with -h/--help --- pokesay.go | 10 ++++++++++ src/pokesay/print.go | 1 + 2 files changed, 11 insertions(+) diff --git a/pokesay.go b/pokesay.go index 4caa109c..adfd9e69 100644 --- a/pokesay.go +++ b/pokesay.go @@ -34,6 +34,9 @@ var ( ) func parseFlags() pokesay.Args { + // print usage with -h, --help + help := getopt.BoolLong("help", 'h', "display this help message") + // list operations listCategories := getopt.BoolLong("list-categories", 'L', "list all available categories") listNames := getopt.BoolLong("list-names", 'l', "list all available names") @@ -84,6 +87,7 @@ func parseFlags() pokesay.Args { JapaneseName: *japaneseName, BoxCharacters: pokesay.DetermineBoxCharacters(*unicodeBorders), DrawInfoBorder: *drawInfoBorder, + Help: *help, } } return args @@ -189,6 +193,12 @@ func runPrintRandom(args pokesay.Args) { func main() { args := parseFlags() + // if the -h/--help flag is set, print usage and exit + if args.Help { + getopt.Usage() + return + } + t := timer.NewTimer("main", true) if args.ListCategories { diff --git a/src/pokesay/print.go b/src/pokesay/print.go index f74d421f..f4fcff19 100644 --- a/src/pokesay/print.go +++ b/src/pokesay/print.go @@ -38,6 +38,7 @@ type Args struct { JapaneseName bool BoxCharacters *BoxCharacters DrawInfoBorder bool + Help bool } var ( From 86244b9645aef698a5665f81aa429200ca5e6c4c Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Tue, 19 Mar 2024 23:14:38 +1100 Subject: [PATCH 3/9] add help arg to all modes --- pokesay.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pokesay.go b/pokesay.go index adfd9e69..4b7a9336 100644 --- a/pokesay.go +++ b/pokesay.go @@ -72,6 +72,7 @@ func parseFlags() pokesay.Args { TabSpaces: " ", NoTabSpaces: true, BoxCharacters: pokesay.DetermineBoxCharacters(false), + Help: *help, } } else { args = pokesay.Args{ From 5d5f8db8420ceb0e3becb9641d76dc18c1048b1b Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Tue, 19 Mar 2024 23:15:26 +1100 Subject: [PATCH 4/9] rearrange arg order --- pokesay.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pokesay.go b/pokesay.go index 4b7a9336..78ca30db 100644 --- a/pokesay.go +++ b/pokesay.go @@ -37,13 +37,14 @@ func parseFlags() pokesay.Args { // print usage with -h, --help help := getopt.BoolLong("help", 'h', "display this help message") + // selection/filtering + name := getopt.StringLong("name", 'n', "", "choose a pokemon from a specific name") + category := getopt.StringLong("category", 'c', "", "choose a pokemon from a specific category") + // list operations listCategories := getopt.BoolLong("list-categories", 'L', "list all available categories") listNames := getopt.BoolLong("list-names", 'l', "list all available names") - // selection/filtering - category := getopt.StringLong("category", 'c', "", "choose a pokemon from a specific category") - name := getopt.StringLong("name", 'n', "", "choose a pokemon from a specific name") width := getopt.IntLong("width", 'w', 80, "the max speech bubble width") getopt.FlagLong(&verbose, "verbose", 'v', "print verbose output", "verbose") From b26024e6fdc16d76d36f0c94c0fbc60dfc04fd32 Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Tue, 19 Mar 2024 23:27:00 +1100 Subject: [PATCH 5/9] print timer info with -v instead of using DEBUG=true pokesay ... --- pokesay.go | 18 +++++++++++------- src/pokesay/print.go | 1 + src/timer/timer.go | 8 ++++---- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/pokesay.go b/pokesay.go index 78ca30db..55835fb4 100644 --- a/pokesay.go +++ b/pokesay.go @@ -29,13 +29,13 @@ var ( CategoryRoot string = "build/assets/categories" MetadataRoot string = "build/assets/metadata" CowDataRoot string = "build/assets/cows" - - verbose bool ) func parseFlags() pokesay.Args { - // print usage with -h, --help + // print usage help := getopt.BoolLong("help", 'h', "display this help message") + // print verbose output (currently timer output) + verbose := getopt.BoolLong("verbose", 'v', "print verbose output", "verbose") // selection/filtering name := getopt.StringLong("name", 'n', "", "choose a pokemon from a specific name") @@ -47,8 +47,6 @@ func parseFlags() pokesay.Args { width := getopt.IntLong("width", 'w', 80, "the max speech bubble width") - getopt.FlagLong(&verbose, "verbose", 'v', "print verbose output", "verbose") - // speech bubble options noWrap := getopt.BoolLong("no-wrap", 'W', "disable text wrapping Long(fastest)") tabWidth := getopt.IntLong("tab-width", 't', 4, "replace any tab characters with N spaces") @@ -74,6 +72,7 @@ func parseFlags() pokesay.Args { NoTabSpaces: true, BoxCharacters: pokesay.DetermineBoxCharacters(false), Help: *help, + Verbose: *verbose, } } else { args = pokesay.Args{ @@ -90,6 +89,7 @@ func parseFlags() pokesay.Args { BoxCharacters: pokesay.DetermineBoxCharacters(*unicodeBorders), DrawInfoBorder: *drawInfoBorder, Help: *help, + Verbose: *verbose, } } return args @@ -137,7 +137,7 @@ func runPrintByName(args pokesay.Args) { t.Mark("read name struct") metadata, final := pokesay.ChooseByName(names, args.NameToken, GOBCowNames, MetadataRoot) - t.Mark("find and read metadata") + t.Mark("find/read metadata") pokesay.Print(args, final.EntryIndex, GenerateNames(metadata, args), final.Categories, GOBCowData) t.Mark("print") @@ -167,7 +167,7 @@ func runPrintByNameAndCategory(args pokesay.Args) { t.Mark("read name struct") metadata, final := pokesay.ChooseByNameAndCategory(names, args.NameToken, GOBCowNames, MetadataRoot, args.Category) - t.Mark("find and read metadata") + t.Mark("find/read metadata") pokesay.Print(args, final.EntryIndex, GenerateNames(metadata, args), final.Categories, GOBCowData) t.Mark("print") @@ -200,6 +200,10 @@ func main() { getopt.Usage() return } + if args.Verbose { + fmt.Println("Verbose output enabled") + timer.DEBUG = true + } t := timer.NewTimer("main", true) diff --git a/src/pokesay/print.go b/src/pokesay/print.go index f4fcff19..43f06030 100644 --- a/src/pokesay/print.go +++ b/src/pokesay/print.go @@ -39,6 +39,7 @@ type Args struct { BoxCharacters *BoxCharacters DrawInfoBorder bool Help bool + Verbose bool } var ( diff --git a/src/timer/timer.go b/src/timer/timer.go index 5cdb4ffe..2089b691 100644 --- a/src/timer/timer.go +++ b/src/timer/timer.go @@ -23,10 +23,10 @@ type Timer struct { Enabled bool } -func NewTimer(name string, alignKeys ...bool) *Timer { +func NewTimer(name string, boolArgs ...bool) *Timer { align := false - if len(alignKeys) == 1 { - align = alignKeys[0] + if len(boolArgs) == 1 { + align = boolArgs[0] } t := &Timer{ Name: name, @@ -48,7 +48,7 @@ func (t *Timer) Mark(stage string) { } now := time.Now() if t.AlignKeys { - stage = fmt.Sprintf("%02d.%-15s", len(t.stageNames), stage) + stage = fmt.Sprintf("%02d.%-20s", len(t.stageNames), stage) } else { stage = fmt.Sprintf("%02d.%s", len(t.stageNames), stage) } From 9fcfe3b1ff38282a3dc881665a49e0263abc6ec8 Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Tue, 19 Mar 2024 23:49:19 +1100 Subject: [PATCH 6/9] update arg language --- pokesay.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pokesay.go b/pokesay.go index ca77afc0..2234b195 100644 --- a/pokesay.go +++ b/pokesay.go @@ -32,7 +32,6 @@ var ( ) func parseFlags() pokesay.Args { - // print usage help := getopt.BoolLong("help", 'h', "display this help message") // print verbose output (currently timer output) verbose := getopt.BoolLong("verbose", 'v', "print verbose output", "verbose") @@ -42,24 +41,24 @@ func parseFlags() pokesay.Args { category := getopt.StringLong("category", 'c', "", "choose a pokemon from a specific category") // list operations - listCategories := getopt.BoolLong("list-categories", 'L', "list all available categories") listNames := getopt.BoolLong("list-names", 'l', "list all available names") + listCategories := getopt.BoolLong("list-categories", 'L', "list all available categories") width := getopt.IntLong("width", 'w', 80, "the max speech bubble width") // speech bubble options - noWrap := getopt.BoolLong("no-wrap", 'W', "disable text wrapping Long(fastest)") tabWidth := getopt.IntLong("tab-width", 't', 4, "replace any tab characters with N spaces") - noTabSpaces := getopt.BoolLong("no-tab-spaces", 's', "do not replace tab characters Long(fastest)") - fastest := getopt.BoolLong("fastest", 'f', "run with the fastest possible configuration Long(-nowrap -notabspaces)") + noWrap := getopt.BoolLong("no-wrap", 'W', "disable text wrapping (fastest)") + noTabSpaces := getopt.BoolLong("no-tab-spaces", 's', "do not replace tab characters (fastest)") + fastest := getopt.BoolLong("fastest", 'f', "run with the fastest possible configuration (-nowrap -notabspaces)") // info box options - japaneseName := getopt.BoolLong("japanese-name", 'j', "print the japanese name") - noCategoryInfo := getopt.BoolLong("no-category-info", 'C', "do not print pokemon categories") - drawInfoBorder := getopt.BoolLong("info-border", 'b', "draw a border around the info line") + japaneseName := getopt.BoolLong("japanese-name", 'j', "print the japanese name in the info box") + noCategoryInfo := getopt.BoolLong("no-category-info", 'C', "do not print pokemon category information in the info box") + drawInfoBorder := getopt.BoolLong("info-border", 'b', "draw a border around the info box") // other option - unicodeBorders := getopt.BoolLong("unicode-borders", 'u', "use unicode characters to draw the border around the speech box Long(and info box if -info-border is enabled)") + unicodeBorders := getopt.BoolLong("unicode-borders", 'u', "use unicode characters to draw the border around the speech box (and info box if -info-border is enabled)") getopt.Parse() var args pokesay.Args From cf7b66b8c0c8bd3adb85cde21e3c97ab52350d1d Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Wed, 20 Mar 2024 00:04:57 +1100 Subject: [PATCH 7/9] update arg format in help docs --- pokesay.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pokesay.go b/pokesay.go index 2234b195..bf669f11 100644 --- a/pokesay.go +++ b/pokesay.go @@ -50,7 +50,7 @@ func parseFlags() pokesay.Args { tabWidth := getopt.IntLong("tab-width", 't', 4, "replace any tab characters with N spaces") noWrap := getopt.BoolLong("no-wrap", 'W', "disable text wrapping (fastest)") noTabSpaces := getopt.BoolLong("no-tab-spaces", 's', "do not replace tab characters (fastest)") - fastest := getopt.BoolLong("fastest", 'f', "run with the fastest possible configuration (-nowrap -notabspaces)") + fastest := getopt.BoolLong("fastest", 'f', "run with the fastest possible configuration (--nowrap & --notabspaces)") // info box options japaneseName := getopt.BoolLong("japanese-name", 'j', "print the japanese name in the info box") @@ -58,7 +58,7 @@ func parseFlags() pokesay.Args { drawInfoBorder := getopt.BoolLong("info-border", 'b', "draw a border around the info box") // other option - unicodeBorders := getopt.BoolLong("unicode-borders", 'u', "use unicode characters to draw the border around the speech box (and info box if -info-border is enabled)") + unicodeBorders := getopt.BoolLong("unicode-borders", 'u', "use unicode characters to draw the border around the speech box (and info box if --info-border is enabled)") getopt.Parse() var args pokesay.Args From 3b2fff195702fb12a1c403b0c1781bf4c881cbe5 Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Wed, 20 Mar 2024 00:09:53 +1100 Subject: [PATCH 8/9] add a readme section for full cli usage --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 1c867d55..a785c2d0 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Print pokemon in the CLI! An adaptation of the classic "cowsay" - [pokesay](#pokesay) - [One-line installs](#one-line-installs) - [Usage](#usage) + - [Full Usage](#full-usage) - [How it works](#how-it-works) - [Building binaries](#building-binaries) - [On your host OS](#on-your-host-os) @@ -64,6 +65,38 @@ echo 'fortune | pokesay' >> $HOME/.bashrc > _Note: The pokesay tool is intended to only be used with piped text input from STDIN, entering text by typing (or other methods) might not work as expected!_ +### Full Usage + +> Run pokesay with `-h` or `--help` to see the full usage + +```shell +Usage: pokesay [-bCfhjLlsuvW] [-c value] [-n value] [-t value] [-w value] [parameters ...] + -b, --info-border draw a border around the info box + -c, --category=value + choose a pokemon from a specific category + -C, --no-category-info + do not print pokemon category information in the info box + -f, --fastest run with the fastest possible configuration (--nowrap & + --notabspaces) + -h, --help display this help message + -j, --japanese-name + print the japanese name in the info box + -L, --list-categories + list all available categories + -l, --list-names list all available names + -n, --name=value choose a pokemon from a specific name + -s, --no-tab-spaces + do not replace tab characters (fastest) + -t, --tab-width=value + replace any tab characters with N spaces [4] + -u, --unicode-borders + use unicode characters to draw the border around the speech + box (and info box if --info-border is enabled) + -v, --verbose print verbose output + -W, --no-wrap disable text wrapping (fastest) + -w, --width=value the max speech bubble width [80] + ``` + --- ## How it works From 73fb0fffadf4a82fd137e852305e461180190a27 Mon Sep 17 00:00:00 2001 From: Tom McKeesick Date: Wed, 20 Mar 2024 00:10:07 +1100 Subject: [PATCH 9/9] move todo item --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a785c2d0..2ebbf317 100644 --- a/README.md +++ b/README.md @@ -201,9 +201,10 @@ DEBUG=test go test -v ./test/ ## TODO - Short-term - - [ ] support long and short cli args (e.g. --name/-n) - [ ] optionally print ID assigned to each pokemon, support deterministic selection via the same ID - Longer-term +- In Beta + - [x] support long and short cli args (e.g. --name/-n) - Completed - [x] Make the category struct faster to load - currently takes up to 80% of the execution time - [x] Store metadata and names in a more storage-efficient manner