defmodule GenAI.Graph.Link do
@vsn 1.0
@moduledoc """
Represent a link between two nodes in a graph.
"""
alias GenAI.Graph.NodeProtocol
alias GenAI.Records, as: R
alias GenAI.Types, as: T
require GenAI.Records.Link
require GenAI.Types.Graph
@type t :: %__MODULE__{
# Identifier
id: G.graph_id(),
handle: T.handle(),
# Name / Description
name: T.name(),
description: T.description(),
# Link Details
type: T.Graph.link_type(),
label: T.Graph.link_label(),
# @todo specifier like count, trait, direction, etc. o(5)-->1 etc. for uml.
# Link Endpoints
source: R.Link.connector(),
target: R.Link.connector(),
# Meta
meta: nil,
vsn: float()
}
defstruct [
# Identifier
id: nil,
handle: nil,
# Name / Description
name: nil,
description: nil,
# Link Details
type: nil,
label: nil,
# Link Endpoints
source: nil,
target: nil,
# Meta
meta: nil,
vsn: @vsn
]
@doc """
Create a new link.
# Examples
iex> node1_id = UUID.uuid5(:oid, "node-1")
iex> node2_id = UUID.uuid5(:oid, "node-2")
iex> l = GenAI.Graph.Link.new(node1_id, node2_id, name: "Hello")
%GenAI.Graph.Link{
handle: nil,
name: "Hello",
description: nil,
source: R.Link.connector(node: ^node1_id, socket: :default, external: false),
target: R.Link.connector(node: ^node2_id, socket: :default, external: false),
vsn: 1.0
} = l
iex> node1_id = UUID.uuid5(:oid, "node-1")
iex> node2_id = UUID.uuid5(:oid, "node-2")
iex> l = GenAI.Graph.Link.new(R.Link.connector(node: node1_id, socket: :default, external: false), node2_id, handle: :andy)
%GenAI.Graph.Link{
handle: :andy,
source: R.Link.connector(node: ^node1_id, socket: :default, external: false),
target: R.Link.connector(node: ^node2_id, socket: :default, external: false),
vsn: 1.0
} = l
iex> node1_id = UUID.uuid5(:oid, "node-1")
iex> l = GenAI.Graph.Link.new(R.Link.connector(node: node1_id, socket: :default, external: false), nil, description: "A Node")
%GenAI.Graph.Link{
description: "A Node",
source: R.Link.connector(node: ^node1_id, socket: :default, external: false),
target: R.Link.connector(node: nil, socket: :default, external: true),
vsn: 1.0
} = l
# from node struct (requires protocol impl)
"""
@spec new(term, term, term) :: __MODULE__.t()
def new(source, target, options \\ nil)
def new(source, target, options) do
id = options[:id] || UUID.uuid4()
source = to_connector(source)
target = to_connector(target)
type = if Keyword.has_key?(options || [], :type), do: options[:type], else: :link
%GenAI.Graph.Link{
id: id,
handle: options[:handle],
name: options[:name],
description: options[:description],
type: type,
label: options[:label],
source: source,
target: target,
vsn: @vsn
}
end
# =============================================================================
# Link Protocol
# =============================================================================
# -------------------------
# id/1
# -------------------------
@doc """
Obtain the id of a graph link.
# Examples
## when set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, handle: :foo, name: "A", description: "B")
...> GenAI.Graph.Link.id(l)
{:ok, l.id}
## when not set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, handle: :foo, name: "A", description: "B") |> put_in([Access.key(:id)], nil)
...> GenAI.Graph.Link.id(l)
{:error, {:id, :is_nil}}
"""
@spec id(graph_link :: T.Graph.graph_link()) :: T.result(T.Graph.graph_link_id(), T.details())
def id(graph_link)
def id(%__MODULE__{id: nil}), do: {:error, {:id, :is_nil}}
def id(%__MODULE__{id: id}), do: {:ok, id}
# -------------------------
# handle/1
# -------------------------
@doc """
Obtain the handle of a graph link.
# Examples
## When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, handle: :foo)
...> GenAI.Graph.Link.handle(l)
{:ok, :foo}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.handle(l)
{:error, {:handle, :is_nil}}
"""
@spec handle(graph_link :: T.Graph.graph_link()) :: T.result(T.handle(), T.details())
def handle(graph_link)
def handle(%__MODULE__{handle: nil}), do: {:error, {:handle, :is_nil}}
def handle(%__MODULE__{handle: handle}), do: {:ok, handle}
# -------------------------
# handle/2
# -------------------------
@doc """
Obtain the handle of a graph link, or return a default value if the handle is nil.
# Examples
## When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, handle: :foo)
...> GenAI.Graph.Link.handle(l, :default)
{:ok, :foo}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.handle(l, :default)
{:ok, :default}
"""
@spec handle(graph_link :: T.Graph.graph_link(), default :: T.handle()) ::
T.result(T.handle(), T.details())
def handle(graph_link, default)
def handle(%__MODULE__{handle: nil}, default), do: {:ok, default}
def handle(%__MODULE__{handle: handle}, _), do: {:ok, handle}
# -------------------------
# name/1
# -------------------------
@doc """
Obtain the name of a graph link.
# Examples
## When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, name: "A")
...> GenAI.Graph.Link.name(l)
{:ok, "A"}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.name(l)
{:error, {:name, :is_nil}}
"""
@spec name(graph_link :: T.Graph.graph_link()) :: T.result(T.name(), T.details())
def name(graph_link)
def name(%__MODULE__{name: nil}), do: {:error, {:name, :is_nil}}
def name(%__MODULE__{name: name}), do: {:ok, name}
# -------------------------
# name/2
# -------------------------
@doc """
Obtain the name of a graph link, or return a default value if the name is nil.
# Examples
## When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, name: "A")
...> GenAI.Graph.Link.name(l, "default")
{:ok, "A"}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.name(l, "default")
{:ok, "default"}
"""
@spec name(graph_link :: T.Graph.graph_link(), default :: T.name()) ::
T.result(T.name(), T.details())
def name(graph_link, default)
def name(%__MODULE__{name: nil}, default), do: {:ok, default}
def name(%__MODULE__{name: name}, _), do: {:ok, name}
# -------------------------
# description/1
# -------------------------
@doc """
Obtain the description of a graph link.
# Examples
## When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, description: "B")
...> GenAI.Graph.Link.description(l)
{:ok, "B"}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.description(l)
{:error, {:description, :is_nil}}
"""
@spec description(graph_link :: T.Graph.graph_link()) :: T.result(T.description(), T.details())
def description(graph_link)
def description(%__MODULE__{description: nil}), do: {:error, {:description, :is_nil}}
def description(%__MODULE__{description: description}), do: {:ok, description}
# -------------------------
# description/2
# -------------------------
@doc """
Obtain the description of a graph link, or return a default value if the description is nil.
# Examples
## When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, description: "B")
...> GenAI.Graph.Link.description(l, "default")
{:ok, "B"}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.description(l, "default")
{:ok, "default"}
"""
@spec description(graph_link :: T.Graph.graph_link(), default :: T.description()) ::
T.result(T.description(), T.details())
def description(graph_link, default)
def description(%__MODULE__{description: nil}, default), do: {:ok, default}
def description(%__MODULE__{description: description}, _), do: {:ok, description}
# -------------------------
# type/1
# -------------------------
@doc """
Obtain the type of a graph link.
## Examples
### When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, type: :link_type)
...> GenAI.Graph.Link.type(l)
{:ok, :link_type}
### When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, type: nil)
...> GenAI.Graph.Link.type(l)
{:error, {:type, :is_nil}}
"""
@spec type(__MODULE__.t()) :: T.result(T.Graph.link_type(), T.details())
def type(graph_link)
def type(%__MODULE__{type: nil}), do: {:error, {:type, :is_nil}}
def type(%__MODULE__{type: type}), do: {:ok, type}
# -------------------------
# type/2
# -------------------------
@doc """
Obtain the type of a graph link, or return a default value if the type is nil.
## Examples
### When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, type: :link_type)
...> GenAI.Graph.Link.type(l, :default)
{:ok, :link_type}
### When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, type: nil)
...> GenAI.Graph.Link.type(l, :default)
{:ok, :default}
"""
@spec type(__MODULE__.t(), default :: any) :: T.result(T.Graph.link_type(), T.details())
def type(graph_link, default)
def type(%__MODULE__{type: nil}, default), do: {:ok, default}
def type(%__MODULE__{type: type}, _), do: {:ok, type}
# -------------------------
# label/1
# -------------------------
@doc """
Obtain the label of a graph link.
## Examples
### When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, label: :some_label)
...> GenAI.Graph.Link.label(l)
{:ok, :some_label}
### When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.label(l)
{:error, {:label, :is_nil}}
"""
@spec label(__MODULE__.t()) :: T.result(T.Graph.link_label(), T.details())
def label(graph_link)
def label(%__MODULE__{label: nil}), do: {:error, {:label, :is_nil}}
def label(%__MODULE__{label: label}), do: {:ok, label}
# -------------------------
# label/2
# -------------------------
@doc """
Obtain the label of a graph link, or return a default value if the label is nil.
## Examples
### When Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id, label: :my_label)
...> GenAI.Graph.Link.label(l, :default)
{:ok, :my_label}
### When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> GenAI.Graph.Link.label(l, :default)
{:ok, :default}
"""
@spec label(__MODULE__.t(), default :: any) :: T.result(T.Graph.link_label(), T.details())
def label(graph_link, default)
def label(%__MODULE__{label: nil}, default), do: {:ok, default}
def label(%__MODULE__{label: label}, _), do: {:ok, label}
# -------------------------
# with_id/1
# -------------------------
@doc """
Ensure the graph link has an id, generating one if necessary.
# Examples
## When Already Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> {:ok, l2} = GenAI.Graph.Link.with_id(l)
...> %{was_nil: is_nil(l.id), is_nil: is_nil(l2.id), id_change: l.id != l2.id}
%{was_nil: false, is_nil: false, id_change: false}
## When Not Set
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id) |> put_in([Access.key(:id)], nil)
...> {:ok, l2} = GenAI.Graph.Link.with_id(l)
...> %{was_nil: is_nil(l.id), is_nil: is_nil(l2.id), id_change: l.id != l2.id}
%{was_nil: true, is_nil: false, id_change: true}
"""
@spec with_id(graph_link :: T.Graph.graph_link()) :: T.result(T.Graph.graph_link(), T.details())
def with_id(graph_link) do
graph_link
|> with_id!()
|> then(&{:ok, &1})
end
@spec with_id!(__MODULE__.t()) :: __MODULE__.t()
def with_id!(graph_link) do
cond do
graph_link.id == nil ->
put_in(graph_link, [Access.key(:id)], UUID.uuid4())
graph_link.id == :auto ->
put_in(graph_link, [Access.key(:id)], UUID.uuid4())
:else ->
graph_link
end
end
# -------------------------
# source_connector/1
# -------------------------
@doc """
Obtain the source connector of a graph link.
# Examples
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> {:ok, sut} = GenAI.Graph.Link.source_connector(l)
...> sut
R.Link.connector(external: false) = sut
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> l = GenAI.Graph.Link.new(nil, node1_id)
...> {:ok, sut} = GenAI.Graph.Link.source_connector(l)
...> sut
R.Link.connector(external: true) = sut
"""
@spec source_connector(graph_link :: T.Graph.graph_link()) ::
T.result(R.Link.connector(), T.details())
def source_connector(%__MODULE__{source: nil}), do: {:error, {:source, :is_nil}}
def source_connector(%__MODULE__{source: connector}), do: {:ok, connector}
# -------------------------
# target_connector/1
# -------------------------
@doc """
Obtain the target connector of a graph link.
# Examples
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> {:ok, sut} = GenAI.Graph.Link.target_connector(l)
...> sut
R.Link.connector(node: ^node2_id) = sut
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> l = GenAI.Graph.Link.new(node1_id, nil)
...> {:ok, sut} = GenAI.Graph.Link.target_connector(l)
...> sut
R.Link.connector(external: true) = sut
"""
@spec target_connector(graph_link :: T.Graph.graph_link()) ::
T.result(R.Link.connector(), T.details())
def target_connector(%__MODULE__{target: nil}), do: {:error, {:target, :is_nil}}
def target_connector(%__MODULE__{target: connector}), do: {:ok, connector}
# -------------------------
# putnew_target/2
# -------------------------
@doc """
Set the target connector of a graph link, if it is not already set.
# Examples
## When Not Set. By ID
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, nil)
...> |> GenAI.Graph.Link.putnew_target(node2_id)
%GenAI.Graph.Link{target: R.Link.connector(node: ^node2_id, external: false)} = l
## When Not Set. By Connector
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(node1_id, nil)
...> |> GenAI.Graph.Link.putnew_target(R.Link.connector(node: node2_id, socket: :foo, external: false))
%GenAI.Graph.Link{target: R.Link.connector(node: ^node2_id, external: false)} = l
## When Set. By ID
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> node3_id = UUID.uuid5(:oid, "node-3")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> |> GenAI.Graph.Link.putnew_target(node3_id)
%GenAI.Graph.Link{target: R.Link.connector(node: ^node2_id, external: false)} = l
## When Set. By Connector
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> node3_id = UUID.uuid5(:oid, "node-3")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> |> GenAI.Graph.Link.putnew_target(R.Link.connector(node: node3_id, socket: :foo, external: false))
%GenAI.Graph.Link{target: R.Link.connector(node: ^node2_id, external: false)} = l
"""
@spec putnew_target(graph_link :: T.Graph.graph_link(), target :: term) :: T.Graph.graph_link()
def putnew_target(
graph_link,
R.Link.connector(
node: connector_node,
socket: connector_socket,
external: connector_external
)
) do
x = graph_link.target || R.Link.connector(node: nil, socket: nil, external: false)
if is_nil(R.Link.connector(x, :node)) do
%__MODULE__{
graph_link
| target:
R.Link.connector(
x,
node: connector_node,
socket: connector_socket,
external: connector_external
)
}
else
graph_link
end
end
def putnew_target(graph_link, target) when T.Graph.is_node_id(target) do
x = graph_link.target || R.Link.connector(node: nil, socket: nil, external: nil)
if is_nil(R.Link.connector(x, :node)) do
%__MODULE__{
graph_link
| target:
R.Link.connector(
x,
node: R.Link.connector(x, :node) || target,
socket: R.Link.connector(x, :socket) || :default,
# wip
external: false
)
}
else
graph_link
end
end
def putnew_target(graph_link, target) when is_struct(target) do
{:ok, connector_id} = NodeProtocol.id(target)
x = graph_link.target || R.Link.connector(node: nil, socket: nil, external: false)
if is_nil(R.Link.connector(x, :node)) do
%__MODULE__{
graph_link
| target:
R.Link.connector(
x,
node: R.Link.connector(x, :node) || connector_id,
socket: R.Link.connector(x, :socket) || :default,
# wip
external: false
)
}
else
graph_link
end
end
# -------------------------
# putnew_source/2
# -------------------------
@doc """
Set the source connector of a graph link, if it is not already set.
# Examples
## When Not Set. By ID
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(nil, node2_id)
...> |> GenAI.Graph.Link.putnew_source(node1_id)
%GenAI.Graph.Link{source: R.Link.connector(node: ^node1_id, external: false)} = l
## When Not Set. By Connector
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> l = GenAI.Graph.Link.new(nil, node2_id)
...> |> GenAI.Graph.Link.putnew_source(R.Link.connector(node: node1_id, socket: :foo, external: false))
%GenAI.Graph.Link{source: R.Link.connector(node: ^node1_id, socket: :foo, external: false)} = l
## When Set. By ID
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> node3_id = UUID.uuid5(:oid, "node-3")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> |> GenAI.Graph.Link.putnew_source(node3_id)
%GenAI.Graph.Link{source: R.Link.connector(node: ^node1_id, external: false)} = l
## When Set. By Connector
iex> node1_id = UUID.uuid5(:oid, "node-1")
...> node2_id = UUID.uuid5(:oid, "node-2")
...> node3_id = UUID.uuid5(:oid, "node-3")
...> l = GenAI.Graph.Link.new(node1_id, node2_id)
...> |> GenAI.Graph.Link.putnew_source(R.Link.connector(node: node3_id, socket: :foo, external: false))
%GenAI.Graph.Link{source: R.Link.connector(node: ^node1_id, external: false)} = l
"""
@spec putnew_source(graph_link :: T.Graph.graph_link(), source :: term) :: T.Graph.graph_link()
def putnew_source(
graph_link,
R.Link.connector(
node: connector_node,
socket: connector_socket,
external: connector_external
)
) do
x = graph_link.source || R.Link.connector(node: nil, socket: nil, external: false)
if is_nil(R.Link.connector(x, :node)) do
%__MODULE__{
graph_link
| source:
R.Link.connector(
x,
node: connector_node,
socket: connector_socket,
external: connector_external
)
}
else
graph_link
end
end
def putnew_source(graph_link, source) when T.Graph.is_node_id(source) do
x = graph_link.source || R.Link.connector(node: nil, socket: nil, external: false)
if is_nil(R.Link.connector(x, :node)) do
%__MODULE__{
graph_link
| source:
R.Link.connector(
x,
node: R.Link.connector(x, :node) || source,
socket: R.Link.connector(x, :socket) || :default,
# wip
external: false
)
}
else
graph_link
end
end
def putnew_source(graph_link, source) when is_struct(source) do
{:ok, connector_id} = NodeProtocol.id(source)
x = graph_link.source || R.Link.connector(node: nil, socket: nil, external: false)
if is_nil(R.Link.connector(x, :node)) do
%__MODULE__{
graph_link
| source:
R.Link.connector(
x,
node: R.Link.connector(x, :node) || connector_id,
socket: R.Link.connector(x, :socket) || :default,
# wip
external: false
)
}
else
graph_link
end
end
# =============================================================================
# Internal
# =============================================================================
defp to_connector(value = R.Link.connector()), do: value
defp to_connector(nil), do: R.Link.connector(node: nil, socket: :default, external: true)
defp to_connector(value) when T.Graph.is_node_id(value),
do: R.Link.connector(node: value, socket: :default, external: false)
defp to_connector(value) do
{:ok, x} = NodeProtocol.id(value)
R.Link.connector(node: x, socket: :default, external: false)
end
end