From f156ac0035639f2d3ae962ba0d4092030a7803a1 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 15 Apr 2025 09:22:06 -0700 Subject: [PATCH 01/18] updating version numbers --- .github/workflows/ci.yml | 2 +- .goreleaser.yaml | 2 +- Dockerfile | 2 +- README.md | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b0c9a49..3cf6bfd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ on: - main env: - VERSION_NUMBER: 'v1.2.1' + VERSION_NUMBER: 'v1.2.2' DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli' AWS_REGION: 'us-west-2' diff --git a/.goreleaser.yaml b/.goreleaser.yaml index fc81a6eb..08ad1b45 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -14,7 +14,7 @@ builds: - windows - darwin ldflags: - - -s -w -X main.version=v1.2.1 + - -s -w -X main.version=v1.2.2 archives: - format: tar.gz diff --git a/Dockerfile b/Dockerfile index 936228e4..48696a02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN go mod download COPY . . -RUN go build -ldflags "-X main.version=v1.2.1" -o poke-cli . +RUN go build -ldflags "-X main.version=v1.2.2" -o poke-cli . # build 2 FROM --platform=$BUILDPLATFORM alpine:latest diff --git a/README.md b/README.md index 9e6fabc2..e4e20b68 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ pokemon-logo

Pokémon CLI

