lib/graph/serializer.ex

defmodule Graph.Serializer do
  @moduledoc """
  This module defines the Serializer behavior for graphs.
  """
  @callback serialize(Graph.t()) :: {:ok, binary} | {:error, term}

  defmacro __using__(_) do
    quote do
      @behaviour Graph.Serializer
    end
  end

  def get_vertex_label(%Graph{vertex_labels: vl}, id, v) do
    case Map.get(vl, id) do
      [] -> encode_label(v)
      label -> encode_label(label)
    end
  end

  def encode_label([h | _] = label) when length(label) == 1, do: encode_label(h)
  def encode_label(label) when is_binary(label), do: quoted(label)
  def encode_label(label) when is_integer(label), do: Integer.to_string(label)
  def encode_label(label) when is_float(label), do: Float.to_string(label)
  def encode_label(label) when is_atom(label), do: quoted(Atom.to_string(label))
  def encode_label(label), do: quoted("#{inspect(label)}")

  def quoted(str) do
    <<?", escape_quotes(str)::binary, ?">>
  end

  def escape_quotes(str) do
    escape_quotes(str, "")
  end

  def escape_quotes(<<>>, acc), do: acc

  def escape_quotes(<<?\\, ?\", rest::binary>>, acc) do
    escape_quotes(rest, <<acc::binary, ?\\, ?\">>)
  end

  def escape_quotes(<<?\", rest::binary>>, acc) do
    escape_quotes(rest, <<acc::binary, ?\\, ?\">>)
  end

  def escape_quotes(<<c::utf8, rest::binary>>, acc) do
    escape_quotes(rest, <<acc::binary, c::utf8>>)
  end

  def indent(tabs), do: String.duplicate(" ", tabs * 4)
end