defmodule Momento.Validation do
import Momento.Error
@spec validate_cache_name(cache_name :: String.t()) :: :ok | {:error, Momento.Error.t()}
def validate_cache_name(cache_name) do
validate_string(cache_name, "cache name")
end
@spec validate_sorted_set_name(sorted_set_name :: String.t()) ::
:ok | {:error, Momento.Error.t()}
def validate_sorted_set_name(sorted_set_name) do
validate_string(sorted_set_name, "sorted set name")
end
@spec validate_sorted_set_elements(elements :: %{binary() => number()} | [{binary(), number()}]) ::
:ok | {:error, Momento.Error.t()}
def validate_sorted_set_elements(nil),
do: {:error, invalid_argument("Sorted set elements cannot be nil")}
def validate_sorted_set_elements(elements) do
try do
case Enum.all?(elements, fn {value, score} ->
is_binary(value) and is_number(score)
end) do
true ->
:ok
false ->
{:error,
invalid_argument(
"Sorted set elements must contain only binary values and float scores"
)}
end
rescue
e ->
{:error,
invalid_argument(
"Sorted set elements must be a map or list of tuples of values and scores",
e
)}
end
end
@spec validate_sort_order(sort_order :: atom()) :: :ok | {:error, Momento.Error.t()}
def validate_sort_order(sort_order) when sort_order in [:asc, :desc], do: :ok
def validate_sort_order(_),
do: {:error, invalid_argument("The sort order must be either :asc or :desc")}
@spec validate_key(key :: binary()) :: :ok | {:error, Momento.Error.t()}
def validate_key(key), do: validate_binary(key, "key")
@spec validate_value(value :: binary()) :: :ok | {:error, Momento.Error.t()}
def validate_value(value), do: validate_binary(value, "value")
@spec validate_score(score :: number()) :: :ok | {:error, Momento.Error.t()}
def validate_score(score), do: validate_number(score, "score")
@spec validate_ttl(ttl :: number()) :: :ok | {:error, Momento.Error.t()}
def validate_ttl(ttl), do: validate_positive_number(ttl, "TTL")
@spec validate_collection_ttl(collection_ttl :: Momento.Requests.CollectionTtl.t()) ::
:ok | {:error, Momento.Error.t()}
def validate_collection_ttl(collection_ttl) do
with :ok <-
validate_struct(
collection_ttl,
"collection_ttl",
Elixir.Momento.Requests.CollectionTtl
),
:ok <- validate_positive_number(collection_ttl.ttl_seconds, "TTL") do
:ok
else
error -> error
end
end
@spec validate_index_range(start_index :: integer() | nil, end_index :: integer() | nil) ::
:ok | {:error, Momento.Error.t()}
def validate_index_range(nil, _), do: :ok
def validate_index_range(_, nil), do: :ok
def validate_index_range(start_index, _) when not is_integer(start_index),
do: {:error, invalid_argument("#{start_index} is not an integer")}
def validate_index_range(_, end_index) when not is_integer(end_index),
do: {:error, invalid_argument("#{end_index} is not an integer")}
def validate_index_range(start_index, end_index) when start_index < end_index, do: :ok
def validate_index_range(_, _),
do:
{:error,
invalid_argument("start_index (inclusive) must be less than end_index (exclusive)")}
@spec validate_not_nil(any(), String.t()) :: :ok | {:error, Momento.Error.t()}
def validate_not_nil(nil, name), do: {:error, invalid_argument("#{name} cannot be nil")}
def validate_not_nil(_, _), do: :ok
@spec validate_string(string :: String.t(), name_type :: String.t()) ::
:ok | {:error, Momento.Error.t()}
defp validate_string(nil, string_name),
do: {:error, invalid_argument("The #{string_name} cannot be nil")}
defp validate_string(string, string_name) do
with true <- is_binary(string),
String.valid?(string) do
:ok
else
_ -> {:error, invalid_argument("The #{string_name} must be a string")}
end
end
@spec validate_binary(binary :: binary(), binary_name :: String.t()) ::
:ok | {:error, Momento.Error.t()}
defp validate_binary(nil, binary_name),
do: {:error, invalid_argument("The #{binary_name} cannot be nil")}
defp validate_binary(binary, _) when is_binary(binary), do: :ok
defp validate_binary(_, binary_name),
do: {:error, invalid_argument("The #{binary_name} must be a binary")}
@spec validate_positive_number(num :: number(), name :: String.t()) ::
:ok | {:error, Momento.Error.t()}
defp validate_positive_number(float, name) do
case validate_number(float, name) do
:ok ->
if float > 0.0 do
:ok
else
{:error, invalid_argument("The #{name} must be positive")}
end
error ->
error
end
end
@spec validate_number(num :: number(), name :: String.t()) ::
:ok | {:error, Momento.Error.t()}
defp validate_number(nil, name),
do: {:error, invalid_argument("The #{name} cannot be nil")}
defp validate_number(num, _) when is_number(num), do: :ok
defp validate_number(_, name),
do: {:error, invalid_argument("The #{name} must be a number")}
@spec validate_struct(struct :: struct(), struct_name :: String.t(), struct_type :: atom()) ::
:ok | {:error, Momento.Error.t()}
defp validate_struct(nil, struct_name, _),
do: {:error, invalid_argument("The #{struct_name} cannot be nil")}
defp validate_struct(struct, _, struct_type) when struct.__struct__ == struct_type, do: :ok
defp validate_struct(_, struct_name, struct_type),
do: {:error, invalid_argument("#{struct_name} must be an #{Atom.to_string(struct_type)}")}
end