Current section
Files
Jump to
Current section
Files
lib/iconvex/specs/codec_support.ex
defmodule Iconvex.Specs.CodecSupport do
@moduledoc false
def decode(id, input), do: Iconvex.TableCodec.decode(entry(id), input)
def decode_discard(id, input), do: Iconvex.TableCodec.decode_discard(entry(id), input)
def decode_chunk(id, input, final?),
do: Iconvex.TableCodec.decode_chunk(entry(id), input, final?)
def decode_error_consumption(kind, sequence),
do: Iconvex.TableCodec.decode_error_consumption(kind, sequence)
def decode_provider(id, input), do: Iconvex.TableCodec.decode(provider_entry(id), input)
def decode_discard_provider(id, input),
do: Iconvex.TableCodec.decode_discard(provider_entry(id), input)
def decode_to_utf8(id, input) do
case Iconvex.TableCodec.decode_to_utf8(entry(id), input) do
:miss ->
case decode(id, input) do
{:ok, codepoints} -> {:ok, List.to_string(codepoints)}
error -> error
end
result ->
result
end
end
def decode_to_utf8_provider(id, input) do
case Iconvex.TableCodec.decode_to_utf8(provider_entry(id), input) do
:miss ->
case decode_provider(id, input) do
{:ok, codepoints} -> {:ok, List.to_string(codepoints)}
error -> error
end
result ->
result
end
end
def encode(id, codepoints), do: Iconvex.TableCodec.encode(entry(id), codepoints)
def encode_provider(id, codepoints),
do: Iconvex.TableCodec.encode(provider_entry(id), codepoints)
def encode_discard(id, codepoints),
do: Iconvex.TableCodec.encode_discard(entry(id), codepoints)
def encode_substitute(id, codepoints, replacer),
do: Iconvex.TableCodec.encode_substitute(entry(id), codepoints, replacer)
def encode_chunk(id, codepoints, final?, policy),
do: Iconvex.TableCodec.encode_chunk(entry(id), codepoints, final?, policy)
def encode_discard_provider(id, codepoints),
do: Iconvex.TableCodec.encode_discard(provider_entry(id), codepoints)
def encode_substitute_provider(id, codepoints, replacer),
do: Iconvex.TableCodec.encode_substitute(provider_entry(id), codepoints, replacer)
def encode_substitute_each(codepoints, encoder, replacer)
when is_list(codepoints) and is_function(encoder, 1) and is_function(replacer, 1),
do: encode_substitute_each_loop(codepoints, encoder, replacer, [])
def encode_substitute_transform(codepoints, encoder, replacer)
when is_list(codepoints) and is_function(encoder, 1) and is_function(replacer, 1) do
case substitute_codepoints(codepoints, encoder, replacer, []) do
{:ok, transformed} -> encoder.(:lists.reverse(transformed))
error -> error
end
end
def encode_from_utf8(id, input) do
case Iconvex.TableCodec.encode_from_utf8(entry(id), input) do
:miss -> encode_from_utf8_fallback(id, input, &encode/2)
result -> result
end
end
def encode_from_utf8_provider(id, input) do
case Iconvex.TableCodec.encode_from_utf8(provider_entry(id), input) do
:miss -> encode_from_utf8_fallback(id, input, &encode_provider/2)
result -> result
end
end
def encode_utf8(input, encoder) when is_binary(input) and is_function(encoder, 1) do
case :unicode.characters_to_list(input, :utf8) do
codepoints when is_list(codepoints) ->
encoder.(codepoints)
{:incomplete, converted, rest} ->
first_error_or_utf8_error(converted, rest, input, :incomplete_sequence, encoder)
{:error, converted, rest} ->
first_error_or_utf8_error(converted, rest, input, :invalid_sequence, encoder)
end
end
def encode_utf8(input, valid_encoder, prefix_encoder)
when is_binary(input) and is_function(valid_encoder, 1) and
is_function(prefix_encoder, 1) do
if String.valid?(input) do
valid_encoder.(input)
else
case :unicode.characters_to_list(input, :utf8) do
{:incomplete, converted, rest} ->
first_error_or_utf8_error(
converted,
rest,
input,
:incomplete_sequence,
prefix_encoder
)
{:error, converted, rest} ->
first_error_or_utf8_error(
converted,
rest,
input,
:invalid_sequence,
prefix_encoder
)
end
end
end
def malformed_utf8(input, offset \\ 0) when is_binary(input) do
case :unicode.characters_to_list(input, :utf8) do
{:incomplete, _converted, rest} ->
{:decode_error, :incomplete_sequence, offset + byte_size(input) - byte_size(rest), rest}
{:error, _converted, rest} ->
{:decode_error, :invalid_sequence, offset + byte_size(input) - byte_size(rest), rest}
end
end
defp encode_from_utf8_fallback(id, input, encoder) do
encode_utf8(input, fn codepoints ->
case encoder.(id, codepoints) do
{:error, :unrepresentable_character, codepoint} ->
{:encode_error, :unrepresentable_character, codepoint}
result ->
result
end
end)
end
defp first_error_or_utf8_error(converted, rest, input, kind, encoder) do
case encoder.(converted) do
{:ok, output} when is_binary(output) ->
{:decode_error, kind, byte_size(input) - byte_size(rest), rest}
earlier_error ->
earlier_error
end
end
defp encode_substitute_each_loop([], _encoder, _replacer, acc),
do: {:ok, acc |> :lists.reverse() |> IO.iodata_to_binary()}
defp encode_substitute_each_loop([codepoint | rest], encoder, replacer, acc) do
case encoder.([codepoint]) do
{:ok, bytes} ->
encode_substitute_each_loop(rest, encoder, replacer, [bytes | acc])
{:error, :unrepresentable_character, ^codepoint} ->
case encoder.(replacer.(codepoint)) do
{:ok, replacement} ->
encode_substitute_each_loop(rest, encoder, replacer, [replacement | acc])
error ->
error
end
error ->
error
end
end
defp substitute_codepoints([], _encoder, _replacer, acc), do: {:ok, acc}
defp substitute_codepoints([codepoint | rest], encoder, replacer, acc) do
case encoder.([codepoint]) do
{:ok, _bytes} ->
substitute_codepoints(rest, encoder, replacer, [codepoint | acc])
{:error, :unrepresentable_character, ^codepoint} ->
replacement = replacer.(codepoint)
case encoder.(replacement) do
{:ok, _bytes} ->
substitute_codepoints(rest, encoder, replacer, :lists.reverse(replacement, acc))
error ->
error
end
error ->
error
end
end
defp entry(id), do: %{id: id, table_app: :iconvex_specs}
defp provider_entry(id), do: %{id: id}
end