Current section
Files
Jump to
Current section
Files
lib/fountainedge.ex
defmodule Fountainedge do
@moduledoc """
Documentation for Fountainedge.
"""
alias __MODULE__, as: Workflow
alias Fountainedge.{Schema, Edge, State, Node, Token, OutEdge}
@enforce_keys [:schema, :states]
defstruct schema: %Schema{nodes: [], edges: []}, states: []
def transition %Workflow{} = workflow, %Edge{} = edge do
%Workflow{workflow | states: transition(workflow.states, workflow.schema, edge)}
end
defp transition(states, %Schema{} = schema, %Edge{} = edge) do
edge = Edge.find(schema.edges, edge)
node = Node.find(schema.nodes, edge.next)
state = current_state states, edge
next_state = %State{state | id: edge.next}
states = Enum.reject(states, fn s -> s == state end) ++ [next_state]
case node.type do
:fork -> fork states, schema, node, next_state
:join -> join states, schema, node
_ -> states
end
end
defp current_state states, %Edge{} = edge do
Enum.find(states, fn state ->
state.id == edge.id and Enum.find state.tokens, fn token -> token.token == edge.next end
end) || Enum.find states, fn state -> state.id == edge.id end
end
defp fork states, %Schema{} = schema, %Node{} = node, %State{} = next_state do
edges = Enum.filter schema.edges, fn e -> e.id == node.id end
forked_states = Enum.reduce edges, [], fn edge, acc ->
token = %Token{id: edge.id, token: edge.next}
tokens = next_state.tokens ++ [token]
[%State{next_state | tokens: tokens} | acc]
end
Enum.reject(states, fn s -> s.id == next_state.id end) ++ forked_states
|> fork_transition(schema, edges)
end
defp fork_transition(states, %Schema{} = schema, [edge | edges]) do
states
|> transition(schema, edge)
|> fork_transition(schema, edges)
end
defp fork_transition(states, %Schema{} = _schema, []), do: states
defp join states, %Schema{} = schema, %Node{} = node do
origin_node = Enum.find schema.nodes, fn n -> n.join == node.id end
branches = Enum.count schema.edges, fn e -> e.id == origin_node.id end
arrivals = Enum.filter states, fn s ->
s.id == node.id and Enum.any? s.tokens, fn t -> t.id == origin_node.id end
end
if branches == Enum.count arrivals do
join_states(states, node, origin_node, arrivals)
|> transition(schema, Edge.find(schema.edges, node.id))
else
states
end
end
defp join_states states, %Node{} = node, %Node{} = origin_node, arrivals do
tokens = Enum.uniq join_tokens [], origin_node, arrivals
(states -- arrivals) ++ [%State{id: node.id, tokens: tokens}]
end
defp join_tokens tokens, %Node{} = origin_node, [state | arrivals] do
tokens ++ Enum.reject(state.tokens, fn t -> t.id == origin_node.id end)
|> join_tokens(origin_node, arrivals)
end
defp join_tokens(tokens, %Node{} = _origin_node, []), do: tokens
def out_edges(%Workflow{} = workflow) do
gather_out_edges_state(workflow, [], workflow.states)
|> Enum.uniq()
end
defp gather_out_edges_state(%Workflow{} = workflow, out_edges, [state | states]) do
out_edges = out_edges(workflow, state) ++ out_edges
gather_out_edges_state(workflow, out_edges, states)
end
defp gather_out_edges_state(%Workflow{} = _workflow, out_edges, []), do: out_edges
def out_edges(%Workflow{} = workflow, %State{} = state) do
edges = Enum.filter(workflow.schema.edges, fn edge -> edge.id == state.id end)
gather_out_edges(workflow, [], edges)
end
defp gather_out_edges(%Workflow{} = workflow, out_edges, [edge | edges]) do
node = Node.find(workflow.schema.nodes, edge.id)
disabled = case node.type do
:join -> true
_ -> false
end
out_edge = %OutEdge{edge: edge, disabled: disabled}
gather_out_edges(workflow, [out_edge] ++ out_edges, edges)
end
defp gather_out_edges(%Workflow{} = _workflow, out_edges, []), do: out_edges
end