defmodule Manganese.EntityKit.Structs.EntitiesCollection do alias Manganese.EntityKit.Structs @type t :: %Structs.EntitiesCollection{ entities: %{ optional(atom) => %{ optional(integer) => term }} } defstruct [ entities: %{} ] # Utilities @doc """ Create an empty entities collection. """ @spec new :: t def new, do: %Structs.EntitiesCollection{} @doc """ Retrieve an entity from the collection. If no entity of the given type and ID pair exist, `nil` is returned instead. """ @spec get_entity(t, atom, pos_integer) :: term | nil def get_entity(entities_collection, entity_type, id) do case Map.get entities_collection.entities, entity_type do %{ ^id => entity } -> entity _ -> nil end end @doc """ Add an entity to the collection. If the entity of the given type and ID pair already exist, it will be overwritten with the new value. """ @spec put_entity(t, atom, term) :: t def put_entity(entities_collection, entity_type, entity) do %Structs.EntitiesCollection{ entities: entities_collection.entities |> Map.put( entity_type, (Map.get(entities_collection.entities, entity_type) || %{}) |> Map.put(entity.id, entity) ) } end @doc """ Add multiple entities of the same type to the collection. If an entity of the given type and ID pair already exist, it will be overwritten with the new value. """ @spec put_entities(t, atom, [ term ]) :: t def put_entities(entities_collection, entity_type, entities) do entities |> Enum.reduce(entities_collection, fn entity, entities_collection -> entities_collection |> put_entity(entity_type, entity) end) end @doc """ Combine two collections into one. If an entity of the same type and ID pair is present in both collections, the value in `entities_collection2` will take precedence. """ @spec merge(t | nil, t | nil) :: t def merge(entities_collection1, nil), do: entities_collection1 def merge(nil, entities_collection2), do: entities_collection2 def merge(entities_collection1, entities_collection2) do entities1 = (entities_collection1 || Structs.EntitiesCollection.new).entities entities2 = (entities_collection2 || Structs.EntitiesCollection.new).entities %Structs.EntitiesCollection{ entities: Map.merge(entities1, entities2, fn _, entities_of_type1, entities_of_type2 -> Map.merge(entities_of_type1 || %{}, entities_of_type2 || %{}, fn _, _entity1, entity2 -> entity2 end) end) } end end