Current section

Files

Jump to
graphex lib graphex.ex
Raw

lib/graphex.ex

defmodule Graphex do
alias Graphex.Dag
alias Graphex.Execute.ParallelExecute, as: P
@typedoc """
A `component` is a keyword list with a `name`, `fun` and optionally `deps`.
"""
@type component :: [Keyword]
@typedoc """
Identifier for a vertex
"""
@type vertex :: :digraph.vertex
@typedoc """
Identifier for an edge
"""
@type edge :: :digraph.edge
@typedoc """
`results` is a collection of results from executing a graph
"""
@type results :: %{}
@typedoc """
`result_key` is a key used to retrieve an item from `results`.
"""
@type result_key :: atom
@typedoc """
A graph
"""
@type graph :: Graphex.Dag.dag
@doc """
Generate and execute a graph and return the results for the given `result_key`.
The graph will be automatically deleted after execution.
"""
@spec exec_graph(result_key, [component]) :: results
def exec_graph(key, components) do
results = exec_graph(components)
results[key]
end
@doc """
Generate and execute a graph and return the results for all vertices.
The graph will be automatically deleted after execution.
"""
@spec exec_graph([component]) :: results
def exec_graph(components) do
dag = graph(components)
results = P.exec_bf(dag)
Dag.delete(dag)
results
end
@doc """
Generate a graph. Returns a new graph or `{:error, message}` if there is a
problem constructing the graph.
You will need to manually delete the graph after using it.
"""
@spec graph([component]) :: graph
def graph(components) do
# construct dag and add vertices
{graph, verticies} = add_vertices(Dag.new, components)
# Then add edges since we don't want the order of component statements to matter
# without doing this, if the dep doesn't exist yet, the edge is silently not added
add_edges(graph, components, Enum.into(verticies, HashSet.new()))
|> graph_return()
end
@spec add_vertices(graph, [component]) :: {graph, [vertex]}
defp add_vertices(graph, components) do
Enum.reduce(components, {graph, []}, fn component, {acc, verticies} ->
Dag.add_vertex(acc, component)
{acc, [component[:name]|verticies]}
end)
end
@spec add_edges(graph, [component], HashSet) :: {graph, [vertex]}
defp add_edges(graph, components, name_set) do
Enum.reduce(components, {graph, []}, fn component, {acc, missing_deps} ->
deps = Dict.get(component, :deps, [])
more_missing = Set.difference(Enum.into(deps, HashSet.new), name_set)
Dag.add_edges(acc, component[:name], deps)
{acc, Set.to_list(more_missing) ++ missing_deps}
end)
end
@spec graph_return({graph, [Atom]}) :: graph | {:error, binary}
defp graph_return({graph, []}), do: graph
defp graph_return({_graph, missing_deps}) do
{:error, "Dependencies do not exist: #{Enum.sort(missing_deps) |> Enum.join(", ")}"}
end
end