defmodule Bio.AcidHelper do
# Helpers for things that are similar between DNA and RNA modules
# Please don't use this unless you're me.
@moduledoc """
Internal helper module for dealing with amino acids
"""
@type alphabet :: String.t()
@type index :: integer()
@type character :: String.t()
@type mismatch :: [{:mismatch_alpha, character, index, alphabet}]
@doc false
def complement(alpha_mod, acid_mod, %_{alphabet: alpha} = sequence, opts) do
alphabet = get_alpha({alpha, Keyword.get(opts, :alphabet)}, alpha_mod)
comps =
sequence
|> Enum.with_index()
|> Enum.map(fn {base, index} ->
{apply(alpha_mod, :complement, [base, alphabet]), index}
end)
cond do
Enum.any?(comps, fn {{status, _}, _} -> status == :error end) ->
{:error,
Enum.reduce(comps, [], fn {{status, result}, index}, acc ->
case status do
:ok ->
acc
:error ->
{_, char, alpha} = result
List.insert_at(acc, -1, {:mismatch_alpha, char, index, alpha})
end
end)}
true ->
{:ok,
Enum.reduce(comps, "", fn {{_, result}, _}, acc ->
acc <> result
end)
|> then(&apply(acid_mod, :new, [&1, [{:alphabet, alphabet}]]))}
end
end
@doc false
def complement(alpha_mod, sequence, opts) when is_binary(sequence) do
alphabet = get_alpha({nil, Keyword.get(opts, :alphabet)}, alpha_mod)
comps =
sequence
|> String.graphemes()
|> Enum.with_index()
|> Enum.map(fn {base, index} ->
{apply(alpha_mod, :complement, [base, alphabet]), index}
end)
cond do
Enum.any?(comps, fn {{status, _}, _} -> status == :error end) ->
{:error,
Enum.reduce(comps, [], fn {{status, result}, index}, acc ->
case status do
:ok ->
acc
:error ->
{_, char, alpha} = result
List.insert_at(acc, -1, {:mismatch_alpha, char, index, alpha})
end
end)}
true ->
{:ok,
Enum.reduce(comps, "", fn {{_, result}, _}, acc ->
acc <> result
end)}
end
end
@doc false
defp get_alpha(opts, alpha_mod) do
case opts do
{nil, nil} -> apply(alpha_mod, :common, [])
{nil, opted} -> opted
{built, nil} -> built
{_built, opted} -> opted
end
end
end