         link_component_type: spec["type"]}
     end)
   end
+
+  @spec unused_component?(HELL.PK.t) :: :unused_component | :linked_component
+  defp unused_component?(component_id) do
+    MdlMoboSlot
+    |> where([s], s.link_component_id == ^component_id)
+    |> Repo.all()
+    |> Enum.empty?()
+    |> case do
+      true ->
+        :unused_component
+      false ->
+        :linked_component
+    end
+  end
+
+  @spec empty_slot?(HELL.PK.t) :: :empty_slot | :linked_slot | {:error, :notfound}
+  defp empty_slot?(slot_id) do
+    case find(slot_id) do
+      {:ok, slot} ->
+        if slot.link_component_id == nil do
+          :empty_slot
+        else
+          :linked_slot
+        end
+      error ->
+        error
+    end
+  end
 end

  end

  @spec parse_motherboard_spec(%{String.t => any}) ::
    [%{slot_internal_id: non_neg_integer, link_component_type: String.t}]
  def parse_motherboard_spec(component_spec) do
    Enum.map(component_spec.spec["slots"], fn {id, spec} ->
      %{

    end
  end

  @spec link(slot_id :: HELL.PK.t, component :: HELL.PK.t | nil) ::
    {:ok, MdlMoboSlot.t}
    | {:error, :linked_component | :linked_slot | :notfound | Ecto.Changeset.t}
  def link(slot_id, link_component_id) do
    with \
      :unused_component <- unused_component?(link_component_id),
      :empty_slot <- empty_slot?(slot_id)
    do
      update(slot_id, %{link_component_id: link_component_id})
    else
      {:error, msg} ->
        {:error, msg}
      msg ->
        {:error, msg}
    end
  end

  @spec unlink(HELL.PK.t) ::
    {:ok, MdlMoboSlot.t}
    | {:error, :empty_slot | :notfound | Ecto.Changeset.t}
  def unlink(slot_id) do
    case empty_slot?(slot_id) do
      :linked_slot ->
        update(slot_id, %{link_component_id: nil})
      :empty_slot ->
        {:error, :empty_slot}
      error ->
        error
    end
  end

  @spec delete(HELL.PK.t) :: no_return
