diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 0bb12e4..7c08c7d 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -1,5 +1,10 @@ name: Codspeed Benchmarks on: + push: + branches: + - main + paths: + - 'card_data/**' pull_request: types: [ opened, reopened, synchronize ] paths: @@ -26,7 +31,7 @@ jobs: python-version: '3.12' - name: Install uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v7 - name: Install dependencies run: uv sync --dev diff --git a/.github/workflows/go_test.yml b/.github/workflows/go_test.yml index abdc6b1..efb93b3 100644 --- a/.github/workflows/go_test.yml +++ b/.github/workflows/go_test.yml @@ -1,4 +1,4 @@ -name: Test +name: Golang Tests on: pull_request: @@ -13,7 +13,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Set up Go + - name: Setup Go uses: actions/setup-go@v5 with: go-version: 1.24 diff --git a/card_data/pipelines/definitions.py b/card_data/pipelines/definitions.py index 892db84..d8a6cb1 100644 --- a/card_data/pipelines/definitions.py +++ b/card_data/pipelines/definitions.py @@ -6,6 +6,7 @@ from .defs.extract.tcgcsv.extract_pricing import build_dataframe from .defs.load.tcgcsv.load_pricing import load_pricing_data, data_quality_checks_on_pricing +from .sensors import discord_success_sensor, discord_failure_sensor @definitions @@ -31,4 +32,5 @@ def defs() -> dg.Definitions: assets=[build_dataframe, load_pricing_data, data_quality_checks_on_pricing], jobs=[pricing_pipeline_job], schedules=[price_schedule], + sensors=[discord_success_sensor, discord_failure_sensor] ) \ No newline at end of file diff --git a/card_data/pipelines/sensors.py b/card_data/pipelines/sensors.py new file mode 100644 index 0000000..8707e30 --- /dev/null +++ b/card_data/pipelines/sensors.py @@ -0,0 +1,40 @@ +import requests +from dagster import DagsterRunStatus, RunStatusSensorContext, run_status_sensor + +from .utils.secret_retriever import fetch_n8n_webhook_secret + + +@run_status_sensor(run_status=DagsterRunStatus.SUCCESS, name="discord_success_sensor") +def discord_success_sensor(context: RunStatusSensorContext): + context.log.info(f"Detected successful run: {context.dagster_run.run_id}") + try: + response = requests.post( + fetch_n8n_webhook_secret(), + json={ + "job_name": context.dagster_run.job_name, + "status": "SUCCESS", + "run_id": context.dagster_run.run_id, + }, + timeout=10, + ) + context.log.info(f"n8n response: {response.status_code}") + except Exception as e: + context.log.error(f"Failed to send notification: {e}") + + +@run_status_sensor(run_status=DagsterRunStatus.FAILURE, name="discord_failure_sensor") +def discord_failure_sensor(context: RunStatusSensorContext): + context.log.info(f"Detected failed run: {context.dagster_run.run_id}") + try: + response = requests.post( + fetch_n8n_webhook_secret(), + json={ + "job_name": context.dagster_run.job_name, + "status": "FAILURE", + "run_id": context.dagster_run.run_id, + }, + timeout=10, + ) + context.log.info(f"n8n response: {response.status_code}") + except Exception as e: + context.log.error(f"Failed to send notification: {e}") \ No newline at end of file diff --git a/card_data/pipelines/utils/secret_retriever.py b/card_data/pipelines/utils/secret_retriever.py index 81cd8ec..a23eac9 100644 --- a/card_data/pipelines/utils/secret_retriever.py +++ b/card_data/pipelines/utils/secret_retriever.py @@ -21,3 +21,14 @@ def fetch_secret() -> str: secret_dict: SupabaseSecret = json.loads(secret) return secret_dict["database_uri"] + + +def fetch_n8n_webhook_secret() -> str: + client = botocore.session.get_session().create_client("secretsmanager") + cache_config = SecretCacheConfig() + cache = SecretCache(config=cache_config, client=client) + + secret = cast(str, cache.get_secret_string("n8n_webhook")) + secret_dict: dict[str, str] = json.loads(secret) + + return secret_dict["n8n_webhook"] diff --git a/cli.go b/cli.go index 1f2add9..8cc8502 100644 --- a/cli.go +++ b/cli.go @@ -80,7 +80,7 @@ func runCLI(args []string) int { fmt.Sprintf("\n\t%-15s %s", "search", "Search for a resource"), fmt.Sprintf("\n\t%-15s %s", "speed", "Calculate the speed of a Pokémon in battle"), fmt.Sprintf("\n\t%-15s %s", "types", "Get details about a typing"), - "\n\n", styling.StyleItalic.Render("hint: when calling a resource with a space, use a hyphen"), + "\n\n", styling.StyleItalic.Render(styling.HyphenHint), "\n", styling.StyleItalic.Render("example: poke-cli ability strong-jaw"), "\n", styling.StyleItalic.Render("example: poke-cli pokemon flutter-mane"), "\n\n", fmt.Sprintf("%s %s", "↓ ctrl/cmd + click for docs/guides\n", styling.DocsLink), diff --git a/cmd/ability/ability.go b/cmd/ability/ability.go index f128144..e8ec795 100644 --- a/cmd/ability/ability.go +++ b/cmd/ability/ability.go @@ -10,8 +10,6 @@ import ( "github.com/digitalghost-dev/poke-cli/connections" "github.com/digitalghost-dev/poke-cli/flags" "github.com/digitalghost-dev/poke-cli/styling" - "golang.org/x/text/cases" - "golang.org/x/text/language" ) func AbilityCommand() (string, error) { @@ -22,7 +20,7 @@ func AbilityCommand() (string, error) { "Get details about a specific ability.\n\n", styling.StyleBold.Render("USAGE:"), fmt.Sprintf("\n\t%s %s %s %s", "poke-cli", styling.StyleBold.Render("ability"), "", "[flag]"), - fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render("Use a hyphen when typing a name with a space.")), + fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render(styling.HyphenHint)), "\n\n", styling.StyleBold.Render("FLAGS:"), fmt.Sprintf("\n\t%-30s %s", "-p, --pokemon", "Prints Pokémon that learn this ability."), @@ -35,13 +33,12 @@ func AbilityCommand() (string, error) { args := os.Args - flag.Parse() - - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + if err := utils.ValidateAbilityArgs(args); err != nil { output.WriteString(err.Error()) return output.String(), err @@ -81,7 +78,7 @@ func AbilityCommand() (string, error) { } } - capitalizedAbility := cases.Title(language.English).String(strings.ReplaceAll(abilityName, "-", " ")) + capitalizedAbility := styling.CapitalizeResourceName(abilityName) output.WriteString(styling.StyleBold.Render(capitalizedAbility) + "\n") generationParts := strings.Split(abilitiesStruct.Generation.Name, "-") diff --git a/cmd/ability/ability_test.go b/cmd/ability/ability_test.go index 020d6c0..1ce0496 100644 --- a/cmd/ability/ability_test.go +++ b/cmd/ability/ability_test.go @@ -31,6 +31,11 @@ func TestAbilityCommand(t *testing.T) { args: []string{"ability", "clear-body"}, expectedOutput: utils.LoadGolden(t, "ability.golden"), }, + { + name: "Ability command: beads-of-ruin", + args: []string{"ability", "beads-of-ruin"}, + expectedOutput: utils.LoadGolden(t, "ability-ii.golden"), + }, { name: "Misspelled ability name", args: []string{"ability", "bulletproff"}, diff --git a/cmd/berry/berry.go b/cmd/berry/berry.go index 22f9978..5409065 100644 --- a/cmd/berry/berry.go +++ b/cmd/berry/berry.go @@ -30,14 +30,12 @@ func BerryCommand() (string, error) { output.WriteString(helpMessage) } - flag.Parse() - - // Handle help flag - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + // Validate arguments if err := utils.ValidateBerryArgs(os.Args); err != nil { output.WriteString(err.Error()) diff --git a/cmd/card/card.go b/cmd/card/card.go index dc3ed96..3276a26 100644 --- a/cmd/card/card.go +++ b/cmd/card/card.go @@ -26,14 +26,12 @@ func CardCommand() (string, error) { output.WriteString(helpMessage) } - flag.Parse() - - // Handle help flag - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + // Validate arguments if err := utils.ValidateCardArgs(os.Args); err != nil { output.WriteString(err.Error()) diff --git a/cmd/item/item.go b/cmd/item/item.go index 4c5b162..52305a0 100644 --- a/cmd/item/item.go +++ b/cmd/item/item.go @@ -11,8 +11,6 @@ import ( "github.com/digitalghost-dev/poke-cli/connections" "github.com/digitalghost-dev/poke-cli/structs" "github.com/digitalghost-dev/poke-cli/styling" - "golang.org/x/text/cases" - "golang.org/x/text/language" ) func ItemCommand() (string, error) { @@ -22,8 +20,8 @@ func ItemCommand() (string, error) { helpMessage := styling.HelpBorder.Render( "Get details about a specific item.\n\n", styling.StyleBold.Render("USAGE:"), - fmt.Sprintf("\n\t%s %s %s %s", "poke-cli", styling.StyleBold.Render("item"), "", "[flag]"), - fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render("Use a hyphen when typing a name with a space.")), + fmt.Sprintf("\n\t%s %s %s", "poke-cli", styling.StyleBold.Render("item"), ""), + fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render(styling.HyphenHint)), "\n\n", styling.StyleBold.Render("FLAGS:"), fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints the help menu."), @@ -33,13 +31,12 @@ func ItemCommand() (string, error) { args := os.Args - flag.Parse() - - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + if err := utils.ValidateItemArgs(os.Args); err != nil { output.WriteString(err.Error()) return output.String(), err @@ -60,9 +57,9 @@ func ItemCommand() (string, error) { } func itemInfoContainer(output *strings.Builder, itemStruct structs.ItemJSONStruct, itemName string) { - capitalizedItem := styling.StyleBold.Render(cases.Title(language.English).String(strings.ReplaceAll(itemName, "-", " "))) + capitalizedItem := styling.StyleBold.Render(styling.CapitalizeResourceName(itemName)) itemCost := fmt.Sprintf("Cost: %d", itemStruct.Cost) - itemCategory := "Category: " + cases.Title(language.English).String(strings.ReplaceAll(itemStruct.Category.Name, "-", " ")) + itemCategory := "Category: " + styling.CapitalizeResourceName(itemStruct.Category.Name) docStyle := lipgloss.NewStyle(). Padding(1, 2). diff --git a/cmd/move/move.go b/cmd/move/move.go index 6281ad7..37792fb 100644 --- a/cmd/move/move.go +++ b/cmd/move/move.go @@ -3,6 +3,10 @@ package move import ( "flag" "fmt" + "os" + "strconv" + "strings" + "github.com/charmbracelet/lipgloss" "github.com/digitalghost-dev/poke-cli/cmd/utils" "github.com/digitalghost-dev/poke-cli/connections" @@ -10,9 +14,6 @@ import ( "github.com/digitalghost-dev/poke-cli/styling" "golang.org/x/text/cases" "golang.org/x/text/language" - "os" - "strconv" - "strings" ) func MoveCommand() (string, error) { @@ -23,18 +24,20 @@ func MoveCommand() (string, error) { "Get details about a specific move.\n\n", styling.StyleBold.Render("USAGE:"), "\n\t"+"poke-cli"+" "+styling.StyleBold.Render("move")+" ", - "\n\n"+styling.StyleItalic.Render("Use a hyphen when typing a name with a space."), + fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render(styling.HyphenHint)), + "\n\n", + styling.StyleBold.Render("FLAGS:"), + fmt.Sprintf("\n\t%-30s %s", "-h, --help", "Prints the help menu."), ) output.WriteString(helpMessage) } - flag.Parse() - - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + if err := utils.ValidateMoveArgs(os.Args); err != nil { output.WriteString(err.Error()) return output.String(), err @@ -57,7 +60,7 @@ func MoveCommand() (string, error) { } func moveInfoContainer(output *strings.Builder, moveStruct structs.MoveJSONStruct, moveName string) { - capitalizedMove := cases.Title(language.English).String(strings.ReplaceAll(moveName, "-", " ")) + capitalizedMove := styling.CapitalizeResourceName(moveName) docStyle := lipgloss.NewStyle(). Padding(1, 2). diff --git a/cmd/natures/natures.go b/cmd/natures/natures.go index bdd16e5..f12200c 100644 --- a/cmd/natures/natures.go +++ b/cmd/natures/natures.go @@ -3,12 +3,13 @@ package natures import ( "flag" "fmt" + "os" + "strings" + "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/table" "github.com/digitalghost-dev/poke-cli/cmd/utils" "github.com/digitalghost-dev/poke-cli/styling" - "os" - "strings" ) func NaturesCommand() (string, error) { @@ -23,13 +24,12 @@ func NaturesCommand() (string, error) { output.WriteString(helpMessage) } - flag.Parse() - - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + if err := utils.ValidateNaturesArgs(os.Args); err != nil { output.WriteString(err.Error()) return output.String(), err diff --git a/cmd/pokemon/pokemon.go b/cmd/pokemon/pokemon.go index 918fae1..84c23ae 100644 --- a/cmd/pokemon/pokemon.go +++ b/cmd/pokemon/pokemon.go @@ -31,7 +31,7 @@ func PokemonCommand() (string, error) { "Get details about a specific Pokémon.\n\n", styling.StyleBold.Render("USAGE:"), fmt.Sprintf("\n\t%s %s %s %s", "poke-cli", styling.StyleBold.Render("pokemon"), "", "[flag]"), - fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render("Use a hyphen when typing a name with a space.")), + fmt.Sprintf("\n\t%-30s", styling.StyleItalic.Render(styling.HyphenHint)), "\n\n", styling.StyleBold.Render("FLAGS:"), fmt.Sprintf("\n\t%-30s %s", "-a, --abilities", "Prints the Pokémon's abilities."), @@ -49,13 +49,12 @@ func PokemonCommand() (string, error) { args := os.Args - flag.Parse() - - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + err := utils.ValidatePokemonArgs(args) if err != nil { output.WriteString(err.Error()) // This is the styled error @@ -83,7 +82,7 @@ func PokemonCommand() (string, error) { return output.String(), err } - capitalizedString := cases.Title(language.English).String(strings.ReplaceAll(pokemonName, "-", " ")) + capitalizedString := styling.CapitalizeResourceName(pokemonName) entry := func(w io.Writer) { for _, entry := range pokemonSpeciesStruct.FlavorTextEntries { @@ -173,7 +172,7 @@ func PokemonCommand() (string, error) { if pokemonSpeciesStruct.EvolvesFromSpecies.Name != "" { evolvesFrom := pokemonSpeciesStruct.EvolvesFromSpecies.Name - capitalizedPokemonName := cases.Title(language.English).String(strings.ReplaceAll(evolvesFrom, "-", " ")) + capitalizedPokemonName := styling.CapitalizeResourceName(evolvesFrom) fmt.Fprintf(w, "%s %s %s", styling.ColoredBullet, "Evolves from:", capitalizedPokemonName) } else { fmt.Fprintf(w, "%s %s", styling.ColoredBullet, "Basic Pokémon") diff --git a/cmd/search/search.go b/cmd/search/search.go index 5f71a44..f67109e 100644 --- a/cmd/search/search.go +++ b/cmd/search/search.go @@ -25,13 +25,13 @@ func SearchCommand() error { fmt.Println(helpMessage) } - flag.Parse() - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { flag.Usage() return nil } + flag.Parse() + if err := utils.ValidateSearchArgs(os.Args); err != nil { fmt.Println(err.Error()) return err diff --git a/cmd/speed/speed.go b/cmd/speed/speed.go index 48928f9..b30e3f7 100644 --- a/cmd/speed/speed.go +++ b/cmd/speed/speed.go @@ -105,14 +105,12 @@ func SpeedCommand() (string, error) { output.WriteString(helpMessage) } - flag.Parse() - - // Handle help flag - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + // Validate arguments if err := utils.ValidateSpeedArgs(os.Args); err != nil { output.WriteString(err.Error()) diff --git a/cmd/types/types.go b/cmd/types/types.go index a78cf83..918c177 100644 --- a/cmd/types/types.go +++ b/cmd/types/types.go @@ -28,14 +28,12 @@ func TypesCommand() (string, error) { output.WriteString(helpMessage) } - flag.Parse() - - // Handle help flag - if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { - flag.Usage() + if utils.CheckHelpFlag(&output, flag.Usage) { return output.String(), nil } + flag.Parse() + // Validate arguments if err := utils.ValidateTypesArgs(os.Args); err != nil { output.WriteString(err.Error()) diff --git a/cmd/utils/output.go b/cmd/utils/output.go index e658261..a73a4be 100644 --- a/cmd/utils/output.go +++ b/cmd/utils/output.go @@ -26,6 +26,14 @@ func HandleFlagError(output *strings.Builder, err error) (string, error) { return "", fmt.Errorf("error parsing flags: %w", err) } +func CheckHelpFlag(output *strings.Builder, usageFunc func()) bool { + if len(os.Args) == 3 && (os.Args[2] == "-h" || os.Args[2] == "--help") { + usageFunc() + return true + } + return false +} + func WrapText(text string, width int) string { words := strings.Fields(text) if len(words) == 0 { diff --git a/cmd/utils/output_test.go b/cmd/utils/output_test.go index cc2c865..be92c8c 100644 --- a/cmd/utils/output_test.go +++ b/cmd/utils/output_test.go @@ -136,3 +136,119 @@ func TestWrapText_MultipleLines(t *testing.T) { t.Fatalf("expected %q, got %q", expected, got) } } + +func TestCheckHelpFlag_ShortFlag(t *testing.T) { + // Save and restore original os.Args + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + + os.Args = []string{"poke-cli", "pokemon", "-h"} + + var output strings.Builder + usageCalled := false + usageFunc := func() { + output.WriteString("help message") + usageCalled = true + } + + result := CheckHelpFlag(&output, usageFunc) + + if !result { + t.Error("CheckHelpFlag should return true for -h flag") + } + if !usageCalled { + t.Error("usage function should be called for -h flag") + } +} + +func TestCheckHelpFlag_LongFlag(t *testing.T) { + // Save and restore original os.Args + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + + os.Args = []string{"poke-cli", "pokemon", "--help"} + + var output strings.Builder + usageCalled := false + usageFunc := func() { + output.WriteString("help message") + usageCalled = true + } + + result := CheckHelpFlag(&output, usageFunc) + + if !result { + t.Error("CheckHelpFlag should return true for --help flag") + } + if !usageCalled { + t.Error("usage function should be called for --help flag") + } +} + +func TestCheckHelpFlag_NoFlag(t *testing.T) { + // Save and restore original os.Args + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + + os.Args = []string{"poke-cli", "pokemon", "charizard"} + + var output strings.Builder + usageCalled := false + usageFunc := func() { + usageCalled = true + } + + result := CheckHelpFlag(&output, usageFunc) + + if result { + t.Error("CheckHelpFlag should return false when no help flag present") + } + if usageCalled { + t.Error("usage function should not be called when no help flag present") + } +} + +func TestCheckHelpFlag_WrongNumberOfArgs(t *testing.T) { + tests := []struct { + name string + args []string + }{ + { + name: "too few args", + args: []string{"poke-cli", "pokemon"}, + }, + { + name: "too many args", + args: []string{"poke-cli", "pokemon", "-h", "extra"}, + }, + { + name: "help flag in wrong position", + args: []string{"poke-cli", "pokemon", "charizard", "-h"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save and restore original os.Args + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + + os.Args = tt.args + + var output strings.Builder + usageCalled := false + usageFunc := func() { + usageCalled = true + } + + result := CheckHelpFlag(&output, usageFunc) + + if result { + t.Errorf("CheckHelpFlag should return false for args: %v", tt.args) + } + if usageCalled { + t.Errorf("usage function should not be called for args: %v", tt.args) + } + }) + } +} diff --git a/flags/pokemonflagset.go b/flags/pokemonflagset.go index d680f4e..27f8604 100644 --- a/flags/pokemonflagset.go +++ b/flags/pokemonflagset.go @@ -106,32 +106,8 @@ func AbilitiesFlag(w io.Writer, endpoint string, pokemonName string) error { return err } - // Anonymous function to format ability names - formatAbilityName := func(name string) string { - exceptions := map[string]bool{ - "of": true, - "the": true, - "to": true, - "as": true, - } - - name = strings.ReplaceAll(name, "-", " ") - words := strings.Split(name, " ") - titleCaser := cases.Title(language.English) - - // Process each word - for i, word := range words { - if _, found := exceptions[strings.ToLower(word)]; found && i != 0 { - words[i] = strings.ToLower(word) - } else { - words[i] = titleCaser.String(word) - } - } - return strings.Join(words, " ") - } - for _, pokeAbility := range pokemonStruct.Abilities { - formattedName := formatAbilityName(pokeAbility.Ability.Name) + formattedName := styling.CapitalizeResourceName(pokeAbility.Ability.Name) switch pokeAbility.Slot { case 1, 2: @@ -228,7 +204,7 @@ func DefenseFlag(w io.Writer, endpoint string, pokemonName string) error { for _, ability := range pokemonStruct.Abilities { abilityName := ability.Ability.Name - formattedAbilityName := cases.Title(language.English).String(strings.ReplaceAll(abilityName, "-", " ")) + formattedAbilityName := styling.CapitalizeResourceName(abilityName) if types, exists := abilityImmunities[abilityName]; exists { typeList := strings.Join(types, " and ") @@ -465,7 +441,7 @@ func MovesFlag(w io.Writer, endpoint string, pokemonName string) error { return } - capitalizedMove := cases.Title(language.English).String(strings.ReplaceAll(moveName, "-", " ")) + capitalizedMove := styling.CapitalizeResourceName(moveName) capitalizedType := cases.Title(language.English).String(moveStruct.Type.Name) movesChan <- MoveInfo{ diff --git a/pokemon.svg b/pokemon.svg deleted file mode 100644 index 5438367..0000000 --- a/pokemon.svg +++ /dev/null @@ -1 +0,0 @@ -Pokémon diff --git a/styling/styling.go b/styling/styling.go index 3e4ac93..33bd823 100644 --- a/styling/styling.go +++ b/styling/styling.go @@ -4,9 +4,16 @@ import ( "fmt" "image/color" "regexp" + "strings" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +const ( + HyphenHint = "Use a hyphen when typing a name with a space." ) var ( @@ -76,6 +83,33 @@ func StripANSI(input string) string { return ansiRegex.ReplaceAllString(input, "") } +// smallWords are words that should remain lowercase in titles (unless first word) +var smallWords = map[string]bool{ + "of": true, + "the": true, + "to": true, + "as": true, +} + +// CapitalizeResourceName converts hyphenated resource names to title case +// Example: "strong-jaw" -> "Strong Jaw", "sword-of-ruin" -> "Sword of Ruin" +func CapitalizeResourceName(name string) string { + caser := cases.Title(language.English) + + name = strings.ReplaceAll(name, "-", " ") + words := strings.Split(name, " ") + + for i, word := range words { + if _, found := smallWords[strings.ToLower(word)]; found && i != 0 { + words[i] = strings.ToLower(word) + } else { + words[i] = caser.String(word) + } + } + + return strings.Join(words, " ") +} + // Color To avoid unnecessary dependencies, I adapted the MakeColor function from // "github.com/lucasb-eyer/go-colorful" and implemented it using only the // standard library. Since I only needed this function, importing the entire diff --git a/testdata/ability-ii.golden b/testdata/ability-ii.golden new file mode 100644 index 0000000..6b9b745 --- /dev/null +++ b/testdata/ability-ii.golden @@ -0,0 +1,3 @@ +Beads of Ruin +• First introduced in generation IX +• Effect: Lowers Special Defense of all Pokémon except itself. \ No newline at end of file diff --git a/testdata/cli_help.golden b/testdata/cli_help.golden index a86fca9..9596b16 100644 --- a/testdata/cli_help.golden +++ b/testdata/cli_help.golden @@ -23,7 +23,7 @@ │ speed Calculate the speed of a Pokémon in battle │ │ types Get details about a typing │ │ │ -│ hint: when calling a resource with a space, use a hyphen │ +│ Use a hyphen when typing a name with a space. │ │ example: poke-cli ability strong-jaw │ │ example: poke-cli pokemon flutter-mane │ │ │ diff --git a/testdata/item_help.golden b/testdata/item_help.golden index c197516..f41f947 100644 --- a/testdata/item_help.golden +++ b/testdata/item_help.golden @@ -2,7 +2,7 @@ │Get details about a specific item. │ │ │ │ USAGE: │ -│ poke-cli item [flag] │ +│ poke-cli item │ │ Use a hyphen when typing a name with a space. │ │ │ │ FLAGS: │ diff --git a/testdata/main_latest_flag.golden b/testdata/main_latest_flag.golden index 2de4c1c..1b715f7 100644 --- a/testdata/main_latest_flag.golden +++ b/testdata/main_latest_flag.golden @@ -2,6 +2,6 @@ ┃ ┃ ┃ Latest available release ┃ ┃ on GitHub: ┃ -┃ • v1.8.3 ┃ +┃ • v1.8.4 ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ diff --git a/testdata/move_help.golden b/testdata/move_help.golden index 998fb11..4a41117 100644 --- a/testdata/move_help.golden +++ b/testdata/move_help.golden @@ -1,8 +1,10 @@ -╭─────────────────────────────────────────────╮ -│Get details about a specific move. │ -│ │ -│ USAGE: │ -│ poke-cli move │ -│ │ -│Use a hyphen when typing a name with a space.│ -╰─────────────────────────────────────────────╯ \ No newline at end of file +╭────────────────────────────────────────────────────────╮ +│Get details about a specific move. │ +│ │ +│ USAGE: │ +│ poke-cli move │ +│ Use a hyphen when typing a name with a space. │ +│ │ +│ FLAGS: │ +│ -h, --help Prints the help menu.│ +╰────────────────────────────────────────────────────────╯ \ No newline at end of file