# credo:disable-for-this-file Credo.Check.Refactor.Nesting
defmodule Wongi.Engine.DSL.Has do
@moduledoc "Positive assertion."
alias Wongi.Engine.DSL
defstruct [:subject, :predicate, :object, :filters]
@type option() :: {:when, list(Wongi.Engine.Filter.t())}
@doc false
def new(subject, predicate, object, opts \\ []) do
%__MODULE__{
subject: subject,
predicate: predicate,
object: object,
filters: opts[:when]
}
end
defimpl DSL.Clause do
import Wongi.Engine.Compiler
alias Wongi.Engine.Beta.Join
alias Wongi.Engine.DSL.Var
alias Wongi.Engine.Rete
alias Wongi.Engine.WME
def compile(%@for{subject: s, predicate: p, object: o, filters: filters} = clause, context) do
{context, tests, assignments} =
[:subject, :predicate, :object]
|> Enum.reduce({context, %{}, %{}}, fn field, {context, tests, assignments} = acc ->
case Map.get(clause, field) do
%Var{name: var} ->
if MapSet.member?(context.variables, var) do
{context, Map.put(tests, field, var), assignments}
else
context = context |> declare_variable(var)
{context, tests, Map.put(assignments, field, var)}
end
_ ->
acc
end
end)
template = WME.template(s, p, o)
filters = extract_filters(filters)
node = Join.new(context.node_ref, template, tests, assignments, when: filters)
case find_existing(context, node) do
nil ->
rete = Rete.subscribe_to_alpha(context.rete, template, node)
context
|> put_rete(rete)
|> advance(node)
node ->
context
|> advance_existing(node)
end
end
defp extract_filters(nil), do: nil
defp extract_filters(%Wongi.Engine.DSL.Filter{filter: filter}), do: filter
defp extract_filters(filters) when is_list(filters), do: Enum.map(filters, &extract_filters/1)
end
end