defmodule NLdoc.Conversion.Writer.Html.State do @moduledoc """ This module defines a struct that captures the state of the converter during a HTML conversion. """ @type html_tag_or_text :: Floki.html_tag() | Floki.html_text() use NLdoc.Util.State schema do field :ordered_footnote_ids, %{String.t() => number()} field :existing_footnote_ids, [String.t()] field :assets, [NLdoc.Spec.Asset.t()] end @doc """ Get current footnote number based on footnotes in state. ## Examples iex> %NLdoc.Conversion.Writer.Html.State{ordered_footnote_ids: %{ "abc" => 1, "xyz" => 2 }} ...> |> NLdoc.Conversion.Writer.Html.State.current_footnote_number() 2 """ @spec current_footnote_number(t()) :: number() def current_footnote_number(%__MODULE__{ordered_footnote_ids: ids}), do: map_size(ids) @doc """ Upsert new footnote to state. ## Examples When reference was encountered that refers to a valid footnote. iex> %NLdoc.Conversion.Writer.Html.State{ ...> ordered_footnote_ids: %{ "abc" => 1 }, ...> existing_footnote_ids: ["abc", "xyz"]} ...> |> NLdoc.Conversion.Writer.Html.State.upsert_footnote_id("xyz") {2, %NLdoc.Conversion.Writer.Html.State{ ordered_footnote_ids: %{"abc" => 1, "xyz" => 2}, existing_footnote_ids: ["abc", "xyz"]}} When dead reference was encountered. iex> %NLdoc.Conversion.Writer.Html.State{ ...> ordered_footnote_ids: %{ "abc" => 1 }, ...> existing_footnote_ids: ["abc", "xyz"]} ...> |> NLdoc.Conversion.Writer.Html.State.upsert_footnote_id("def") {nil, %NLdoc.Conversion.Writer.Html.State{ ordered_footnote_ids: %{"abc" => 1}, existing_footnote_ids: ["abc", "xyz"]}} When footnote is referenced that was referenced before iex> %NLdoc.Conversion.Writer.Html.State{ ...> ordered_footnote_ids: %{ "abc" => 1, "xyz" => 2 }, ...> existing_footnote_ids: ["abc", "xyz"]} ...> |> NLdoc.Conversion.Writer.Html.State.upsert_footnote_id("xyz") {2, %NLdoc.Conversion.Writer.Html.State{ ordered_footnote_ids: %{"abc" => 1, "xyz" => 2}, existing_footnote_ids: ["abc", "xyz"]}} """ @spec upsert_footnote_id(t(), String.t()) :: {number() | nil, t()} def upsert_footnote_id(state = %__MODULE__{}, id) do cond do # Footnote referenced to does not exist not Enum.member?(state.existing_footnote_ids, id) -> {nil, state} # Footnote is referenced to previously, so already has a number. Map.has_key?(state.ordered_footnote_ids, id) -> {Map.get(state.ordered_footnote_ids, id), state} true -> number = current_footnote_number(state) + 1 {number, %{state | ordered_footnote_ids: Map.put(state.ordered_footnote_ids, id, number)}} end end @doc """ Get asset by id from state. ## Examples iex> alias NLdoc.Conversion.Writer.Html.State iex> state = %State{assets: [%NLdoc.Spec.Asset{id: "123"}]} iex> state |> State.find_asset("123") %NLdoc.Spec.Asset{id: "123"} iex> state |> State.find_asset("456") nil """ @spec find_asset(state :: t(), id :: String.t()) :: NLdoc.Spec.Asset.t() | nil def find_asset(%__MODULE__{assets: assets}, id) do assets |> Enum.find(&(&1.id == id)) end end