Current section
Files
Jump to
Current section
Files
lib/execs.ex
defmodule Execs do
@moduledoc """
Execs provides the data abstraction layer for an Entity-Component System. It
uses a Mnesia client by default, with disc_copy tables. There is currently
no other client though the framework is in place for one should the need
arise.
All clients must implement the Execs.DbClient.Client behavior.
"""
use Execs.Utils
@opaque id :: integer()
@type component :: atom()
@type component_list :: [component]
@type component_match :: %{id: id, components: %{component => boolean()}}
@type component_match_list :: [component_match]
@type entity :: %{id: id, components: %{component => kv_pairs}}
@type entity_list :: [entity]
@type key :: any()
@type key_match :: %{id: id, components: %{component => %{key => boolean()}}}
@type key_match_list :: [key_match]
@type kv_pairs :: map()
@type id_list :: [id]
@type id_match :: %{id: id, result: boolean()}
@type id_match_list :: [id_match]
@type fun_list :: [fun()]
@type key_list :: [key]
@type list_keys :: %{id: id, components: %{component => [key]}}
@type list_keys_list :: [list_keys]
@type list_components :: %{id: id, components: maybe_component_list}
@type list_components_list :: [list_components]
@type maybe_component_list :: component | component_list
@type maybe_component_match_list :: component_match | component_match_list
@type maybe_entity_list :: entity | entity_list
@type maybe_fun_list :: fun | fun_list
@type maybe_id_list :: id | id_list
@type maybe_id_match_list :: id_match | id_match_list
@type maybe_key_list :: key | key_list
@type maybe_key_match_list :: key_match | key_match_list
@type maybe_list_components_list :: list_components | list_components_list
@type maybe_list_keys_list :: list_keys | list_keys_list
#
# Mix tasks
#
@doc false
def create_schema do
client().create_schema()
end
@doc false
def delete_schema do
client().delete_schema()
end
@doc false
def create_tables do
client().create_tables()
end
@doc false
def drop_tables do
client().drop_tables()
end
#
# Application API
#
@doc """
All data manipulation functions expect to be performed in the context of a
transaction. This ensures all systems can run concurrently while safely
accessing data.
"""
@spec transaction(fun()) :: any()
def transaction(block), do: client().transaction(block)
@doc """
Create a single entity and return the id.
"""
@spec create() :: id
def create, do: hd(create(1))
@doc """
Create the specified number of entities and return their ids.
"""
@spec create(integer()) :: id_list
def create(n), do: client().create(n)
@doc """
Delete a set of entities and all their data.
"""
@spec delete(maybe_id_list) :: maybe_entity_list
def delete(ids) when is_list(ids), do: client().delete(ids)
def delete(id), do: hd(delete([id]))
@doc """
Delete a set of components and all their data from a set of entities.
"""
@spec delete(maybe_id_list, maybe_component_list) :: maybe_entity_list
def delete(ids, components) when is_list(ids) do
client().delete(ids, enforce_list(components))
end
def delete(id, components) do
hd(delete([id], components))
end
@doc """
Delete a set of keys from a set of components which belong to a set of entities.
"""
@spec delete(maybe_id_list, maybe_component_list, maybe_key_list) :: maybe_entity_list
def delete(ids, components, keys) when is_list(ids) do
client().delete(ids, enforce_list(components), enforce_list(keys))
end
def delete(id, components, keys) do
hd(delete([id], components, keys))
end
@doc """
List the entities which have a set of components.
"""
@spec find_with_all(maybe_component_list) :: id_list
def find_with_all(components) do
client().find_with_all(enforce_list(components))
end
@doc """
List the entities which have a set of keys.
"""
@spec find_with_all(maybe_component_list, maybe_key_list) :: id_list
def find_with_all(components, keys) do
client().find_with_all(enforce_list(components), enforce_list(keys))
end
@doc """
List the entities which have a set of keys. The value associated
with the each key is passed to each of the comparison functions. If all functions return
true then there is a match.
"""
@spec find_with_all(maybe_component_list, maybe_key_list, maybe_fun_list) :: id_list
def find_with_all(components, keys, functions) do
client().find_with_all(enforce_list(components),
enforce_list(keys),
enforce_list(functions))
end
@doc """
List the entities which have at least one of a set of components.
"""
@spec find_with_any(maybe_component_list) :: id_list
def find_with_any(components) do
client().find_with_any(enforce_list(components))
end
@doc """
List the entities which have at least one of a set of keys.
"""
@spec find_with_any(maybe_component_list, maybe_key_list) :: id_list
def find_with_any(components, keys) do
client().find_with_any(enforce_list(components), enforce_list(keys))
end
@doc """
List the entities which have at least one of a set of keys. The value associated
with the each key is passed to each of the comparison functions. If all functions return
true then there is a match.
"""
@spec find_with_any(maybe_component_list, maybe_key_list, maybe_fun_list) :: id_list
def find_with_any(components, keys, functions) do
client().find_with_any(enforce_list(components), enforce_list(keys), enforce_list(functions))
end
@doc """
Check to see if a set of entities has a set of components.
"""
@spec has_all(maybe_id_list, maybe_component_list) :: maybe_id_match_list
def has_all(ids, components) when is_list(ids) do
client().has_all(ids, enforce_list(components))
end
def has_all(id, components) do
has_all([id], components)
|> hd()
|> Map.get(:result)
end
@doc """
Check to see if a set of entities has set of keys.
"""
@spec has_all(maybe_id_list, maybe_component_list, maybe_key_list) :: maybe_id_match_list
def has_all(ids, components, keys) when is_list(ids) do
client().has_all(ids, enforce_list(components), enforce_list(keys))
end
def has_all(id, components, keys) do
has_all([id], components, keys)
|> hd()
|> Map.get(:result)
end
@doc """
Check to see if a set of entities has set of keys. The value associated with the each key
is passed to each of the comparison functions. If all functions return true then there is
a match.
"""
@spec has_all(maybe_id_list, maybe_component_list, maybe_key_list, maybe_fun_list) :: maybe_id_match_list
def has_all(ids, components, keys, functions) when is_list(ids) do
client().has_all(ids, enforce_list(components), enforce_list(keys), enforce_list(functions))
end
def has_all(id, components, keys, functions) do
has_all([id], components, keys, functions)
|> hd()
|> Map.get(:result)
end
@doc """
Check to see if a set of entities has at least one of a set of components.
"""
@spec has_any(maybe_id_list, maybe_component_list) :: maybe_id_match_list
def has_any(ids, components) when is_list(ids) do
client().has_any(ids, enforce_list(components))
end
def has_any(id, components) do
has_any([id], components)
|> hd()
|> Map.get(:result)
end
@doc """
Check to see if a set of entities has at least one of a set of keys.
"""
@spec has_any(maybe_id_list, maybe_component_list, maybe_key_list) :: maybe_id_match_list
def has_any(ids, components, keys) when is_list(ids) do
client().has_any(ids, enforce_list(components), enforce_list(keys))
end
def has_any(id, components, keys) do
has_any([id], components, keys)
|> hd()
|> Map.get(:result)
end
@doc """
Check to see if a set of entities has at least one of a set of keys. The value associated
with the each key is passed to each of the comparison functions. If all functions return
true then there is a match.
"""
@spec has_any(maybe_id_list, maybe_component_list, maybe_key_list, maybe_fun_list) :: maybe_id_match_list
def has_any(ids, components, keys, functions) when is_list(ids) do
client().has_any(ids, enforce_list(components), enforce_list(keys), enforce_list(functions))
end
def has_any(id, components, keys, functions) do
has_any([id], components, keys, functions)
|> hd()
|> Map.get(:result)
end
@doc """
Check to see which of a set of components a set of entities has.
"""
@spec has_which(maybe_id_list, maybe_component_list) :: maybe_component_match_list
def has_which(ids, components) when is_list(ids) do
client().has_which(ids, enforce_list(components))
end
def has_which(id, components), do: hd(has_which([id], components))
@doc """
Check to see which of a set of keys a set of entities has.
"""
@spec has_which(maybe_id_list, maybe_component_list, maybe_key_list) :: maybe_component_match_list
def has_which(ids, components, keys) when is_list(ids) do
client().has_which(ids, enforce_list(components), enforce_list(keys))
end
def has_which(id, components, keys), do: hd(has_which([id], components, keys))
@doc """
Check to see which of a set of keys a set of entities has. The value associated
with the each key is passed to each of the comparison functions. If all functions return
true then there is a match.
"""
@spec has_which(maybe_id_list, maybe_component_list, maybe_key_list, maybe_fun_list) :: maybe_component_match_list
def has_which(ids, components, keys, functions) when is_list(ids) do
client().has_which(ids, enforce_list(components), enforce_list(keys), enforce_list(functions))
end
def has_which(id, components, keys, functions) do
hd(has_which([id], components, keys, functions))
end
@doc """
List the components of a set of entities.
"""
@spec list(maybe_id_list) :: maybe_list_components_list
def list(ids) when is_list(ids), do: client().list(ids)
def list(id), do: hd(list([id]))
@doc """
List the keys belonging to a set of components of a set of entities.
"""
@spec list(maybe_id_list, maybe_component_list) :: maybe_list_keys_list
def list(ids, components) when is_list(ids) do
client().list(ids, enforce_list(components))
end
def list(id, components), do: hd(list([id], components))
@doc """
Read a set of entities.
"""
@spec read(maybe_id_list) :: maybe_entity_list
def read(ids) when is_list(ids), do: client().read(ids)
def read(id), do: hd(read([id]))
@doc """
Read a set of components belonging to a set of entities.
"""
@spec read(maybe_id_list, maybe_component_list) :: maybe_entity_list
def read(ids, components) when is_list(ids) do
client().read(ids, enforce_list(components))
end
def read(id, components), do: hd(read([id], components))
@doc """
Read a set of keys belonging to a set of entities. Providing anything other
than a single id, component, and key will return a map otherwise a single
value is returned.
"""
@spec read(maybe_id_list, maybe_component_list, maybe_key_list) :: maybe_entity_list | any()
def read(ids, components, keys) when is_list(ids) do
client().read(ids, enforce_list(components), enforce_list(keys))
end
def read(id, component, key)
when is_list(component) == false
and is_list(key) == false do
entity = hd(read([id], component, key))
entity[:components][component][key]
end
def read(id, components, keys), do: hd(read([id], components, keys))
@doc """
Write a value to any combination of keys, components, and entities.
"""
@spec write(maybe_id_list, maybe_component_list, maybe_key_list, any()) :: maybe_id_list
def write(ids, components, keys, value) when is_list(ids) do
client().write(ids, enforce_list(components), enforce_list(keys), value)
end
def write(id, components, keys, value) do
hd(write([id], components, keys, value))
end
#
# Private functions
#
@spec client() :: any()
defp client, do: cfg(:db_client)
@spec enforce_list(list(any())) :: list(any())
defp enforce_list(value) when is_list(value), do: value
@spec enforce_list(any()) :: list(any())
defp enforce_list(value), do: [value]
end