From cf3a5096f5c08a1609dd8924ede1fafa564002b5 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Thu, 12 Feb 2026 10:45:17 -0800 Subject: [PATCH 01/11] updating version numbers --- .github/workflows/ci.yml | 2 +- .goreleaser.yml | 2 +- Dockerfile | 2 +- README.md | 8 ++++---- card_data/pipelines/poke_cli_dbt/dbt_project.yml | 2 +- card_data/pyproject.toml | 2 +- nfpm.yaml | 2 +- testdata/main_latest_flag.golden | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ff0f94..5fd7cb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ on: - main env: - VERSION_NUMBER: 'v1.8.9' + VERSION_NUMBER: 'v1.8.10' DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli' AWS_REGION: 'us-west-2' diff --git a/.goreleaser.yml b/.goreleaser.yml index e2944c1..1a6d47d 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -14,7 +14,7 @@ builds: - windows - darwin ldflags: - - -s -w -X main.version=v1.8.9 + - -s -w -X main.version=v1.8.10 archives: - formats: [ 'zip' ] diff --git a/Dockerfile b/Dockerfile index 7261655..c75bf05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN go mod download COPY . . -RUN go build -ldflags "-X main.version=v1.8.9" -o poke-cli . +RUN go build -ldflags "-X main.version=v1.8.10" -o poke-cli . # build 2 FROM --platform=$BUILDPLATFORM alpine:3.23 diff --git a/README.md b/README.md index bdf0a4e..b2c28cb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ pokemon-logo

