This commit is contained in:
89
lib/cobblemon_ui/poke_api.ex
Normal file
89
lib/cobblemon_ui/poke_api.ex
Normal file
@@ -0,0 +1,89 @@
|
||||
defmodule CobblemonUi.PokeApi do
|
||||
@moduledoc """
|
||||
Fetches and caches Pokémon type data from PokeAPI.
|
||||
|
||||
Uses an ETS table for fast lookups. Data is fetched once per species
|
||||
and cached for the lifetime of the process.
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
@table :poke_api_types_cache
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Client API
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def start_link(_opts) do
|
||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of type names (lowercase strings) for the given species.
|
||||
|
||||
Example: `get_types("bulbasaur")` → `["grass", "poison"]`
|
||||
"""
|
||||
@spec get_types(String.t()) :: [String.t()]
|
||||
def get_types(species) when is_binary(species) do
|
||||
key = String.downcase(species)
|
||||
|
||||
case :ets.lookup(@table, key) do
|
||||
[{^key, types}] ->
|
||||
types
|
||||
|
||||
[] ->
|
||||
GenServer.call(__MODULE__, {:fetch_types, key}, 15_000)
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Server callbacks
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@impl true
|
||||
def init(_opts) do
|
||||
:ets.new(@table, [:named_table, :set, :public, read_concurrency: true])
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:fetch_types, species}, _from, state) do
|
||||
result =
|
||||
case :ets.lookup(@table, species) do
|
||||
[{^species, types}] ->
|
||||
types
|
||||
|
||||
[] ->
|
||||
types = fetch_types(species)
|
||||
:ets.insert(@table, {species, types})
|
||||
types
|
||||
end
|
||||
|
||||
{:reply, result, state}
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
defp fetch_types(species) do
|
||||
url = "https://pokeapi.co/api/v2/pokemon/#{species}"
|
||||
|
||||
case Req.get(url) do
|
||||
{:ok, %Req.Response{status: 200, body: %{"types" => types}}} ->
|
||||
types
|
||||
|> Enum.sort_by(fn t -> t["slot"] end)
|
||||
|> Enum.map(fn t -> get_in(t, ["type", "name"]) end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
{:ok, %Req.Response{status: status}} ->
|
||||
Logger.warning("[PokeApi] Type lookup failed for #{species}: HTTP #{status}")
|
||||
[]
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("[PokeApi] Type lookup failed for #{species}: #{inspect(reason)}")
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user