defmodule Sorcery.Storage.Adapters.Ecto.Parsing do
use Norm
alias Sorcery.Specs.Primative, as: T
alias Sorcery.Utils.Maps
# This is probably the entrypoint you are looking for
@contract get_ordered_inserts(T.src()) :: coll_of({T.tk(), T.id(), T.entity()})
def get_ordered_inserts(src) do
src = mv_changes_inserts(src)
li = list_inserts_deps(src)
ids = inserts_id_order(li)
Enum.map(ids, fn id ->
{tk, _, _} = Enum.find(li, fn {_, sorcery_id, _} -> sorcery_id == id end)
entity = Map.get(src.inserts[tk], id)
{tk, id, entity}
end)
end
# Moves entities from changes_db into :inserts.
@contract mv_changes_inserts(spec(is_struct(Sorcery.Src))) :: spec(is_struct(Sorcery.Src))
def mv_changes_inserts(src) do
Enum.reduce(src.changes_db, src, fn {tk, table}, src_acc ->
Enum.reduce(table, src_acc, fn
{"$sorcery:" <> _ = id, entity}, src_acc ->
src_acc
|> Maps.put_in_p([:inserts, tk, id], entity)
|> Maps.delete_in([:changes_db, tk, id])
{_id, _entity}, src_acc -> src_acc
end)
end)
end
@contract list_inserts_deps(spec(is_struct(Sorcery.Src))) :: coll_of({spec(is_atom), spec(is_binary), coll_of(spec(is_binary()))})
def list_inserts_deps(src) do
Enum.reduce(src.inserts, [], fn {tk, table}, src_acc ->
Enum.reduce(table, src_acc, fn {id, entity}, src_acc ->
deps = Enum.reduce(entity, [], fn
{_, "$sorcery:" <> _ = dep}, acc -> [dep | acc]
_, acc -> acc
end)
[{tk, id, deps} | src_acc]
end)
end)
end
@contract inserts_id_order(coll_of({spec(is_atom), spec(is_binary), coll_of(spec(is_binary()))})) :: coll_of(spec(is_binary()))
def inserts_id_order(curr) do
inserts_id_order(curr, [], [])
end
def inserts_id_order([], [], acc), do: Enum.reverse(acc)
def inserts_id_order([], pending, acc), do: inserts_id_order(pending, [], acc)
def inserts_id_order([hd | tl], pending, acc) do
case hd do
{_tk, id, []} -> inserts_id_order(tl, pending, [id | acc])
{tk, id, deps} ->
new_deps = Enum.filter(deps, fn k -> k not in acc end)
inserts_id_order(tl, [{tk, id, new_deps} | pending], acc)
end
end
end