defmodule Wongi.Engine.Entity do
@moduledoc """
An iterator-like object that represents a set of facts about a single subject.
"""
alias Wongi.Engine.Rete
defstruct [:rete, :subject]
def new(rete, subject), do: %__MODULE__{rete: rete, subject: subject}
def on(entity, rete), do: %__MODULE__{entity | rete: rete}
def over(entity, subject), do: %__MODULE__{entity | subject: subject}
def fetch(%__MODULE__{rete: nil}, _),
do: raise("Entity not bound to an engine instance; use Entity.on/2")
def fetch(%__MODULE__{subject: nil}, _),
do: raise("Entity not bound to a subject; use Entity.over/2")
def fetch(%__MODULE__{rete: rete, subject: subject}, predicate) do
case Rete.select(rete, subject, predicate, :_) |> Enum.to_list() do
[wme | _] -> {:ok, wme.object}
[] -> :error
end
end
defimpl Enumerable do
def reduce(%@for{rete: rete, subject: subject}, acc, fun) do
Rete.select(rete, subject, :_, :_)
|> Enum.map(&{&1.predicate, &1.object})
|> Enumerable.reduce(acc, fun)
end
def member?(%@for{rete: rete, subject: subject}, {predicate, object}) do
{:ok, Rete.select(rete, subject, predicate, object) |> MapSet.size() > 0}
end
def member?(%@for{rete: rete, subject: subject}, predicate) do
{:ok, Rete.select(rete, subject, predicate, :_) |> MapSet.size() > 0}
end
def count(%@for{rete: rete, subject: subject}) do
{:ok, Rete.select(rete, subject, :_, :_) |> MapSet.size()}
end
def slice(_) do
{:error, __MODULE__}
end
end
end