Current section
Files
Jump to
Current section
Files
lib/nba/utils.ex
defmodule NBA.Utils do
@moduledoc false
# Normalizes a player or team ID to an integer.
def integer_id(val) when is_integer(val), do: val
def integer_id(val) when is_binary(val), do: String.to_integer(val)
# Normalizes a player or team ID to a string.
def string_id(val) when is_binary(val), do: val
def string_id(val) when is_integer(val), do: Integer.to_string(val)
# Validates whether a player or team ID is either an integer or a numeric string.
def valid_id?(val) when is_integer(val), do: true
def valid_id?(val) when is_binary(val), do: String.match?(val, ~r/^\d+$/)
def valid_id?(_), do: false
# Validates input parameters and options against accepted types and required keys.
# Accepts keyword lists of parameters and options, a map of accepted types, and optionally a list of required param keys.
# Returns :ok if valid, or an error tuple if invalid.
def validate_input(params, opts, accepted_types, required_keys \\ [])
def validate_input(params, opts, accepted_types, required_keys)
when is_list(params) and is_list(opts) do
# 1. Ensure all required keys are present
missing = Enum.filter(required_keys, fn key -> Keyword.get(params, key) == nil end)
if missing != [] do
{:error, {:missing_keys, missing}}
else
# 2. Continue to type validation
allowed_keys = Map.keys(accepted_types)
invalid_keys = Keyword.keys(params) -- allowed_keys
if invalid_keys != [] do
{:error, {:invalid_keys, invalid_keys}}
else
Enum.reduce_while(accepted_types, :ok, fn {key, types}, acc ->
case Keyword.get(params, key) do
nil ->
{:cont, acc}
value ->
cond do
# Special case: accept numeric strings when integer is allowed
types == [:integer, :string] and not valid_id?(value) ->
{:halt, {:error, {:invalid_type, key, types, value}}}
Enum.any?(types, &type_check?(&1, value)) ->
{:cont, acc}
true ->
{:halt, {:error, {:invalid_type, key, types, value}}}
end
end
end)
end
end
end
def validate_input(_params, _opts, _types, _req), do: {:error, :invalid_input_format}
# Handles validation errors by returning a standardized error tuple.
# This function takes an error tuple from `validate_input/3` and returns a more user-friendly error message.
def handle_validation_error({:error, {:missing_keys, keys}}) do
{:error, "Missing required parameter(s): #{Enum.map_join(keys, ", ", &inspect/1)}"}
end
def handle_validation_error({:error, {:invalid_keys, keys}}) do
{:error, "Invalid parameter(s): #{Enum.map_join(keys, ", ", &inspect/1)}"}
end
def handle_validation_error({:error, {:invalid_type, key, expected_types, value}}) do
{:error,
"Invalid type for #{key}: got #{inspect(value)}, accepts #{Enum.join(Enum.map(expected_types, &Atom.to_string/1), ", ")}"}
end
def handle_validation_error({:error, :invalid_input_format}) do
{:error, "Parameters and Options must be keyword lists or nil"}
end
def handle_validation_error({:error, _} = err), do: err
def handle_validation_error(other), do: {:error, {:unexpected, other}}
# Handles common API response errors.
def handle_api_error({:error, _} = err), do: err
def handle_api_error(other), do: {:error, {:unexpected, other}}
defp type_check?(:string, val), do: is_binary(val)
defp type_check?(:integer, val), do: is_integer(val)
defp type_check?(:boolean, val), do: is_boolean(val)
defp type_check?(:float, val), do: is_float(val)
defp type_check?(_, _), do: false
defmacro def_get_bang(base_module) do
quote do
def get!(params \\ [], opts \\ []) do
case unquote(base_module).get(params, opts) do
{:ok, result} ->
result
{:error, reason} ->
raise ArgumentError, "Failed to fetch #{unquote(base_module)}: #{inspect(reason)}"
end
end
end
end
end