version-label - docker-image-size + docker-image-size ci-status-badge
@@ -76,11 +76,11 @@ View future plans in the [Roadmap](#roadmap) section. 3. Choose how to interact with the container: * Run a single command and exit: ```bash - docker run --rm -it digitalghostdev/poke-cli:v1.2.1 [subcommand] flag] + docker run --rm -it digitalghostdev/poke-cli:v1.2.2 [subcommand] flag] ``` * Enter the container and use its shell: ```bash - docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.2.1 -c "cd /app && exec sh" + docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.2.2 -c "cd /app && exec sh" # placed into the /app directory, run the program with './poke-cli' # example: ./poke-cli ability swift-swim ``` @@ -145,7 +145,7 @@ Below is a list of the planned/completed commands and flags: - [x] `move`: get data about a specific move. - [ ] `-p | --pokemon`: display Pokémon that learn this move. - [x] `natures`: get data about natures. -- [ ] `pokemon`: get data about a specific Pokémon. +- [x] `pokemon`: get data about a specific Pokémon. - [x] `-a | --abilities`: display the Pokémon's abilities. - [x] `-i | --image`: display a pixel image of the Pokémon. - [x] `-s | --stats`: display the Pokémon's base stats. From 83c6e68067b2b285b73cecc533a475bc66839ffa Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 17 Apr 2025 14:55:43 -0700 Subject: [PATCH 02/18] initial deconstruction of types package (#130) --- cmd/{types.go => types/damage_table.go} | 115 +---------------------- cmd/types/types.go | 119 ++++++++++++++++++++++++ cmd/{ => types}/types_test.go | 7 +- 3 files changed, 126 insertions(+), 115 deletions(-) rename cmd/{types.go => types/damage_table.go} (53%) create mode 100644 cmd/types/types.go rename cmd/{ => types}/types_test.go (90%) diff --git a/cmd/types.go b/cmd/types/damage_table.go similarity index 53% rename from cmd/types.go rename to cmd/types/damage_table.go index f1ee2e7f..1f91e4fa 100644 --- a/cmd/types.go +++ b/cmd/types/damage_table.go @@ -1,10 +1,7 @@ -package cmd +package types import ( - "flag" "fmt" - "github.com/charmbracelet/bubbles/table" - tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/term" "github.com/digitalghost-dev/poke-cli/connections" @@ -15,47 +12,8 @@ import ( "strings" ) -type model struct { - table table.Model - selectedOption string // Track the selected option -} - -func (m model) Init() tea.Cmd { return nil } - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "q", "esc", "ctrl+c": - m.selectedOption = "quit" - return m, tea.Quit - case "enter": - selectedRow := m.table.SelectedRow() - m.selectedOption = selectedRow[0] - return m, tea.Batch( - tea.Quit, - ) - } - } - m.table, cmd = m.table.Update(msg) - return m, cmd -} - -func (m model) View() string { - // When an option is selected, no longer display the table. - if m.selectedOption != "" { - return "" - } - // Otherwise, display the table - return "Select a type!\n" + - styling.TypesTableBorder.Render(m.table.View()) + - "\n" + - styling.KeyMenu.Render("↑ (move up) • ↓ (move down)\nenter (select) • ctrl+c | esc (quit)") -} - -// Function to display type details after a type is selected -func displayTypeDetails(typesName string, endpoint string) { +// DamageTable Function to display type details after a type is selected +func DamageTable(typesName string, endpoint string) { // Setting up variables to style the list var columnWidth = 11 var subtle = lipgloss.AdaptiveColor{Light: "#D9DCCF", Dark: "#383838"} @@ -139,70 +97,3 @@ func displayTypeDetails(typesName string, endpoint string) { // Print the rendered document fmt.Println(docStyle.Render(doc.String())) } - -// Function that generates and handles the type selection table -func tableGeneration(endpoint string) table.Model { - columns := []table.Column{{Title: "Type", Width: 16}} - rows := []table.Row{ - {"Normal"}, {"Fire"}, {"Water"}, {"Electric"}, {"Grass"}, {"Ice"}, - {"Fighting"}, {"Poison"}, {"Ground"}, {"Flying"}, {"Psychic"}, {"Bug"}, - {"Rock"}, {"Ghost"}, {"Dragon"}, {"Steel"}, {"Fairy"}, - } - - t := table.New( - table.WithColumns(columns), - table.WithRows(rows), - table.WithFocused(true), - table.WithHeight(7), - ) - - s := table.DefaultStyles() - s.Header = s.Header.BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("#FFCC00")).BorderBottom(true) - s.Selected = s.Selected.Foreground(lipgloss.Color("#000")).Background(lipgloss.Color("#FFCC00")) - t.SetStyles(s) - - m := model{table: t} - programModel, err := tea.NewProgram(m).Run() - if err != nil { - fmt.Println("Error running program:", err) - os.Exit(1) - } - - // Access the selected option from the model - finalModel, ok := programModel.(model) - if !ok { - fmt.Println("Error: could not retrieve final model") - os.Exit(1) - } - - if finalModel.selectedOption != "quit" { - typesName := strings.ToLower(finalModel.selectedOption) - displayTypeDetails(typesName, endpoint) // Call function to display type details - } - - return t -} - -func TypesCommand() { - flag.Usage = func() { - helpMessage := styling.HelpBorder.Render( - "Get details about a specific typing.\n\n", - styling.StyleBold.Render("USAGE:"), - fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("types"), "[flag]"), - "\n\n", - styling.StyleBold.Render("FLAGS:"), - fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints out the help menu."), - ) - fmt.Println(helpMessage) - } - - flag.Parse() - - if err := ValidateTypesArgs(os.Args); err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } - - endpoint := strings.ToLower(os.Args[1])[0:4] - tableGeneration(endpoint) -} diff --git a/cmd/types/types.go b/cmd/types/types.go new file mode 100644 index 00000000..7c2d2b55 --- /dev/null +++ b/cmd/types/types.go @@ -0,0 +1,119 @@ +package types + +import ( + "flag" + "fmt" + "github.com/charmbracelet/bubbles/table" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/digitalghost-dev/poke-cli/cmd" + "github.com/digitalghost-dev/poke-cli/styling" + "os" + "strings" +) + +type model struct { + table table.Model + selectedOption string // Track the selected option +} + +func (m model) Init() tea.Cmd { return nil } + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var bubbleCmd tea.Cmd + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q", "esc", "ctrl+c": + m.selectedOption = "quit" + return m, tea.Quit + case "enter": + selectedRow := m.table.SelectedRow() + m.selectedOption = selectedRow[0] + return m, tea.Batch( + tea.Quit, + ) + } + } + m.table, bubbleCmd = m.table.Update(msg) + return m, bubbleCmd +} + +func (m model) View() string { + // When an option is selected, no longer display the table. + if m.selectedOption != "" { + return "" + } + // Otherwise, display the table + return "Select a type!\n" + + styling.TypesTableBorder.Render(m.table.View()) + + "\n" + + styling.KeyMenu.Render("↑ (move up) • ↓ (move down)\nenter (select) • ctrl+c | esc (quit)") +} + +// Function that generates and handles the type selection table +func tableGeneration(endpoint string) table.Model { + columns := []table.Column{{Title: "Type", Width: 16}} + rows := []table.Row{ + {"Normal"}, {"Fire"}, {"Water"}, {"Electric"}, {"Grass"}, {"Ice"}, + {"Fighting"}, {"Poison"}, {"Ground"}, {"Flying"}, {"Psychic"}, {"Bug"}, + {"Rock"}, {"Ghost"}, {"Dragon"}, {"Steel"}, {"Fairy"}, + } + + t := table.New( + table.WithColumns(columns), + table.WithRows(rows), + table.WithFocused(true), + table.WithHeight(7), + ) + + s := table.DefaultStyles() + s.Header = s.Header.BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("#FFCC00")).BorderBottom(true) + s.Selected = s.Selected.Foreground(lipgloss.Color("#000")).Background(lipgloss.Color("#FFCC00")) + t.SetStyles(s) + + m := model{table: t} + programModel, err := tea.NewProgram(m).Run() + if err != nil { + fmt.Println("Error running program:", err) + os.Exit(1) + } + + // Access the selected option from the model + finalModel, ok := programModel.(model) + if !ok { + fmt.Println("Error: could not retrieve final model") + os.Exit(1) + } + + if finalModel.selectedOption != "quit" { + typesName := strings.ToLower(finalModel.selectedOption) + DamageTable(typesName, endpoint) // Call function to display type details + } + + return t +} + +func TypesCommand() { + flag.Usage = func() { + helpMessage := styling.HelpBorder.Render( + "Get details about a specific typing.\n\n", + styling.StyleBold.Render("USAGE:"), + fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("types"), "[flag]"), + "\n\n", + styling.StyleBold.Render("FLAGS:"), + fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints out the help menu."), + ) + fmt.Println(helpMessage) + } + + flag.Parse() + + if err := cmd.ValidateTypesArgs(os.Args); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + endpoint := strings.ToLower(os.Args[1])[0:4] + tableGeneration(endpoint) +} diff --git a/cmd/types_test.go b/cmd/types/types_test.go similarity index 90% rename from cmd/types_test.go rename to cmd/types/types_test.go index aa4c3a69..221b6a47 100644 --- a/cmd/types_test.go +++ b/cmd/types/types_test.go @@ -1,6 +1,7 @@ -package cmd +package types import ( + "github.com/digitalghost-dev/poke-cli/cmd" "github.com/digitalghost-dev/poke-cli/styling" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,7 +15,7 @@ func TestValidateTypesArgs_ValidInput(t *testing.T) { } for _, input := range validInputs { - err := ValidateTypesArgs(input) + err := cmd.ValidateTypesArgs(input) assert.NoError(t, err, "Expected no error for valid input") } } @@ -26,7 +27,7 @@ func TestValidateTypesArgs_TooManyArgs(t *testing.T) { expectedError := "error, too many arguments\n" for _, input := range invalidInputs { - err := ValidateTypesArgs(input) + err := cmd.ValidateTypesArgs(input) require.Error(t, err, "Expected error for too many arguments") assert.NotEqual(t, expectedError, err.Error()) } From 12484658d2ca02f4d283892e12e7d8498c1eae40 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 18 Apr 2025 14:39:56 -0700 Subject: [PATCH 03/18] adding updated help flag checked (#133) --- cmd/natures.go | 9 +++++++++ cmd/natures_test.go | 35 ++--------------------------------- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/cmd/natures.go b/cmd/natures.go index 298762b0..a922a5d1 100644 --- a/cmd/natures.go +++ b/cmd/natures.go @@ -21,6 +21,15 @@ func NaturesCommand() { flag.Parse() + // Check for help flag + for _, arg := range os.Args[1:] { + if arg == "-h" || arg == "--help" { + flag.Usage() + + return + } + } + if err := ValidateNaturesArgs(os.Args); err != nil { fmt.Println(err.Error()) os.Exit(1) diff --git a/cmd/natures_test.go b/cmd/natures_test.go index afcd4f86..4755ea58 100644 --- a/cmd/natures_test.go +++ b/cmd/natures_test.go @@ -4,13 +4,12 @@ import ( "bytes" "fmt" "github.com/digitalghost-dev/poke-cli/styling" + "github.com/stretchr/testify/assert" "os" - "strings" "testing" ) func captureNaturesOutput(f func()) string { - // Create a pipe to capture standard output r, w, _ := os.Pipe() defer func(r *os.File) { err := r.Close() @@ -19,21 +18,17 @@ func captureNaturesOutput(f func()) string { } }(r) - // Redirect os.Stdout to the write end of the pipe oldStdout := os.Stdout os.Stdout = w defer func() { os.Stdout = oldStdout }() - // Run the function f() - // Close the write end of the pipe err := w.Close() if err != nil { return "" } - // Read the captured output var buf bytes.Buffer _, _ = buf.ReadFrom(r) return buf.String() @@ -58,29 +53,6 @@ func TestNaturesCommand(t *testing.T) { "╰──────────────────────────────╯\n"), expectedError: false, }, - { - name: "Valid Execution", - args: []string{"natures"}, - expectedOutput: styling.StripANSI( - "Natures affect the growth of a Pokémon.\n" + - "Each nature increases one of its stats by 10% and decreases one by 10%.\n" + - "Five natures increase and decrease the same stat and therefore have no effect.\n\n" + - "Nature Chart:\n" + - "┌──────────┬─────────┬──────────┬──────────┬──────────┬─────────┐\n" + - "│ │ -Attack │ -Defense │ -Sp. Atk │ -Sp. Def │ Speed │\n" + - "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + - "│ +Attack │ Hardy │ Lonely │ Adamant │ Naughty │ Brave │\n" + - "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + - "│ +Defense │ Bold │ Docile │ Impish │ Lax │ Relaxed │\n" + - "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + - "│ +Sp. Atk │ Modest │ Mild │ Bashful │ Rash │ Quiet │\n" + - "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + - "│ +Sp. Def │ Calm │ Gentle │ Careful │ Quirky │ Sassy │\n" + - "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + - "│ Speed │ Timid │ Hasty │ Jolly │ Naive │ Serious │\n" + - "└──────────┴─────────┴──────────┴──────────┴──────────┴─────────┘\n"), - expectedError: false, - }, } for _, tt := range tests { @@ -107,10 +79,7 @@ func TestNaturesCommand(t *testing.T) { cleanOutput := styling.StripANSI(output) - // Check output - if !strings.Contains(cleanOutput, tt.expectedOutput) { - t.Errorf("Output mismatch.\nExpected to contain:\n%s\nGot:\n%s", tt.expectedOutput, output) - } + assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should equal the expected string") }) } } From 240c89ea979c36131cc34f756456b7bdd7ff2c04 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 18 Apr 2025 14:40:29 -0700 Subject: [PATCH 04/18] initial commit (#130) --- cmd/types/damage_table_test.go | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 cmd/types/damage_table_test.go diff --git a/cmd/types/damage_table_test.go b/cmd/types/damage_table_test.go new file mode 100644 index 00000000..f0fab2df --- /dev/null +++ b/cmd/types/damage_table_test.go @@ -0,0 +1,43 @@ +package types + +import ( + "bytes" + "os" + "strings" + "testing" +) + +func TestDamageTable(t *testing.T) { + originalStdout := os.Stdout + + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("Failed to create pipe: %v", err) + } + + os.Stdout = w + + DamageTable("fire", "type") + + err = w.Close() + if err != nil { + t.Fatalf("Failed to close pipe: %v", err) + } + os.Stdout = originalStdout + + var buf bytes.Buffer + _, err = buf.ReadFrom(r) + if err != nil { + t.Fatalf("Failed to read from pipe: %v", err) + } + output := buf.String() + + // Step 7: Assert the output contains expected strings + if !strings.Contains(output, "You selected the Fire type.") { + t.Errorf("Expected output to contain Fire type header, got:\n%s", output) + } + + if !strings.Contains(output, "Damage Chart:") { + t.Errorf("Expected output to contain 'Damage Chart:', got:\n%s", output) + } +} From c6f6f4804a9813bcad715633119c52322cdabf27 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 18 Apr 2025 14:54:32 -0700 Subject: [PATCH 05/18] updating to go v1.24.1 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 193f63b6..8855bc6e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/digitalghost-dev/poke-cli -go 1.23.6 +go 1.24.1 require ( github.com/charmbracelet/bubbles v0.20.0 From 055ee8458938ce56e8c01ba5453cff0b1b6a6eb6 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 20 Apr 2025 20:47:18 -0700 Subject: [PATCH 06/18] bringing in types package (#130) --- cli.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli.go b/cli.go index 7b1b0be8..a5335fc9 100644 --- a/cli.go +++ b/cli.go @@ -6,6 +6,7 @@ import ( "github.com/digitalghost-dev/poke-cli/cmd" "github.com/digitalghost-dev/poke-cli/cmd/move" "github.com/digitalghost-dev/poke-cli/cmd/search" + "github.com/digitalghost-dev/poke-cli/cmd/types" "github.com/digitalghost-dev/poke-cli/flags" "github.com/digitalghost-dev/poke-cli/styling" "os" @@ -92,7 +93,7 @@ func runCLI(args []string) int { "move": move.MoveCommand, "natures": cmd.NaturesCommand, "pokemon": cmd.PokemonCommand, - "types": cmd.TypesCommand, + "types": types.TypesCommand, "search": search.SearchCommand, } From 82d707ce041f20205d71664c165aba26ef3cda84 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 21 Apr 2025 20:53:52 -0700 Subject: [PATCH 07/18] updating tests --- cmd/types/types_test.go | 117 ++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/cmd/types/types_test.go b/cmd/types/types_test.go index 221b6a47..e0510be2 100644 --- a/cmd/types/types_test.go +++ b/cmd/types/types_test.go @@ -1,55 +1,102 @@ package types import ( - "github.com/digitalghost-dev/poke-cli/cmd" + "bytes" + "fmt" "github.com/digitalghost-dev/poke-cli/styling" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "os" "testing" ) -func TestValidateTypesArgs_ValidInput(t *testing.T) { - validInputs := [][]string{ - {"poke-cli", "types"}, - {"poke-cli", "types", "-h"}, - } +func captureTypesOutput(f func()) string { + r, w, _ := os.Pipe() + defer func(r *os.File) { + err := r.Close() + if err != nil { + fmt.Println(err) + } + }(r) - for _, input := range validInputs { - err := cmd.ValidateTypesArgs(input) - assert.NoError(t, err, "Expected no error for valid input") - } -} + oldStdout := os.Stdout + os.Stdout = w + defer func() { os.Stdout = oldStdout }() -func TestValidateTypesArgs_TooManyArgs(t *testing.T) { - invalidInputs := [][]string{ - {"poke-cli", "types", "ground"}, + f() + err := w.Close() + if err != nil { + return "" } - expectedError := "error, too many arguments\n" - for _, input := range invalidInputs { - err := cmd.ValidateTypesArgs(input) - require.Error(t, err, "Expected error for too many arguments") - assert.NotEqual(t, expectedError, err.Error()) - } + var buf bytes.Buffer + _, _ = buf.ReadFrom(r) + return buf.String() } -func TestModelInit(t *testing.T) { - m := model{} - result := m.Init() +func TestTypesCommand(t *testing.T) { + tests := []struct { + name string + args []string + expectedOutput string + expectedError bool + }{ + { + name: "Help flag", + args: []string{"types", "-h"}, + expectedOutput: styling.StripANSI( + "╭───────────────────────────────────────────────────────────╮\n" + + "│Get details about a specific typing. │\n" + + "│ │\n" + + "│ USAGE: │\n" + + "│ poke-cli types [flag] │\n" + + "│ │\n" + + "│ FLAGS: │\n" + + "│ -h, --help Prints out the help menu│\n" + + "╰───────────────────────────────────────────────────────────╯\n"), + expectedError: false, + }, + { + name: "Help flag", + args: []string{"types", "--help"}, + expectedOutput: styling.StripANSI( + "╭───────────────────────────────────────────────────────────╮\n" + + "│Get details about a specific typing. │\n" + + "│ │\n" + + "│ USAGE: │\n" + + "│ poke-cli types [flag] │\n" + + "│ │\n" + + "│ FLAGS: │\n" + + "│ -h, --help Prints out the help menu│\n" + + "╰───────────────────────────────────────────────────────────╯\n"), + expectedError: false, + }, + } - assert.Nil(t, result, "Expected Init() to return nil") -} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + originalArgs := os.Args + defer func() { os.Args = originalArgs }() + + os.Args = append([]string{"poke-cli"}, tt.args...) -func TestModelView_DisplayTable(t *testing.T) { - m := model{selectedOption: ""} + output := captureTypesOutput(func() { + defer func() { + if r := recover(); r != nil && !tt.expectedError { + t.Fatalf("Unexpected error: %v", r) + } + }() + TypesCommand() + }) - // Construct the expected output exactly as `View()` should render it - expectedOutput := "Select a type!\n" + - styling.TypesTableBorder.Render(m.table.View()) + - "\n" + - styling.KeyMenu.Render("↑ (move up) • ↓ (move down)\nenter (select) • ctrl+c | esc (quit)") + cleanOutput := styling.StripANSI(output) - output := m.View() + assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should contain the expected string") + }) + } +} - assert.Equal(t, expectedOutput, output, "Expected View output to include table view") +func TestModelInit(t *testing.T) { + m := model{} + cmd := m.Init() + assert.Nil(t, cmd, "Init() should return nil") } From 024366d9d86adff58c9974641c67d9c0ddcc48c2 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 22 Apr 2025 10:32:44 -0700 Subject: [PATCH 08/18] adding help flag check (#133) --- cmd/natures.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cmd/natures.go b/cmd/natures.go index a922a5d1..803a2cda 100644 --- a/cmd/natures.go +++ b/cmd/natures.go @@ -21,13 +21,9 @@ func NaturesCommand() { flag.Parse() - // Check for help flag - for _, arg := range os.Args[1:] { - if arg == "-h" || arg == "--help" { - flag.Usage() - - return - } + if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { + flag.Usage() + return } if err := ValidateNaturesArgs(os.Args); err != nil { From a56154c32210e0a40daeb28707293449442df7f1 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 22 Apr 2025 15:40:39 -0700 Subject: [PATCH 09/18] refactoring/simplifying existing code (#130) --- cmd/types/types.go | 116 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/cmd/types/types.go b/cmd/types/types.go index 7c2d2b55..5e221a24 100644 --- a/cmd/types/types.go +++ b/cmd/types/types.go @@ -12,9 +12,38 @@ import ( "strings" ) +func TypesCommand() { + flag.Usage = func() { + helpMessage := styling.HelpBorder.Render( + "Get details about a specific typing.\n\n", + styling.StyleBold.Render("USAGE:"), + fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("types"), "[flag]"), + "\n\n", + styling.StyleBold.Render("FLAGS:"), + fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints out the help menu"), + ) + fmt.Println(helpMessage) + } + + flag.Parse() + + if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { + flag.Usage() + return + } + + if err := cmd.ValidateTypesArgs(os.Args); err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + endpoint := strings.ToLower(os.Args[1])[0:4] + tableGeneration(endpoint) +} + type model struct { table table.Model - selectedOption string // Track the selected option + selectedOption string } func (m model) Init() tea.Cmd { return nil } @@ -24,96 +53,63 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { - case "q", "esc", "ctrl+c": - m.selectedOption = "quit" + case "esc", "ctrl+c": return m, tea.Quit case "enter": - selectedRow := m.table.SelectedRow() - m.selectedOption = selectedRow[0] - return m, tea.Batch( - tea.Quit, - ) + m.selectedOption = m.table.SelectedRow()[0] + return m, tea.Quit } } + m.table, bubbleCmd = m.table.Update(msg) return m, bubbleCmd } func (m model) View() string { - // When an option is selected, no longer display the table. if m.selectedOption != "" { return "" } - // Otherwise, display the table - return "Select a type!\n" + - styling.TypesTableBorder.Render(m.table.View()) + - "\n" + - styling.KeyMenu.Render("↑ (move up) • ↓ (move down)\nenter (select) • ctrl+c | esc (quit)") + + return fmt.Sprintf("Select a type!\n%s\n%s", + styling.TypesTableBorder.Render(m.table.View()), + styling.KeyMenu.Render("↑ (move up) • ↓ (move down)\nenter (select) • ctrl+c | esc (quit)")) } // Function that generates and handles the type selection table func tableGeneration(endpoint string) table.Model { - columns := []table.Column{{Title: "Type", Width: 16}} - rows := []table.Row{ - {"Normal"}, {"Fire"}, {"Water"}, {"Electric"}, {"Grass"}, {"Ice"}, - {"Fighting"}, {"Poison"}, {"Ground"}, {"Flying"}, {"Psychic"}, {"Bug"}, - {"Rock"}, {"Ghost"}, {"Dragon"}, {"Steel"}, {"Fairy"}, + types := []string{"Normal", "Fire", "Water", "Electric", "Grass", "Ice", + "Fighting", "Poison", "Ground", "Flying", "Psychic", "Bug", + "Rock", "Ghost", "Dragon", "Steel", "Fairy"} + + rows := make([]table.Row, len(types)) + for i, t := range types { + rows[i] = []string{t} } t := table.New( - table.WithColumns(columns), + table.WithColumns([]table.Column{{Title: "Type", Width: 16}}), table.WithRows(rows), table.WithFocused(true), table.WithHeight(7), ) s := table.DefaultStyles() - s.Header = s.Header.BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("#FFCC00")).BorderBottom(true) - s.Selected = s.Selected.Foreground(lipgloss.Color("#000")).Background(lipgloss.Color("#FFCC00")) + s.Header = s.Header. + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("#FFCC00")). + BorderBottom(true) + s.Selected = s.Selected. + Foreground(lipgloss.Color("#000")). + Background(lipgloss.Color("#FFCC00")) t.SetStyles(s) m := model{table: t} - programModel, err := tea.NewProgram(m).Run() - if err != nil { + if programModel, err := tea.NewProgram(m).Run(); err != nil { fmt.Println("Error running program:", err) os.Exit(1) - } - - // Access the selected option from the model - finalModel, ok := programModel.(model) - if !ok { - fmt.Println("Error: could not retrieve final model") - os.Exit(1) - } - - if finalModel.selectedOption != "quit" { - typesName := strings.ToLower(finalModel.selectedOption) - DamageTable(typesName, endpoint) // Call function to display type details + } else if finalModel, ok := programModel.(model); ok && finalModel.selectedOption != "quit" { + DamageTable(strings.ToLower(finalModel.selectedOption), endpoint) } return t } - -func TypesCommand() { - flag.Usage = func() { - helpMessage := styling.HelpBorder.Render( - "Get details about a specific typing.\n\n", - styling.StyleBold.Render("USAGE:"), - fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("types"), "[flag]"), - "\n\n", - styling.StyleBold.Render("FLAGS:"), - fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints out the help menu."), - ) - fmt.Println(helpMessage) - } - - flag.Parse() - - if err := cmd.ValidateTypesArgs(os.Args); err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } - - endpoint := strings.ToLower(os.Args[1])[0:4] - tableGeneration(endpoint) -} From 9c8ae9bc7173fc3bea93a897b6817b45718aa9b8 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 22 Apr 2025 16:10:01 -0700 Subject: [PATCH 10/18] updating tests --- cmd/natures_test.go | 81 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/cmd/natures_test.go b/cmd/natures_test.go index 4755ea58..fdf4b72e 100644 --- a/cmd/natures_test.go +++ b/cmd/natures_test.go @@ -2,21 +2,24 @@ package cmd import ( "bytes" - "fmt" "github.com/digitalghost-dev/poke-cli/styling" "github.com/stretchr/testify/assert" "os" "testing" ) +var exitCode int + +func fakeExit(code int) { + exitCode = code + panic("exit") +} + func captureNaturesOutput(f func()) string { r, w, _ := os.Pipe() - defer func(r *os.File) { - err := r.Close() - if err != nil { - fmt.Println(err) - } - }(r) + defer func() { + _ = r.Close() + }() oldStdout := os.Stdout os.Stdout = w @@ -24,10 +27,7 @@ func captureNaturesOutput(f func()) string { f() - err := w.Close() - if err != nil { - return "" - } + _ = w.Close() var buf bytes.Buffer _, _ = buf.ReadFrom(r) @@ -53,24 +53,59 @@ func TestNaturesCommand(t *testing.T) { "╰──────────────────────────────╯\n"), expectedError: false, }, + { + name: "Invalid extra argument", + args: []string{"natures", "extra"}, + expectedOutput: styling.StripANSI(styling.ErrorBorder.Render(styling.ErrorColor.Render("Error!")+"\nThe only currently available options\nafter command are '-h' or '--help'")) + "\n", + expectedError: true, + }, + { + name: "Full Natures output with table", + args: []string{"natures"}, + expectedOutput: `Natures affect the growth of a Pokémon. + Each nature increases one of its stats by 10% and decreases one by 10%. + Five natures increase and decrease the same stat and therefore have no effect. + + Nature Chart: + ┌──────────┬─────────┬──────────┬──────────┬──────────┬─────────┐ + │ │ -Attack │ -Defense │ -Sp. Atk │ -Sp. Def │ Speed │ + ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ + │ +Attack │ Hardy │ Lonely │ Adamant │ Naughty │ Brave │ + ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ + │ +Defense │ Bold │ Docile │ Impish │ Lax │ Relaxed │ + ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ + │ +Sp. Atk │ Modest │ Mild │ Bashful │ Rash │ Quiet │ + ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ + │ +Sp. Def │ Calm │ Gentle │ Careful │ Quirky │ Sassy │ + ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ + │ Speed │ Timid │ Hasty │ Jolly │ Naive │ Serious │ + └──────────┴─────────┴──────────┴──────────┴──────────┴─────────┘ + `, + expectedError: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + // Override osExit + oldExit := osExit + osExit = fakeExit + defer func() { osExit = oldExit }() + + // Reset captured exit code + exitCode = 0 + // Save original os.Args originalArgs := os.Args defer func() { os.Args = originalArgs }() - - // Set os.Args for the test os.Args = append([]string{"poke-cli"}, tt.args...) - // Capture the output + // Capture output output := captureNaturesOutput(func() { defer func() { - // Recover from os.Exit calls if r := recover(); r != nil { - if !tt.expectedError { - t.Fatalf("Unexpected error: %v", r) + if r != "exit" { + t.Fatalf("Unexpected panic: %v", r) } } }() @@ -79,7 +114,17 @@ func TestNaturesCommand(t *testing.T) { cleanOutput := styling.StripANSI(output) - assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should equal the expected string") + // Logging expected vs actual + t.Logf("Expected Output:\n%s", tt.expectedOutput) + t.Logf("Actual Output:\n%s", cleanOutput) + + // Assertions + assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should match expected") + if tt.expectedError { + assert.Equal(t, 1, exitCode, "Expected exit code 1 on error") + } else { + assert.Equal(t, 0, exitCode, "Expected no exit (code 0) on success") + } }) } } From cd1308adb657bbb9e9561ead55ce1f6029b4685e Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 14:12:47 -0700 Subject: [PATCH 11/18] adding help flag check (#133) --- cmd/move/move.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/move/move.go b/cmd/move/move.go index 8417c344..fed0b106 100644 --- a/cmd/move/move.go +++ b/cmd/move/move.go @@ -31,6 +31,15 @@ func MoveCommand() { flag.Parse() + // Check for help flag + if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { + flag.Usage() + + if flag.Lookup("test.v") == nil { + os.Exit(0) + } + } + if err := cmd.ValidateMoveArgs(os.Args); err != nil { fmt.Println(err.Error()) os.Exit(1) From 39269f674c57cc52089f4668d226f82a81926493 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 14:13:55 -0700 Subject: [PATCH 12/18] adding osExit var for better testing capabilities --- cmd/natures.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/natures.go b/cmd/natures.go index 803a2cda..5764781e 100644 --- a/cmd/natures.go +++ b/cmd/natures.go @@ -9,6 +9,8 @@ import ( "os" ) +var osExit = os.Exit + func NaturesCommand() { flag.Usage = func() { helpMessage := styling.HelpBorder.Render( @@ -28,7 +30,8 @@ func NaturesCommand() { if err := ValidateNaturesArgs(os.Args); err != nil { fmt.Println(err.Error()) - os.Exit(1) + osExit(1) + return } fmt.Println("Natures affect the growth of a Pokémon.\n" + @@ -53,7 +56,7 @@ func NaturesCommand() { Rows(chart...). StyleFunc(func(row, col int) lipgloss.Style { return lipgloss.NewStyle(). - Padding(0, 1) // This styles the border color + Padding(0, 1) }) fmt.Println(t.Render()) From 07883f4682e9d4a293fca90f59124f1577cf26ab Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 14:49:14 -0700 Subject: [PATCH 13/18] updating flag usage text --- cmd/move/move.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/move/move.go b/cmd/move/move.go index fed0b106..128f54f2 100644 --- a/cmd/move/move.go +++ b/cmd/move/move.go @@ -20,11 +20,8 @@ func MoveCommand() { helpMessage := styling.HelpBorder.Render( "Get details about a specific move.\n\n", styling.StyleBold.Render("USAGE:"), - fmt.Sprintf("\n\t%s %s %s %s", "poke-cli", styling.StyleBold.Render("move"), "", "[flag]"), - fmt.Sprintf("\n\t%-20s", styling.StyleItalic.Render("Use a hyphen when typing a name with a space.")), - "\n\n", - styling.StyleBold.Render("FLAGS:"), - fmt.Sprintf("\n\t%-20s %s", "-h, --help", "Prints the help menu."), + fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("move"), ""), + fmt.Sprintf("\n\n%s", styling.StyleItalic.Render("Use a hyphen when typing a name with a space.")), ) fmt.Println(helpMessage) } From 98b7c5e55b9da53cb10d52ba4dca13a53b53472a Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 14:53:13 -0700 Subject: [PATCH 14/18] applying golangci-lint formatting --- cmd/move/move.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/move/move.go b/cmd/move/move.go index 128f54f2..4daf90f7 100644 --- a/cmd/move/move.go +++ b/cmd/move/move.go @@ -20,8 +20,8 @@ func MoveCommand() { helpMessage := styling.HelpBorder.Render( "Get details about a specific move.\n\n", styling.StyleBold.Render("USAGE:"), - fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("move"), ""), - fmt.Sprintf("\n\n%s", styling.StyleItalic.Render("Use a hyphen when typing a name with a space.")), + "\n\t"+"poke-cli"+" "+styling.StyleBold.Render("move")+" ", + "\n\n"+styling.StyleItalic.Render("Use a hyphen when typing a name with a space."), ) fmt.Println(helpMessage) } From 47545a3dd615f6e478decab35c05f609d3ddc4c8 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 14:54:26 -0700 Subject: [PATCH 15/18] updating tests --- cmd/validateargs_test.go | 65 ++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/cmd/validateargs_test.go b/cmd/validateargs_test.go index 65d3e774..d1bca212 100644 --- a/cmd/validateargs_test.go +++ b/cmd/validateargs_test.go @@ -1,30 +1,63 @@ package cmd import ( - "flag" "github.com/digitalghost-dev/poke-cli/styling" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" ) -func TestHandleHelpFlag(t *testing.T) { - // Mock flag.Usage to avoid actual printing - flag.Usage = func() {} - - // Test cases +func TestCheckLength(t *testing.T) { + // Define test cases tests := []struct { - name string - args []string + name string + args []string + maxLength int + wantErr bool + expectedErr string }{ - {"Valid short help flag", []string{"cmd", "subcmd", "-h"}}, - {"Valid long help flag", []string{"cmd", "subcmd", "--help"}}, - {"Invalid case (no flag)", []string{"cmd", "subcmd"}}, + { + name: "Valid length - Empty slice", + args: []string{}, + maxLength: 1, + wantErr: false, + expectedErr: "", + }, + { + name: "Valid length - Within limit", + args: []string{"arg1", "arg2"}, + maxLength: 3, + wantErr: false, + expectedErr: "", + }, + { + name: "Valid length - Exactly at limit", + args: []string{"arg1", "arg2", "arg3"}, + maxLength: 3, + wantErr: false, + expectedErr: "", + }, + { + name: "Invalid length - Exceeds limit", + args: []string{"arg1", "arg2", "arg3", "arg4"}, + maxLength: 3, + wantErr: true, + expectedErr: "Too many arguments", + }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - handleHelpFlag(tc.args) + // Run test cases + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := checkLength(tt.args, tt.maxLength) + + // Check if an error was expected + if tt.wantErr { + require.Error(t, err) + assert.Contains(t, styling.StripANSI(err.Error()), tt.expectedErr) + } else { + assert.NoError(t, err) + } }) } } @@ -107,7 +140,7 @@ func TestValidatePokemonArgs(t *testing.T) { {"poke-cli", "pokemon", "dodrio", "-a", "-s", "-t"}, {"poke-cli", "pokemon", "dragalge", "-a", "-s", "-t", "--image=sm"}, {"poke-cli", "pokemon", "squirtle", "-a", "-s"}, - {"poke-cli", "pokemon", "squirtle", "-s", "-a"}, + {"poke-cli", "pokemon", "dragapult", "-s", "-a"}, } for _, input := range validInputs { @@ -132,7 +165,7 @@ func TestValidatePokemonArgs(t *testing.T) { // Testing too many arguments tooManyArgs := [][]string{ - {"poke-cli", "pokemon", "hypno", "--abilities", "-s", "--types", "--image=sm", "-m"}, + {"poke-cli", "pokemon", "hypo", "--abilities", "-s", "--types", "--image=sm", "-m"}, } expectedError := styling.StripANSI("╭──────────────────╮\n│Error! │\n│Too many arguments│\n╰──────────────────╯") From ac72b0dec0af681ab0f87c1ed03aeb76bcdce500 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 14:54:45 -0700 Subject: [PATCH 16/18] updating tests --- cmd/natures_test.go | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/cmd/natures_test.go b/cmd/natures_test.go index fdf4b72e..db0b5934 100644 --- a/cmd/natures_test.go +++ b/cmd/natures_test.go @@ -62,25 +62,23 @@ func TestNaturesCommand(t *testing.T) { { name: "Full Natures output with table", args: []string{"natures"}, - expectedOutput: `Natures affect the growth of a Pokémon. - Each nature increases one of its stats by 10% and decreases one by 10%. - Five natures increase and decrease the same stat and therefore have no effect. - - Nature Chart: - ┌──────────┬─────────┬──────────┬──────────┬──────────┬─────────┐ - │ │ -Attack │ -Defense │ -Sp. Atk │ -Sp. Def │ Speed │ - ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ - │ +Attack │ Hardy │ Lonely │ Adamant │ Naughty │ Brave │ - ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ - │ +Defense │ Bold │ Docile │ Impish │ Lax │ Relaxed │ - ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ - │ +Sp. Atk │ Modest │ Mild │ Bashful │ Rash │ Quiet │ - ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ - │ +Sp. Def │ Calm │ Gentle │ Careful │ Quirky │ Sassy │ - ├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤ - │ Speed │ Timid │ Hasty │ Jolly │ Naive │ Serious │ - └──────────┴─────────┴──────────┴──────────┴──────────┴─────────┘ - `, + expectedOutput: "Natures affect the growth of a Pokémon.\n" + + "Each nature increases one of its stats by 10% and decreases one by 10%.\n" + + "Five natures increase and decrease the same stat and therefore have no effect.\n\n" + + "Nature Chart:\n" + + "┌──────────┬─────────┬──────────┬──────────┬──────────┬─────────┐\n" + + "│ │ -Attack │ -Defense │ -Sp. Atk │ -Sp. Def │ Speed │\n" + + "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + + "│ +Attack │ Hardy │ Lonely │ Adamant │ Naughty │ Brave │\n" + + "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + + "│ +Defense │ Bold │ Docile │ Impish │ Lax │ Relaxed │\n" + + "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + + "│ +Sp. Atk │ Modest │ Mild │ Bashful │ Rash │ Quiet │\n" + + "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + + "│ +Sp. Def │ Calm │ Gentle │ Careful │ Quirky │ Sassy │\n" + + "├──────────┼─────────┼──────────┼──────────┼──────────┼─────────┤\n" + + "│ Speed │ Timid │ Hasty │ Jolly │ Naive │ Serious │\n" + + "└──────────┴─────────┴──────────┴──────────┴──────────┴─────────┘\n", expectedError: false, }, } From 3a59aa5beaf94098d46914e089e882a1ae4f3b1d Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 15:00:31 -0700 Subject: [PATCH 17/18] adding help flag check (#133) --- cmd/ability.go | 5 +++++ cmd/pokemon.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/cmd/ability.go b/cmd/ability.go index f1864dc3..2fb6609c 100644 --- a/cmd/ability.go +++ b/cmd/ability.go @@ -33,6 +33,11 @@ func AbilityCommand() { flag.Parse() + if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { + flag.Usage() + return + } + if err := ValidateAbilityArgs(args); err != nil { fmt.Println(err.Error()) if os.Getenv("GO_TESTING") != "1" { diff --git a/cmd/pokemon.go b/cmd/pokemon.go index 669b537b..34889103 100644 --- a/cmd/pokemon.go +++ b/cmd/pokemon.go @@ -57,6 +57,11 @@ func PokemonCommand() { flag.Parse() + if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { + flag.Usage() + return + } + err := ValidatePokemonArgs(args) if err != nil { fmt.Println(err.Error()) From 5ba71b93f0076ba71b2088afc9a7fd4fb98aaaf7 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 23 Apr 2025 15:03:06 -0700 Subject: [PATCH 18/18] adding checkLength function (#134) --- cmd/validateargs.go | 95 ++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 61 deletions(-) diff --git a/cmd/validateargs.go b/cmd/validateargs.go index 81db1001..fda38ff1 100644 --- a/cmd/validateargs.go +++ b/cmd/validateargs.go @@ -1,29 +1,25 @@ package cmd import ( - "flag" "fmt" "github.com/digitalghost-dev/poke-cli/styling" - "os" ) -func handleHelpFlag(args []string) { - if len(args) == 3 && (args[2] == "-h" || args[2] == "--help") { - flag.Usage() - - if flag.Lookup("test.v") == nil { - os.Exit(0) - } +// checkLength checks if the number of arguments is lower than the max value +func checkLength(args []string, max int) error { + if len(args) > max { + errMessage := styling.ErrorBorder.Render( + styling.ErrorColor.Render("Error!") + "\nToo many arguments", + ) + return fmt.Errorf("%s", errMessage) } + return nil } // ValidateAbilityArgs validates the command line arguments func ValidateAbilityArgs(args []string) error { - handleHelpFlag(args) - - if len(args) > 4 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("Error!"), "\nToo many arguments") - return fmt.Errorf("%s", errMessage) + if err := checkLength(args, 4); err != nil { + return err } if len(args) == 2 { @@ -36,11 +32,8 @@ func ValidateAbilityArgs(args []string) error { // ValidateMoveArgs validates the command line arguments func ValidateMoveArgs(args []string) error { - handleHelpFlag(args) - - if len(args) > 4 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("Error!"), "\nToo many arguments") - return fmt.Errorf("%s", errMessage) + if err := checkLength(args, 3); err != nil { + return err } if len(args) == 2 { @@ -53,21 +46,16 @@ func ValidateMoveArgs(args []string) error { // ValidateNaturesArgs validates the command line arguments func ValidateNaturesArgs(args []string) error { - handleHelpFlag(args) - - if len(args) > 3 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("Error!"), "\nToo many arguments") - return fmt.Errorf("%s", errMessage) + if err := checkLength(args, 3); err != nil { + return err } // Check if there are exactly 3 arguments and the third argument is neither '-h' nor '--help' // If true, return an error message since only '-h' and '--help' are allowed after 'types' - if len(args) == 3 && (args[2] != "-h" && args[2] != "--help") { - errorTitle := styling.ErrorColor.Render("Error!") - errorString := "\nThe only currently available options\nafter [natures] command are '-h' or '--help'" - finalErrorMessage := errorTitle + errorString - renderedError := styling.ErrorBorder.Render(finalErrorMessage) - return fmt.Errorf("%s", renderedError) + if len(args) == 3 && args[2] != "-h" && args[2] != "--help" { + errMsg := styling.ErrorColor.Render("Error!") + + "\nThe only currently available options\nafter command are '-h' or '--help'" + return fmt.Errorf("%s", styling.ErrorBorder.Render(errMsg)) } return nil @@ -75,8 +63,6 @@ func ValidateNaturesArgs(args []string) error { // ValidatePokemonArgs validates the command line arguments func ValidatePokemonArgs(args []string) error { - handleHelpFlag(args) - // Check if the number of arguments is less than 3 if len(args) < 3 { errMessage := styling.ErrorBorder.Render( @@ -88,13 +74,8 @@ func ValidatePokemonArgs(args []string) error { return fmt.Errorf("%s", errMessage) } - // Check if there are too many arguments - if len(args) > 7 { - errMessage := styling.ErrorBorder.Render( - styling.ErrorColor.Render("Error!"), - "\nToo many arguments", - ) - return fmt.Errorf("%s", errMessage) + if err := checkLength(args, 7); err != nil { + return err } // Validate each argument after the Pokémon's name @@ -131,19 +112,16 @@ func ValidatePokemonArgs(args []string) error { // ValidateSearchArgs validates the command line arguments func ValidateSearchArgs(args []string) error { - if len(args) > 3 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("Error!"), "\nToo many arguments") - return fmt.Errorf("%s", errMessage) + if err := checkLength(args, 3); err != nil { + return err } // Check if there are exactly 3 arguments and the third argument is neither '-h' nor '--help' - // If true, return an error message since only '-h' and '--help' are allowed after 'types' - if len(args) == 3 && (args[2] != "-h" && args[2] != "--help") { - errorTitle := styling.ErrorColor.Render("Error!") - errorString := "\nThe only currently available options\nafter [search] command are '-h' or '--help'" - finalErrorMessage := errorTitle + errorString - renderedError := styling.ErrorBorder.Render(finalErrorMessage) - return fmt.Errorf("%s", renderedError) + // If true, return an error message since only '-h' and '--help' are allowed after + if len(args) == 3 && args[2] != "-h" && args[2] != "--help" { + errMsg := styling.ErrorColor.Render("Error!") + + "\nThe only currently available options\nafter command are '-h' or '--help'" + return fmt.Errorf("%s", styling.ErrorBorder.Render(errMsg)) } return nil @@ -151,21 +129,16 @@ func ValidateSearchArgs(args []string) error { // ValidateTypesArgs validates the command line arguments func ValidateTypesArgs(args []string) error { - handleHelpFlag(args) - - if len(args) > 3 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("Error!"), "\nToo many arguments") - return fmt.Errorf("%s", errMessage) + if err := checkLength(args, 3); err != nil { + return err } // Check if there are exactly 3 arguments and the third argument is neither '-h' nor '--help' - // If true, return an error message since only '-h' and '--help' are allowed after 'types' - if len(args) == 3 && (args[2] != "-h" && args[2] != "--help") { - errorTitle := styling.ErrorColor.Render("Error!") - errorString := "\nThe only currently available options\nafter [types] command are '-h' or '--help'" - finalErrorMessage := errorTitle + errorString - renderedError := styling.ErrorBorder.Render(finalErrorMessage) - return fmt.Errorf("%s", renderedError) + // If true, return an error message since only '-h' and '--help' are allowed after + if len(args) == 3 && args[2] != "-h" && args[2] != "--help" { + errMsg := styling.ErrorColor.Render("Error!") + + "\nThe only currently available options\nafter command are '-h' or '--help'" + return fmt.Errorf("%s", styling.ErrorBorder.Render(errMsg)) } return nil