defmodule Value do
@moduledoc """
Value
"""
def insert(_scope, _fields, _value, _idx \\ nil)
def insert(nil, [], _value, _idx), do: nil
def insert(nil, fields, value, _idx) do
fields
|> Enum.reverse()
|> Enum.reduce(value, fn field, acc -> Map.put(%{}, field, acc) end)
end
def insert(_scope, [], value, _idx), do: value
def insert([], _fields, _value, _idx), do: []
def insert({scope, status}, fields, value, idx) do
{insert(scope, fields, value, idx), status}
end
def insert([{idx, scope} | tail], fields, value, idx) do
[{idx, insert(scope, fields, value)} | insert(tail, fields, value, idx)]
end
def insert([{key, scope} | tail], fields, value, idx) do
[{key, scope} | insert(tail, fields, value, idx)]
end
def insert(scope, field, value, idx) when is_bitstring(field),
do: insert(scope, String.split(field, "."), value, idx)
def insert(scope, [field | []], value, _idx) do
get_type(field)
|> case do
{:string, field} ->
existing_key(scope, field)
|> case do
{field, :exists} ->
Map.get(scope, field)
|> maybe_merge(value, scope, field)
{field, :new} ->
Map.put(scope, field, value)
end
_ ->
scope
end
end
def insert(scope, [field | tail], value, idx_r) do
get_type(field)
|> case do
{:string, field} ->
{field, _} = existing_key(scope, field)
scope_deep =
get_scope(scope, field)
|> insert(tail, value, idx_r)
Map.get(scope, field)
|> maybe_merge(scope_deep, scope, field)
{:array, field} ->
{field, _} = existing_key(scope, field)
get_scope(scope, field)
|> insert(tail, value, idx_r)
{:array, field, "@"} ->
{field, _} = existing_key(scope, field)
scope_deep = get_scope(scope, field) || []
scoped =
if is_list(value) do
value
|> Enum.with_index()
# |> Flow.from_enumerable(max_demand: 25)
# |> Flow.map(fn {idx, item} ->
# {idx, scope_deep |> Enum.at(idx) |> insert(tail, item, idx_r)}
# end)
# |> Flow.partition()
# |> Flow.reduce(fn -> [] end, fn item, acc -> acc ++ [item] end)
|> Enum.map(fn {item, idx} ->
{idx, scope_deep |> Enum.at(idx) |> insert(tail, item, idx_r)}
end)
|> List.flatten()
|> Enum.sort_by(&elem(&1, 0))
|> Enum.map(&elem(&1, 1))
else
scope_deep
|> Enum.map(&insert(&1, tail, value, idx_r))
end
if field == "_" do
scoped
else
insert(scope, field, scoped)
end
{:array, field, index} ->
{field, _} = existing_key(scope, field)
scope_deep = get_scope(scope, field)
row =
scope_deep
|> Enum.at(index)
|> insert(tail, value, idx_r)
scoped = scope_deep |> List.replace_at(index, row)
Map.replace(scope, field, scoped)
end
end
defp maybe_merge(nil, value, scope, field), do: Map.put(scope, field, value)
defp maybe_merge(_value1, nil, scope, field), do: Map.put(scope, field, nil)
defp maybe_merge(value1, value2, scope, field) when is_map(value1) and is_map(value2) do
value3 = Map.merge(value1, value2)
Map.put(scope, field, value3)
end
defp maybe_merge(value1, value2, scope, field) when is_list(value1) do
Map.replace(scope, field, value2)
end
defp maybe_merge(_value1, value2, scope, field) do
Map.put(scope, field, value2)
end
def replace(string, search, value, scope) do
Map.get(scope, :response)
# value = Parse.get_from(value, scope)
String.replace(string, search, "#{value}")
end
@doc """
Get require value returning {:ok, value}
"""
def getr(scope, field, opts) when is_list(opts) do
if Keyword.keyword?(opts) && opts != [] do
default = opts[:default]
has_when? = Keyword.has_key?(opts, :when)
when_value = opts[:when]
when_default = opts[:when_default]
cond do
has_when? && when_value == true -> getr(scope, field, default)
has_when? && when_value == false -> {:ok, when_default}
:else -> getr(scope, field, default)
end
else
get(scope, field, opts)
|> case do
nil ->
{:error, :required}
v ->
{:ok, v}
end
end
end
def getr(scope, field, default \\ nil, atom \\ :required) do
get(scope, field, default)
|> case do
nil -> {:error, atom}
v -> {:ok, v}
end
end
def getr!(scope, field, default \\ nil, atom \\ :required) do
get(scope, field, default)
|> case do
nil -> raise "Value of '#{field}' is #{atom}"
v -> v
end
end
def getm(scope, fields, opts) when is_bitstring(fields) do
has_when? = Keyword.has_key?(opts, :when)
when_value = opts[:when]
when_default = opts[:when_default]
cond do
has_when? && when_value == true -> getm(scope, fields)
has_when? && when_value == false -> when_default
:else -> getm(scope, fields)
end
end
def getm(scope, fields) when is_bitstring(fields) do
String.split(fields, ",")
|> Enum.map(fn fields ->
if String.starts_with?(fields, "^") do
String.replace(fields, "^", "")
else
{fields, get(scope, fields |> String.split("."))}
end
end)
|> Map.new()
end
def get(_scope, _locate, _default \\ nil)
def get(scope, field, opts) when is_list(opts) do
if Keyword.keyword?(opts) && opts != [] do
default = opts[:default]
has_when? = Keyword.has_key?(opts, :when)
when_value = opts[:when]
when_default = opts[:when_default]
cond do
has_when? && when_value == true -> get(scope, "#{field}", default)
has_when? && when_value == false -> when_default
:else -> String.split(field, "|") |> try_get(scope, opts)
end
else
get(scope, "#{field}") || opts
end
end
def get(_scope, nil, default), do: default
def get(nil, [], default), do: default
def get(scope, [], _default), do: scope
def get(scope, field, default) when is_atom(field), do: get(scope, "#{field}", default)
def get(scope, fields, default) when is_bitstring(fields) do
if String.starts_with?(fields, "^") do
String.replace(fields, "^", "")
else
String.split(fields, "|")
|> try_get(scope, default: default)
end
end
def get(scope, [field | tail], default) do
get_type(field)
|> case do
{:string, field} ->
get_scope(scope, field)
|> get(tail, default)
{:array, field} ->
get_scope(scope, field)
|> get(tail, default)
{:array, field, index} ->
get_scope(scope, field)
|> Enum.at(index)
|> get(tail, default)
# call_function(scope, locate_field, insert_field, scope_field, service, function)
end
end
def get(_scope, value, _opts), do: value
def try_get([], _scope, opts), do: opts[:default]
def try_get([fld | flds], scope, opts) do
null_values = default_null_values(opts)
value = get(scope, fld |> String.split("."), opts[:default])
cond do
value in null_values -> try_get(flds, scope, opts)
:else -> value
end
end
defp default_null_values(opts) do
case opts[:null_values] do
list when is_list(list) -> list
value -> [value]
end
end
def get_scope({_idx, scope}, field) do
get_scope(scope, field)
end
def get_scope(scope, "_") when is_list(scope) do
scope
end
def get_scope(scope, field) when is_map(scope) do
get_value_from_field(scope, field)
end
def get_scope(scope, field) when is_list(scope) do
scope |> Enum.map(&get_scope(&1, field))
end
def get_scope(scope, _field) when is_tuple(scope) do
:invalid_data
end
def get_scope(scope, field), do: get_value_from_field(scope, field)
defp get_value_from_field(scope, field) when is_map(scope) do
case existing_key(scope, field) do
{field, :exists} ->
Map.get(scope, field)
{_field, :new} ->
nil
{_scope, :array} ->
field
end
end
defp get_value_from_field(_scope, _field), do: nil
defp existing_key(scope, "_") when is_list(scope) do
{"_", :base}
end
defp existing_key(scope, field) do
if Map.has_key?(scope, field) do
{field, :exists}
else
try do
field_atom = String.to_existing_atom(field)
if Map.has_key?(scope, field_atom) do
{field_atom, :exists}
else
{field, :new}
end
rescue
_ -> {field, :new}
end
end
end
defp get_type(param) do
param = param |> to_string()
Regex.run(~r/\[(\d)?(\W)?\]/, param)
|> case do
nil ->
{:string, param}
[x] ->
{:array, String.replace(param, x, "")}
[x, _, y] when y in ["@", "*"] ->
{:array, String.replace(param, x, ""), "@"}
[x, num] ->
{:array, String.replace(param, x, ""), String.to_integer(num)}
end
end
end