From 648402a6101d2f6cc3e5595a7c49cd694cc106cb Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 27 Jul 2025 11:08:40 -0500 Subject: [PATCH] Fix build errors and add complete dialyzer specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove erlc_paths and erlc_include_path from mix.exs (leftover from Gleam) - Add comprehensive dialyzer specs to Encoding module - Add missing dialyzer specs to EncodingUtils module - Ensure all public and private functions have proper type specifications This fixes the Docker build error about undefined erlc_paths function and ensures full dialyzer compliance. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/aprsme/encoding.ex | 26 ++++++++++++++++++++++++++ lib/aprsme/encoding_utils.ex | 2 ++ mix.exs | 2 -- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/aprsme/encoding.ex b/lib/aprsme/encoding.ex index 15671cb..230a734 100644 --- a/lib/aprsme/encoding.ex +++ b/lib/aprsme/encoding.ex @@ -9,17 +9,20 @@ defmodule Aprsme.Encoding do Sanitizes a binary to ensure it can be safely JSON encoded. Handles latin1 conversion and removes control characters. """ + @spec sanitize_string(binary()) :: binary() def sanitize_string(input) when is_binary(input) do input |> try_utf8_conversion() |> clean_control_characters() end + @spec sanitize_string(any()) :: binary() def sanitize_string(_), do: "" @doc """ Type-safe float conversion with validation """ + @spec to_float_safe(binary()) :: {:ok, float()} | nil def to_float_safe(value) when is_binary(value) do sanitized = value |> String.trim() @@ -33,20 +36,24 @@ defmodule Aprsme.Encoding do end end + @spec to_float_safe(any()) :: nil def to_float_safe(_), do: nil @doc """ Convert binary to hex string """ + @spec to_hex(binary()) :: binary() def to_hex(input) when is_binary(input) do Base.encode16(input) end + @spec to_hex(any()) :: binary() def to_hex(_), do: "" @doc """ Check if a value looks like it has weather data """ + @spec has_weather_data(any(), any(), any(), any()) :: boolean() def has_weather_data(temperature, humidity, wind_speed, pressure) do not is_nil(temperature) or not is_nil(humidity) or @@ -57,6 +64,12 @@ defmodule Aprsme.Encoding do @doc """ Get encoding information about a binary """ + @spec encoding_info(binary()) :: %{ + valid_utf8: boolean(), + byte_count: non_neg_integer(), + char_count: non_neg_integer() | nil, + invalid_at: non_neg_integer() | nil + } def encoding_info(input) when is_binary(input) do byte_count = byte_size(input) @@ -79,6 +92,12 @@ defmodule Aprsme.Encoding do end end + @spec encoding_info(any()) :: %{ + valid_utf8: boolean(), + byte_count: non_neg_integer(), + char_count: nil, + invalid_at: nil + } def encoding_info(_) do %{ valid_utf8: false, @@ -90,6 +109,7 @@ defmodule Aprsme.Encoding do # Private functions + @spec try_utf8_conversion(binary()) :: binary() defp try_utf8_conversion(input) do if String.valid?(input) do input @@ -99,6 +119,7 @@ defmodule Aprsme.Encoding do end end + @spec latin1_to_utf8(binary()) :: binary() defp latin1_to_utf8(input) do try do input @@ -110,6 +131,7 @@ defmodule Aprsme.Encoding do end end + @spec latin1_char_to_utf8(0..255) :: binary() defp latin1_char_to_utf8(byte) when byte <= 127 do <> end @@ -120,6 +142,7 @@ defmodule Aprsme.Encoding do <<0xC0 + div(byte, 64), 0x80 + rem(byte, 64)>> end + @spec clean_control_characters(binary()) :: binary() defp clean_control_characters(s) do s |> String.graphemes() @@ -128,6 +151,7 @@ defmodule Aprsme.Encoding do |> String.trim() end + @spec valid_grapheme?(String.grapheme()) :: boolean() defp valid_grapheme?(grapheme) do case String.to_charlist(grapheme) do [cp] -> @@ -148,6 +172,7 @@ defmodule Aprsme.Encoding do end end + @spec find_invalid_byte_position(binary()) :: non_neg_integer() | nil defp find_invalid_byte_position(input) do input |> :binary.bin_to_list() @@ -159,6 +184,7 @@ defmodule Aprsme.Encoding do end) end + @spec valid_utf8_continuation?(binary(), non_neg_integer()) :: boolean() defp valid_utf8_continuation?(binary, pos) do try do <<_::binary-size(pos), char::utf8, _::binary>> = binary diff --git a/lib/aprsme/encoding_utils.ex b/lib/aprsme/encoding_utils.ex index 5907e0e..cb50378 100644 --- a/lib/aprsme/encoding_utils.ex +++ b/lib/aprsme/encoding_utils.ex @@ -76,6 +76,7 @@ defmodule Aprsme.EncodingUtils do def to_float(_), do: nil # Helper to check if a float is finite (not infinity or NaN) + @spec finite_float?(any()) :: boolean() defp finite_float?(float) when is_float(float) do # In Elixir, we can't have infinity or NaN in regular floats # This function is kept for defensive programming @@ -319,6 +320,7 @@ defmodule Aprsme.EncodingUtils do def sanitize_data_extended(data_extended), do: data_extended + @spec sanitize_map_value(any()) :: any() defp sanitize_map_value(val) when is_binary(val), do: sanitize_string(val) defp sanitize_map_value(val), do: val diff --git a/mix.exs b/mix.exs index 3256b62..89550ee 100644 --- a/mix.exs +++ b/mix.exs @@ -9,8 +9,6 @@ defmodule Aprsme.MixProject do archives: [], compilers: Mix.compilers(), elixirc_paths: elixirc_paths(Mix.env()), - erlc_paths: erlc_paths(Mix.env()), - erlc_include_path: "build/#{Mix.env()}/erlang/aprsme/include", start_permanent: Mix.env() == :prod, aliases: aliases(), deps: deps(),