Current section

Files

Jump to
odgn_json_pointer lib json_pointer utils.ex
Raw

lib/json_pointer/utils.ex

defmodule JSONPointer.Utils do
@moduledoc false
@type pointer :: String.t() | [String.t()]
@type msg :: String.t()
@doc """
Escapes a reference token
## Examples
iex> JSONPointer.Utils.escape "hello~bla"
"hello~0bla"
iex> JSONPointer.Utils.escape "hello/bla"
"hello~1bla"
"""
@spec escape(String.t()) :: String.t()
def escape(str) do
str
|> String.replace("~", "~0")
|> String.replace("/", "~1")
|> String.replace("**", "~2")
end
@doc """
Unescapes a reference token
## Examples
iex> JSONPointer.Utils.unescape "hello~0bla"
"hello~bla"
iex> JSONPointer.Utils.unescape "hello~1bla"
"hello/bla"
iex> JSONPointer.Utils.unescape "/~01"
"/~1"
"""
@spec unescape(String.t()) :: String.t()
def unescape(str) do
str
|> String.replace("~2", "**")
|> String.replace("~1", "/")
|> String.replace("~0", "~")
end
@doc """
Converts a JSON pointer into a list of reference tokens
## Examples
iex> JSONPointer.Utils.parse("/fridge/butter")
{:ok, [ "fridge", "butter"] }
"""
def parse(""), do: {:ok, []}
def parse(pointer) when is_list(pointer), do: {:ok, pointer}
@spec parse(pointer) :: {:ok, [String.t()]} | {:error, msg, pointer}
def parse(pointer) do
# handle a URI Fragment
pointer = String.trim_leading(pointer, "#")
case String.first(pointer) do
"/" ->
{:ok,
pointer
|> String.trim_leading("/")
|> String.split("/")
# |> Enum.map(&URI.decode/1) # NOTE - decoding uri parts is not spec compliant (and not needed) - so removed
|> Enum.map(&unescape/1)}
_ ->
{:error, "invalid json pointer", pointer}
end
end
# @doc """
# Ensures that the given list has size number of elements
# ## Examples
# iex> JSONPointer.ensure_list_size( [], 2 )
# [nil, nil]
# """
@spec ensure_list_size(list, non_neg_integer()) :: list
def ensure_list_size(list, size) do
diff = size - Enum.count(list)
if diff > 0 do
list ++ List.duplicate(nil, diff)
else
list
end
end
@doc """
Attempts to converts a value into an integer
## Examples
iex> JSONPointer.Utils.parse_index 10
10
iex> JSONPointer.Utils.parse_index "100"
100
iex> JSONPointer.Utils.parse_index "92.4"
{:error, "invalid index: 92.4"}
"""
def parse_index("0"), do: 0
def parse_index(<<"0", rest::binary>>), do: "0" <> rest
def parse_index(val) when is_integer(val), do: val
# defp parse_index(val) when is_float(val), do: {Kernel.trunc(val), 0}
def parse_index(val) do
case Integer.parse(val) do
{int, rem} ->
if rem != "", do: {:error, "invalid index: #{val}"}, else: int
:error ->
{:error, "invalid index: #{val}"}
end
end
@doc """
Checks whether two values are equal, returns an error message if they
are not
## Examples
iex> JSONPointer.Utils.are_equal? "hello", "hello"
{:ok, true}
iex> JSONPointer.Utils.are_equal? "10", 10
{:error, "number is not equal to string"}
iex> JSONPointer.Utils.are_equal? [10, "12", 15], [15, "12", 10]
{:error, "not equal"}
"""
@spec are_equal?(String.t(), number) :: {:error, String.t()}
def are_equal?(val1, val2) when is_binary(val1) and is_number(val2),
do: {:error, "number is not equal to string"}
@spec are_equal?(number, String.t()) :: {:error, String.t()}
def are_equal?(val1, val2) when is_number(val1) and is_binary(val2),
do: {:error, "string is not equal to number"}
@spec are_equal?(any(), any()) :: {:ok, true} | {:error, String.t()}
def are_equal?(val1, val2) do
if val1 == val2 do
{:ok, true}
else
are_not_equal_error(val1, val2)
end
end
defp are_not_equal_error(val1, val2) when is_binary(val1) and is_binary(val2),
do: {:error, "string not equivalent"}
defp are_not_equal_error(_val1, _val2), do: {:error, "not equal"}
end