Current section

Files

Jump to
Raw

lib/core.ex

defmodule ESC.Core do
@moduledoc false
@del_recursive_init_data {0, []}
### Interface
def delete_by_struct(list, struct_name, struct_id)
when is_list(list) and is_atom(struct_name) and is_integer(struct_id) do
traverse_kv(list, struct_name, struct_id)
end
def delete(list, id, len)
when is_list(list) and is_integer(id) and is_integer(len) and len >= 0,
do: list |> del_by_conds({id}) |> make_del_result(list, len)
def delete(list, conds, len)
when is_list(list) and is_list(conds) and is_integer(len) and len >= 0,
do: list |> del_by_conds(conds) |> make_del_result(list, len)
def get_refs(obj) do
with(
{:ok, struct_name} <- Map.fetch(obj, :__struct__),
refs <- drill_down(obj)
) do
{:ok, {struct_name, refs}}
else
:error -> :obj_is_not_struct
end
end
### Implements Get Refs
def drill_down(obj), do: do_drill_down(%MapSet{}, Map.values(obj))
def do_drill_down(refs, []), do: refs
def do_drill_down(refs, [val | rest]) when is_list(val) do
Enum.reduce(val, %MapSet{}, fn obj, acc_refs ->
refs = getput_struct_name(refs, obj)
obj |> drill_down() |> MapSet.union(acc_refs) |> MapSet.union(refs)
end)
|> MapSet.union(refs)
|> do_drill_down(rest)
end
def do_drill_down(refs, [%{} = val | rest]) do
new_refs = getput_struct_name(refs, val)
val |> drill_down() |> MapSet.union(new_refs) |> do_drill_down(rest)
end
def do_drill_down(refs, [_val | rest]), do: do_drill_down(refs, rest)
def getput_struct_name(refs, val) do
Map.fetch(val, :__struct__)
|> case do
{:ok, struct_name} -> MapSet.put(refs, struct_name)
_ -> refs
end
end
### Implements Delete
def del_by_conds([] = _list, _conds), do: @del_recursive_init_data
def del_by_conds(list, conds), do: do_del_by_conds(list, conds, @del_recursive_init_data)
def do_del_by_conds(list, [] = _conds, {idx, _left}), do: {idx, list}
def do_del_by_conds([] = _list, _conds, {idx, left}), do: {idx, Enum.reverse(left)}
def do_del_by_conds([%_{id: id} = obj | rest], {expected_id} = conds, {idx, left}) do
with(
found? <- expected_id == id,
left <- if(found?, do: left, else: [obj | left]),
idx <- idx + 1,
rest <- if(found?, do: [], else: rest)
) do
do_del_by_conds(rest, conds, {idx, left})
end
end
def do_del_by_conds([%_{} = obj | rest], conds, {idx, left}) do
with(
found? <- found?(obj, conds),
left <- if(found?, do: left, else: [obj | left]),
idx <- idx + 1
) do
do_del_by_conds(rest, conds, {idx, left})
end
end
def make_del_result({idx, left}, list, len), do: left ++ make_right(list, idx, len)
### Implements Delete By Struct
def traverse_kv(list, struct_name, struct_id), do: do_traverse_kv(list, struct_name, struct_id, [], [])
def do_traverse_kv([] = _list, _struct_name, _struct_id, new_list, new_del_ids) do
{new_del_ids, Enum.reverse(new_list)}
end
def do_traverse_kv([%_{id: id} = obj | rest], struct_name, struct_id, new_list, del_ids) do
with(
del? when is_atom(del?) <- drill_down_del?(obj, struct_name, struct_id),
new_list <- if(del?, do: new_list, else: [obj | new_list]),
new_del_ids <- if(del?, do: [id | del_ids], else: del_ids)
) do
do_traverse_kv(rest, struct_name, struct_id, new_list, new_del_ids)
end
end
def drill_down_del?(%_{} = obj, struct_name, struct_id) do
obj
|> Map.from_struct()
|> Keyword.new()
|> del?(struct_name, struct_id, false)
end
def del?([] = _obj, _struct_name, _struct_id, should?), do: should?
def del?([{_k, %_{} = obj} | rest], struct_name, struct_id, _should?) do
should? =
obj_del?(obj, struct_name, struct_id)
|> if(
do: del?(rest, struct_name, struct_id, true),
else: drill_down_del?(obj, struct_name, struct_id)
)
del?(rest, struct_name, struct_id, should?)
end
def del?([{_k, [%_{} | _] = objs} | rest], struct_name, struct_id, _should?) do
should? =
objs_del?(objs, struct_name, struct_id)
|> if(
do: del?(rest, struct_name, struct_id, true),
else:
(
{_del_ids, list} = traverse_kv(objs, struct_name, struct_id)
objs -- list != []
)
)
del?(rest, struct_name, struct_id, should?)
end
def del?([{_k, _v} | rest], struct_name, struct_id, should?) do
del?(rest, struct_name, struct_id, should?)
end
def objs_del?(objs, struct_name, struct_id) do
objs
|> Enum.map(fn obj -> obj_del?(obj, struct_name, struct_id) end)
|> Enum.any?()
end
def obj_del?(%{__struct__: struct, id: id}, struct_name, struct_id)
when id == struct_id and struct == struct_name,
do: true
def obj_del?(%_{}, _struct_name, _struct_id), do: false
###
def found?(obj, conds, found \\ nil)
def found?(%_{} = _obj, [] = _conds, nil = _found), do: false
def found?(%_{} = obj, [_ | _] = conds, nil = _found) do
Enum.map(conds, fn {k, expected} ->
Map.fetch(obj, k)
|> case do
{:ok, v} -> expected == v
_ -> false
end
end)
|> Enum.all?()
end
def found?(%_{} = _obj, _conds, _found), do: false
def make_left(list, left, found \\ nil)
def make_left(list, _left, nil = _found), do: list
def make_left(_list, left, found), do: [found | Enum.reverse(left)]
def make_right(list, idx, len) when len != idx, do: Enum.take(list, idx - len)
def make_right(_list, _idx, _len), do: []
end