version-label - docker-image-size + docker-image-size ci-status-badge
@@ -99,11 +99,11 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an 3. Choose how to interact with the container: * Run a single command and exit: ```bash - docker run --rm -it digitalghostdev/poke-cli:v1.8.9 [subcommand] [flag] + docker run --rm -it digitalghostdev/poke-cli:v1.8.10 [subcommand] [flag] ``` * Enter the container and use its shell: ```bash - docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.8.9 -c "cd /app && exec sh" + docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.8.10 -c "cd /app && exec sh" # placed into the /app directory, run the program with './poke-cli' # example: ./poke-cli ability swift-swim ``` @@ -118,7 +118,7 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an 6. Run the tool! > [!IMPORTANT] -> For macOS, you may have to allow the executable to run as it is not signed. Head to System Settings > Privacy & Security > scroll down and allow executable to run. +> For macOS, you may have to allow the executable to run as it is not signed. Head to System Settings > Privacy & Security > scroll down and allow the executable to run.
diff --git a/card_data/pipelines/poke_cli_dbt/dbt_project.yml b/card_data/pipelines/poke_cli_dbt/dbt_project.yml index ace000b..5b5ab59 100644 --- a/card_data/pipelines/poke_cli_dbt/dbt_project.yml +++ b/card_data/pipelines/poke_cli_dbt/dbt_project.yml @@ -1,5 +1,5 @@ name: 'poke_cli_dbt' -version: '1.8.9' +version: '1.8.10' profile: 'poke_cli_dbt' diff --git a/card_data/pyproject.toml b/card_data/pyproject.toml index 3e1a108..baae3e1 100644 --- a/card_data/pyproject.toml +++ b/card_data/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "card-data" -version = "1.5.2" +version = "1.8.10" description = "File directory to store all data related processes for the Pokémon TCG." readme = "README.md" requires-python = ">=3.12" diff --git a/nfpm.yaml b/nfpm.yaml index 32cf979..2ab7df1 100644 --- a/nfpm.yaml +++ b/nfpm.yaml @@ -1,7 +1,7 @@ name: "poke-cli" arch: "arm64" platform: "linux" -version: "v1.8.9" +version: "v1.8.10" section: "default" version_schema: semver maintainer: "Christian S" diff --git a/testdata/main_latest_flag.golden b/testdata/main_latest_flag.golden index 1b82be8..9c8aec0 100644 --- a/testdata/main_latest_flag.golden +++ b/testdata/main_latest_flag.golden @@ -2,6 +2,6 @@ ┃ ┃ ┃ Latest available release ┃ ┃ on GitHub: ┃ -┃ • v1.8.8 ┃ +┃ • v1.8.9 ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ From ad15562d777d824eeff7516727ee7922f22cac60 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Thu, 12 Feb 2026 10:48:23 -0800 Subject: [PATCH 02/11] removing redundant `flag.Parse()` call (#243) --- cmd/ability/ability.go | 2 -- cmd/pokemon/pokemon.go | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/ability/ability.go b/cmd/ability/ability.go index 76d8e30..0d17a5c 100644 --- a/cmd/ability/ability.go +++ b/cmd/ability/ability.go @@ -37,8 +37,6 @@ func AbilityCommand() (string, error) { return output.String(), nil } - flag.Parse() - if err := utils.ValidateAbilityArgs(args); err != nil { output.WriteString(err.Error()) return output.String(), err diff --git a/cmd/pokemon/pokemon.go b/cmd/pokemon/pokemon.go index 84c23ae..b9de1dd 100644 --- a/cmd/pokemon/pokemon.go +++ b/cmd/pokemon/pokemon.go @@ -53,11 +53,9 @@ func PokemonCommand() (string, error) { return output.String(), nil } - flag.Parse() - err := utils.ValidatePokemonArgs(args) if err != nil { - output.WriteString(err.Error()) // This is the styled error + output.WriteString(err.Error()) return output.String(), err } From 53e5baaa3d3b0a81761c89d7da825b92dd6e49cf Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Thu, 12 Feb 2026 14:33:42 -0800 Subject: [PATCH 03/11] replacing specific validation function with a generic one (#244) --- cmd/ability/ability.go | 2 +- cmd/item/item.go | 2 +- cmd/move/move.go | 2 +- cmd/natures/natures.go | 2 +- cmd/search/search.go | 2 +- cmd/speed/speed.go | 2 +- cmd/types/types.go | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/ability/ability.go b/cmd/ability/ability.go index 0d17a5c..c9865f9 100644 --- a/cmd/ability/ability.go +++ b/cmd/ability/ability.go @@ -37,7 +37,7 @@ func AbilityCommand() (string, error) { return output.String(), nil } - if err := utils.ValidateAbilityArgs(args); err != nil { + if err := utils.ValidateArgs(args, utils.Validator{MaxArgs: 4, CmdName: "ability", RequireName: true, HasFlags: true}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/item/item.go b/cmd/item/item.go index 52305a0..ff1bfed 100644 --- a/cmd/item/item.go +++ b/cmd/item/item.go @@ -37,7 +37,7 @@ func ItemCommand() (string, error) { flag.Parse() - if err := utils.ValidateItemArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "item", RequireName: true, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/move/move.go b/cmd/move/move.go index 37db39e..a03c79c 100644 --- a/cmd/move/move.go +++ b/cmd/move/move.go @@ -38,7 +38,7 @@ func MoveCommand() (string, error) { flag.Parse() - if err := utils.ValidateMoveArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "move", RequireName: true, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/natures/natures.go b/cmd/natures/natures.go index f12200c..e3d7bba 100644 --- a/cmd/natures/natures.go +++ b/cmd/natures/natures.go @@ -30,7 +30,7 @@ func NaturesCommand() (string, error) { flag.Parse() - if err := utils.ValidateNaturesArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "natures", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/search/search.go b/cmd/search/search.go index f67109e..5ea0a54 100644 --- a/cmd/search/search.go +++ b/cmd/search/search.go @@ -32,7 +32,7 @@ func SearchCommand() error { flag.Parse() - if err := utils.ValidateSearchArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}); err != nil { fmt.Println(err.Error()) return err } diff --git a/cmd/speed/speed.go b/cmd/speed/speed.go index b30e3f7..266c03c 100644 --- a/cmd/speed/speed.go +++ b/cmd/speed/speed.go @@ -112,7 +112,7 @@ func SpeedCommand() (string, error) { flag.Parse() // Validate arguments - if err := utils.ValidateSpeedArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "speed", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/types/types.go b/cmd/types/types.go index db0f483..c15789b 100644 --- a/cmd/types/types.go +++ b/cmd/types/types.go @@ -35,7 +35,7 @@ func TypesCommand() (string, error) { flag.Parse() // Validate arguments - if err := utils.ValidateTypesArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } From 1a3ca056f30f4583f1d219c68ae2ac24ff98fb18 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Fri, 13 Feb 2026 13:55:14 -0800 Subject: [PATCH 04/11] adding API error style (#245) --- styling/styling.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/styling/styling.go b/styling/styling.go index ecc1db1..1bfa5d7 100644 --- a/styling/styling.go +++ b/styling/styling.go @@ -53,6 +53,10 @@ var ( ErrorBorder = lipgloss.NewStyle(). BorderStyle(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("#F2055C")) + ApiErrorStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#FF0000")). + Bold(true). + Padding(1, 2) WarningColor = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF8C00")) WarningBorder = lipgloss.NewStyle(). BorderStyle(lipgloss.RoundedBorder()). From 778d7ff79c7e86b00723902f08379936e50ea1d9 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Fri, 13 Feb 2026 14:11:15 -0800 Subject: [PATCH 05/11] replacing specific validation function with a generic one (#244) --- cmd/berry/berry.go | 2 +- cmd/card/card.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/berry/berry.go b/cmd/berry/berry.go index 2cbef10..3d05abe 100644 --- a/cmd/berry/berry.go +++ b/cmd/berry/berry.go @@ -37,7 +37,7 @@ func BerryCommand() (string, error) { flag.Parse() // Validate arguments - if err := utils.ValidateBerryArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/card/card.go b/cmd/card/card.go index a5cc8a9..b1e50a8 100644 --- a/cmd/card/card.go +++ b/cmd/card/card.go @@ -33,7 +33,7 @@ func CardCommand() (string, error) { flag.Parse() // Validate arguments - if err := utils.ValidateCardArgs(os.Args); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } From 1b50ed921157c8e000b4a66b4673e8aa0dddc350 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Fri, 13 Feb 2026 14:16:48 -0800 Subject: [PATCH 06/11] adding error messaging for Supabase API calls (#245) --- cmd/card/cardlist.go | 23 ++++++++++++++++------- cmd/card/cardlist_test.go | 18 +++++++++++++----- cmd/card/setslist.go | 13 +++++++++++-- cmd/card/setslist_test.go | 18 +++++++++++++----- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/cmd/card/cardlist.go b/cmd/card/cardlist.go index f168318..cead01d 100644 --- a/cmd/card/cardlist.go +++ b/cmd/card/cardlist.go @@ -19,6 +19,7 @@ var getCardData = connections.CallTCGData type CardsModel struct { AllRows []table.Row Choice string + Error error IllustratorMap map[string]string ImageMap map[string]string Loading bool @@ -62,7 +63,7 @@ func cardTableStyles(selectedBg lipgloss.Color) table.Styles { return s } -func (m *CardsModel) syncTableStylesForFocus() { +func syncTableStylesForFocus(m *CardsModel) { if m.Search.Focused() { m.TableStyles = cardTableStyles(inactiveTableSelectedBg) } else { @@ -146,7 +147,7 @@ func (m CardsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.Search.Focused() { m.Search.Blur() m.Table.Focus() - m.syncTableStylesForFocus() + syncTableStylesForFocus(&m) return m, nil } m.Quitting = true @@ -168,15 +169,16 @@ func (m CardsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Table.Blur() m.Search.Focus() } - m.syncTableStylesForFocus() + syncTableStylesForFocus(&m) return m, nil } case cardDataMsg: // Data arrived - stop loading and build the table if msg.err != nil { - m.Quitting = true - return m, tea.Quit + m.Error = msg.err + m.Loading = false + return m, nil } ti := textinput.New() @@ -219,7 +221,7 @@ func (m CardsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { prev := m.Search.Value() m.Search, bubbleCmd = m.Search.Update(msg) if m.Search.Value() != prev { - m.applyFilter() + applyFilter(&m) } } else { m.Table, bubbleCmd = m.Table.Update(msg) @@ -237,7 +239,7 @@ func (m CardsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, bubbleCmd } -func (m *CardsModel) applyFilter() { +func applyFilter(m *CardsModel) { q := strings.TrimSpace(strings.ToLower(m.Search.Value())) if q == "" { m.Table.SetRows(m.AllRows) @@ -263,6 +265,13 @@ func (m CardsModel) View() string { if m.Quitting { return "\n Quitting card search...\n\n" } + if m.Error != nil { + return styling.ApiErrorStyle.Render( + "Error loading cards from Supabase:\n" + + m.Error.Error() + "\n\n" + + "Press ctrl+c or esc to exit.", + ) + } if m.Loading { return lipgloss.NewStyle().Padding(2).Render( m.Spinner.View() + " Loading cards...", diff --git a/cmd/card/cardlist_test.go b/cmd/card/cardlist_test.go index 6da8922..8563fbd 100644 --- a/cmd/card/cardlist_test.go +++ b/cmd/card/cardlist_test.go @@ -351,7 +351,7 @@ func TestCardDataMsg_PopulatesModel(t *testing.T) { } } -func TestCardDataMsg_Error_QuitsModel(t *testing.T) { +func TestCardDataMsg_Error_StoresError(t *testing.T) { model, _ := CardsList("set123") // Simulate receiving an error via cardDataMsg @@ -362,12 +362,20 @@ func TestCardDataMsg_Error_QuitsModel(t *testing.T) { newModel, cmd := model.Update(msg) resultModel := newModel.(CardsModel) - if !resultModel.Quitting { - t.Error("Quitting should be true when error received") + if resultModel.Error == nil { + t.Error("Error should be set when error received") } - if cmd == nil { - t.Error("Should return tea.Quit command on error") + if resultModel.Error.Error() != "network error" { + t.Errorf("Expected error message 'network error', got '%s'", resultModel.Error.Error()) + } + + if resultModel.Loading { + t.Error("Loading should be false after error") + } + + if cmd != nil { + t.Error("Should not return a command on error (stays in view to show error)") } } diff --git a/cmd/card/setslist.go b/cmd/card/setslist.go index 2c328a3..a02ba70 100644 --- a/cmd/card/setslist.go +++ b/cmd/card/setslist.go @@ -16,6 +16,7 @@ var getSetsData = connections.CallTCGData type SetsModel struct { Choice string + Error error Loading bool List list.Model Quitting bool @@ -90,8 +91,9 @@ func (m SetsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case setsDataMsg: // Data arrived - stop loading and build the list if msg.err != nil { - m.Quitting = true - return m, tea.Quit + m.Error = msg.err + m.Loading = false + return m, nil } const listWidth = 20 @@ -132,6 +134,13 @@ func (m SetsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m SetsModel) View() string { + if m.Error != nil { + return styling.ApiErrorStyle.Render( + "Error loading sets from Supabase:\n" + + m.Error.Error() + "\n\n" + + "Press ctrl+c or esc to exit.", + ) + } if m.Choice != "" { return quitTextStyle.Render("Set selected:", m.Choice) } diff --git a/cmd/card/setslist_test.go b/cmd/card/setslist_test.go index ee2d917..042c1a7 100644 --- a/cmd/card/setslist_test.go +++ b/cmd/card/setslist_test.go @@ -238,7 +238,7 @@ func TestSetsDataMsg_PopulatesModel(t *testing.T) { } } -func TestSetsDataMsg_Error_QuitsModel(t *testing.T) { +func TestSetsDataMsg_Error_StoresError(t *testing.T) { model, _ := SetsList("sv") // Simulate receiving an error via setsDataMsg @@ -249,12 +249,20 @@ func TestSetsDataMsg_Error_QuitsModel(t *testing.T) { newModel, cmd := model.Update(msg) resultModel := newModel.(SetsModel) - if !resultModel.Quitting { - t.Error("Quitting should be true when error received") + if resultModel.Error == nil { + t.Error("Error should be set when error received") } - if cmd == nil { - t.Error("Should return tea.Quit command on error") + if resultModel.Error.Error() != "network error" { + t.Errorf("Expected error message 'network error', got '%s'", resultModel.Error.Error()) + } + + if resultModel.Loading { + t.Error("Loading should be false after error") + } + + if cmd != nil { + t.Error("Should not return a command on error (stays in view to show error)") } } From 970c171a516ab546662654dabae32612abb449fc Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Fri, 13 Feb 2026 14:24:48 -0800 Subject: [PATCH 07/11] reducing duplicated argument validation logic (#244) --- cmd/utils/validateargs.go | 130 ++++----------------------------- cmd/utils/validateargs_test.go | 52 ++++++------- 2 files changed, 42 insertions(+), 140 deletions(-) diff --git a/cmd/utils/validateargs.go b/cmd/utils/validateargs.go index ab2d80c..25bc763 100644 --- a/cmd/utils/validateargs.go +++ b/cmd/utils/validateargs.go @@ -7,6 +7,13 @@ import ( "github.com/digitalghost-dev/poke-cli/styling" ) +type Validator struct { + MaxArgs int + CmdName string + RequireName bool + HasFlags bool +} + // checkLength checks if the number of arguments is lower than the max value. Helper Function. func checkLength(args []string, max int) error { if len(args) > max { @@ -22,90 +29,24 @@ func checkLength(args []string, max int) error { func checkNoOtherOptions(args []string, max int, commandName string) error { if len(args) == max && args[2] != "-h" && args[2] != "--help" { errMsg := styling.ErrorColor.Render("✖ Error!") + - "\nThe only available options after the\n" + commandName + " command are '-h' or '--help'" + "\nThe only available options after the\n" + "<" + commandName + "> command are '-h' or '--help'" return fmt.Errorf("%s", styling.ErrorBorder.Render(errMsg)) } return nil } -// ValidateAbilityArgs validates the command line arguments -func ValidateAbilityArgs(args []string) error { - if err := checkLength(args, 4); err != nil { - return err - } - - if len(args) == 2 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("✖ Error!"), "\nPlease specify an ability") - return fmt.Errorf("%s", errMessage) - } - - return nil -} - -// ValidateBerryArgs validates the command line arguments -func ValidateBerryArgs(args []string) error { - if err := checkLength(args, 3); err != nil { +func ValidateArgs(args []string, v Validator) error { + if err := checkLength(args, v.MaxArgs); err != nil { return err } - - if err := checkNoOtherOptions(args, 3, ""); err != nil { - return err - } - - return nil -} - -// ValidateCardArgs validates the command line arguments -func ValidateCardArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err + if v.RequireName && len(args) == 2 { + return fmt.Errorf("✖ Error! Please specify a(n) %s", v.CmdName) } - - if err := checkNoOtherOptions(args, 3, ""); err != nil { - return err - } - - return nil -} - -// ValidateItemArgs validates the command line arguments -func ValidateItemArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err - } - - if len(args) == 2 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("✖ Error!"), "\nPlease specify an item ") - return fmt.Errorf("%s", errMessage) - } - - return nil -} - -// ValidateMoveArgs validates the command line arguments -func ValidateMoveArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err - } - - if len(args) == 2 { - errMessage := styling.ErrorBorder.Render(styling.ErrorColor.Render("✖ Error!"), "\nPlease specify a move ") - return fmt.Errorf("%s", errMessage) + if !v.HasFlags && !v.RequireName { + if err := checkNoOtherOptions(args, v.MaxArgs, v.CmdName); err != nil { + return err + } } - - return nil -} - -// ValidateNaturesArgs validates the command line arguments -func ValidateNaturesArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err - } - - if err := checkNoOtherOptions(args, 3, ""); err != nil { - return err - } - return nil } @@ -176,42 +117,3 @@ func ValidatePokemonArgs(args []string) error { return nil } - -// ValidateSearchArgs validates the command line arguments -func ValidateSearchArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err - } - - if err := checkNoOtherOptions(args, 3, ""); err != nil { - return err - } - - return nil -} - -// ValidateSpeedArgs validates the command line arguments -func ValidateSpeedArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err - } - - if err := checkNoOtherOptions(args, 3, ""); err != nil { - return err - } - - return nil -} - -// ValidateTypesArgs validates the command line arguments -func ValidateTypesArgs(args []string) error { - if err := checkLength(args, 3); err != nil { - return err - } - - if err := checkNoOtherOptions(args, 3, ""); err != nil { - return err - } - - return nil -} diff --git a/cmd/utils/validateargs_test.go b/cmd/utils/validateargs_test.go index c261716..eb6aa75 100644 --- a/cmd/utils/validateargs_test.go +++ b/cmd/utils/validateargs_test.go @@ -73,7 +73,7 @@ func TestValidateAbilityArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateAbilityArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 4, CmdName: "ability", RequireName: true, HasFlags: true}) require.NoError(t, err, "Expected no error for valid input") } @@ -83,7 +83,7 @@ func TestValidateAbilityArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateAbilityArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 4, CmdName: "ability", RequireName: true, HasFlags: true}) require.Error(t, err, "Expected error for invalid input") } @@ -95,7 +95,7 @@ func TestValidateAbilityArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateAbilityArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 4, CmdName: "ability", RequireName: true, HasFlags: true}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -114,7 +114,7 @@ func TestValidateNaturesArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateNaturesArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -125,7 +125,7 @@ func TestValidateNaturesArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateNaturesArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) assert.Error(t, err, "Expected error for invalid input") } } @@ -191,7 +191,7 @@ func TestValidateBerryArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateBerryArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -200,7 +200,7 @@ func TestValidateBerryArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateBerryArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -211,7 +211,7 @@ func TestValidateBerryArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateBerryArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -230,7 +230,7 @@ func TestValidateCardArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateCardArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -239,7 +239,7 @@ func TestValidateCardArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateCardArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -250,7 +250,7 @@ func TestValidateCardArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateCardArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -270,7 +270,7 @@ func TestValidateItemArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateItemArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "item", RequireName: true, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -279,7 +279,7 @@ func TestValidateItemArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateItemArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "item", RequireName: true, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -290,7 +290,7 @@ func TestValidateItemArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateItemArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "item", RequireName: true, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -310,7 +310,7 @@ func TestValidateMoveArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateMoveArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "move", RequireName: true, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -319,7 +319,7 @@ func TestValidateMoveArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateMoveArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "move", RequireName: true, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -330,7 +330,7 @@ func TestValidateMoveArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateMoveArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "move", RequireName: true, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -349,7 +349,7 @@ func TestValidateSearchArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateSearchArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -358,7 +358,7 @@ func TestValidateSearchArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateSearchArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -369,7 +369,7 @@ func TestValidateSearchArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateSearchArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -389,7 +389,7 @@ func TestValidateTypesArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateTypesArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -399,7 +399,7 @@ func TestValidateTypesArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateTypesArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -411,7 +411,7 @@ func TestValidateTypesArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateTypesArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -430,7 +430,7 @@ func TestValidateSpeedArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateSpeedArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -439,7 +439,7 @@ func TestValidateSpeedArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateSpeedArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -450,7 +450,7 @@ func TestValidateSpeedArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateSpeedArgs(input) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) From 88142e6bc8f5444b9410dd042e3de637b83789e4 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Sat, 14 Feb 2026 09:20:22 -0800 Subject: [PATCH 08/11] fixing incorrect command names --- cmd/berry/berry.go | 2 +- cmd/card/card.go | 2 +- cmd/utils/validateargs_test.go | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/berry/berry.go b/cmd/berry/berry.go index 3d05abe..171946c 100644 --- a/cmd/berry/berry.go +++ b/cmd/berry/berry.go @@ -37,7 +37,7 @@ func BerryCommand() (string, error) { flag.Parse() // Validate arguments - if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "berry", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/card/card.go b/cmd/card/card.go index b1e50a8..700c822 100644 --- a/cmd/card/card.go +++ b/cmd/card/card.go @@ -33,7 +33,7 @@ func CardCommand() (string, error) { flag.Parse() // Validate arguments - if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}); err != nil { + if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 3, CmdName: "card", RequireName: false, HasFlags: false}); err != nil { output.WriteString(err.Error()) return output.String(), err } diff --git a/cmd/utils/validateargs_test.go b/cmd/utils/validateargs_test.go index eb6aa75..aadfd2d 100644 --- a/cmd/utils/validateargs_test.go +++ b/cmd/utils/validateargs_test.go @@ -114,7 +114,7 @@ func TestValidateNaturesArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "natures", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -125,7 +125,7 @@ func TestValidateNaturesArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "natures", RequireName: false, HasFlags: false}) assert.Error(t, err, "Expected error for invalid input") } } @@ -191,7 +191,7 @@ func TestValidateBerryArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "berry", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -200,7 +200,7 @@ func TestValidateBerryArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "berry", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -211,7 +211,7 @@ func TestValidateBerryArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "berry", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -230,7 +230,7 @@ func TestValidateCardArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "card", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -239,7 +239,7 @@ func TestValidateCardArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "card", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -250,7 +250,7 @@ func TestValidateCardArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "types", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "card", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) @@ -430,7 +430,7 @@ func TestValidateSpeedArgs(t *testing.T) { } for _, input := range validInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "speed", RequireName: false, HasFlags: false}) require.NoError(t, err, "Expected no error for valid input") } @@ -439,7 +439,7 @@ func TestValidateSpeedArgs(t *testing.T) { } for _, input := range invalidInputs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "speed", RequireName: false, HasFlags: false}) require.Error(t, err, "Expected error for invalid input") } @@ -450,7 +450,7 @@ func TestValidateSpeedArgs(t *testing.T) { expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") for _, input := range tooManyArgs { - err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "search", RequireName: false, HasFlags: false}) + err := ValidateArgs(input, Validator{MaxArgs: 3, CmdName: "speed", RequireName: false, HasFlags: false}) if err == nil { t.Fatalf("Expected an error for input %v, but got nil", input) From f01c1fd82ffe8091b05f8d31db4698ddb70e0b0c Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Sun, 15 Feb 2026 12:46:51 -0800 Subject: [PATCH 09/11] guard against uninitialized list on API error --- cmd/card/setslist.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/card/setslist.go b/cmd/card/setslist.go index a02ba70..987f676 100644 --- a/cmd/card/setslist.go +++ b/cmd/card/setslist.go @@ -118,14 +118,14 @@ func (m SetsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd case tea.WindowSizeMsg: - if !m.Loading { + if !m.Loading && m.Error == nil { m.List.SetWidth(msg.Width) } return m, nil } // Only update the list if it's been initialized - if !m.Loading { + if !m.Loading && m.Error == nil { var cmd tea.Cmd m.List, cmd = m.List.Update(msg) return m, cmd From cd4ef8c69962838c44fc78c0f6f06f7a433be47d Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Mon, 16 Feb 2026 11:18:00 -0800 Subject: [PATCH 10/11] guard against enter keypress when in error state --- cmd/card/setslist.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/card/setslist.go b/cmd/card/setslist.go index 987f676..9b88ab6 100644 --- a/cmd/card/setslist.go +++ b/cmd/card/setslist.go @@ -80,6 +80,9 @@ func (m SetsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Quitting = true return m, tea.Quit case "enter": + if m.Error != nil { + return m, nil + } i, ok := m.List.SelectedItem().(item) if ok { m.Choice = string(i) @@ -100,7 +103,7 @@ func (m SetsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { const listHeight = 20 l := list.New(msg.items, itemDelegate{}, listWidth, listHeight) - l.Title = fmt.Sprintf("Pick a set from the %s series", msg.seriesID) + l.Title = fmt.Sprintf("Choose a set!") l.SetShowStatusBar(false) l.SetFilteringEnabled(false) l.Styles.Title = titleStyle From b1adc7dbe657f0bcae42d26673c187f96c6ce264 Mon Sep 17 00:00:00 2001 From: Christian Sanchez Date: Mon, 16 Feb 2026 11:22:34 -0800 Subject: [PATCH 11/11] fixing linting error --- cmd/card/setslist.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/card/setslist.go b/cmd/card/setslist.go index 9b88ab6..5c116c0 100644 --- a/cmd/card/setslist.go +++ b/cmd/card/setslist.go @@ -2,7 +2,6 @@ package card import ( "encoding/json" - "fmt" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/spinner" @@ -103,7 +102,7 @@ func (m SetsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { const listHeight = 20 l := list.New(msg.items, itemDelegate{}, listWidth, listHeight) - l.Title = fmt.Sprintf("Choose a set!") + l.Title = "Choose a set!" l.SetShowStatusBar(false) l.SetFilteringEnabled(false) l.Styles.Title = titleStyle