aprs.me/build/dev/erlang/aprsme/_gleam_artefacts/aprsme@encoding.erl
Graham McIntire 00a8f996f4
Add Gleam language integration to Elixir project
- Rename Gleam module from encoding_simple to encoding
- Move Gleam files from src/aprs/ to src/aprsme/ to match project namespace
- Create custom Mix task for Gleam compilation (lib/mix/tasks/gleam_compile.ex)
- Update EncodingUtils to wrap Gleam implementation instead of pure Elixir
- Add Gleam dependencies to mix.exs and configure build paths
- Update Mix aliases to include Gleam compilation in test and compile tasks
- Add Gleam support to GitHub Actions CI workflow with caching
- Add GLEAM_INTEGRATION.md documentation
- All 357 tests passing with Gleam integration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-18 13:58:21 -05:00

275 lines
8.2 KiB
Erlang

-module(aprsme@encoding).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/aprsme/encoding.gleam").
-export([sanitize_string/1, to_float_safe/1, to_hex/1, has_weather_data/4, encoding_info/1]).
-export_type([encoding_info/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type encoding_info() :: {encoding_info,
boolean(),
integer(),
gleam@option:option(integer()),
gleam@option:option(integer())}.
-file("src/aprsme/encoding.gleam", 29).
-spec do_bit_array_to_list(bitstring(), list(integer())) -> list(integer()).
do_bit_array_to_list(Input, Acc) ->
case erlang:byte_size(Input) of
0 ->
Acc;
_ ->
case gleam_stdlib:bit_array_slice(Input, 0, 1) of
{ok, <<Byte>>} ->
case gleam_stdlib:bit_array_slice(
Input,
1,
erlang:byte_size(Input) - 1
) of
{ok, Rest} ->
do_bit_array_to_list(Rest, [Byte | Acc]);
{error, _} ->
[Byte | Acc]
end;
_ ->
Acc
end
end.
-file("src/aprsme/encoding.gleam", 24).
?DOC(" Convert BitArray to list of bytes\n").
-spec bit_array_to_list(bitstring()) -> list(integer()).
bit_array_to_list(Input) ->
_pipe = do_bit_array_to_list(Input, []),
lists:reverse(_pipe).
-file("src/aprsme/encoding.gleam", 48).
?DOC(" Convert latin1 encoded bytes to UTF-8 string\n").
-spec latin1_to_utf8_string(bitstring()) -> binary().
latin1_to_utf8_string(Input) ->
_pipe = Input,
_pipe@1 = bit_array_to_list(_pipe),
_pipe@2 = gleam@list:filter_map(_pipe@1, fun(Byte) -> case Byte of
B when B =< 127 ->
case gleam@bit_array:to_string(<<B>>) of
{ok, S} ->
{ok, S};
{error, _} ->
{error, nil}
end;
B@1 ->
Byte1 = 192 + (B@1 div 64),
Byte2 = 128 + (B@1 rem 64),
case gleam@bit_array:to_string(<<Byte1, Byte2>>) of
{ok, S@1} ->
{ok, S@1};
{error, _} ->
{error, nil}
end
end end),
gleam@string:join(_pipe@2, <<""/utf8>>).
-file("src/aprsme/encoding.gleam", 77).
?DOC(" Remove control characters from a string\n").
-spec clean_control_characters(binary()) -> binary().
clean_control_characters(S) ->
_pipe = S,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(Grapheme) -> case gleam@string:to_utf_codepoints(Grapheme) of
[Codepoint] ->
Cp = gleam_stdlib:identity(Codepoint),
case Cp of
9 ->
true;
10 ->
true;
13 ->
true;
C when (C >= 0) andalso (C =< 31) ->
false;
127 ->
false;
C@1 when (C@1 >= 128) andalso (C@1 =< 159) ->
false;
_ ->
true
end;
_ ->
true
end end
),
_pipe@3 = gleam@string:join(_pipe@2, <<""/utf8>>),
gleam@string:trim(_pipe@3).
-file("src/aprsme/encoding.gleam", 10).
?DOC(
" Sanitizes a binary to ensure it can be safely JSON encoded\n"
" Handles latin1 conversion and removes control characters\n"
).
-spec sanitize_string(bitstring()) -> binary().
sanitize_string(Input) ->
case gleam@bit_array:to_string(Input) of
{ok, S} ->
clean_control_characters(S);
{error, _} ->
_pipe = Input,
_pipe@1 = latin1_to_utf8_string(_pipe),
clean_control_characters(_pipe@1)
end.
-file("src/aprsme/encoding.gleam", 104).
?DOC(" Type-safe float conversion with validation\n").
-spec to_float_safe(binary()) -> gleam@option:option(float()).
to_float_safe(Value) ->
Sanitized = begin
_pipe = Value,
_pipe@1 = gleam@string:trim(_pipe),
gleam@string:slice(_pipe@1, 0, 30)
end,
case gleam_stdlib:parse_float(Sanitized) of
{ok, F} ->
case F of
X when (X > -9.0e15) andalso (X < 9.0e15) ->
{some, F};
_ ->
none
end;
{error, _} ->
none
end.
-file("src/aprsme/encoding.gleam", 129).
-spec do_to_hex(bitstring(), list(binary())) -> list(binary()).
do_to_hex(Input, Acc) ->
case gleam_stdlib:bit_array_slice(Input, 0, 1) of
{ok, <<Byte>>} ->
Rest = case gleam_stdlib:bit_array_slice(
Input,
1,
erlang:byte_size(Input) - 1
) of
{ok, R} ->
R;
{error, _} ->
<<>>
end,
Hex = gleam@int:to_base16(Byte),
Padded = case string:length(Hex) of
1 ->
<<"0"/utf8, Hex/binary>>;
_ ->
Hex
end,
do_to_hex(Rest, [Padded | Acc]);
_ ->
Acc
end.
-file("src/aprsme/encoding.gleam", 122).
?DOC(" Convert binary to hex string\n").
-spec to_hex(bitstring()) -> binary().
to_hex(Input) ->
_pipe = do_to_hex(Input, []),
_pipe@1 = lists:reverse(_pipe),
_pipe@2 = gleam@string:join(_pipe@1, <<""/utf8>>),
string:uppercase(_pipe@2).
-file("src/aprsme/encoding.gleam", 148).
?DOC(" Check if a value looks like it has weather data\n").
-spec has_weather_data(
gleam@option:option(float()),
gleam@option:option(float()),
gleam@option:option(float()),
gleam@option:option(float())
) -> boolean().
has_weather_data(Temperature, Humidity, Wind_speed, Pressure) ->
case {Temperature, Humidity, Wind_speed, Pressure} of
{{some, _}, _, _, _} ->
true;
{_, {some, _}, _, _} ->
true;
{_, _, {some, _}, _} ->
true;
{_, _, _, {some, _}} ->
true;
{_, _, _, _} ->
false
end.
-file("src/aprsme/encoding.gleam", 192).
-spec find_invalid_byte_position(bitstring(), integer()) -> gleam@option:option(integer()).
find_invalid_byte_position(Input, Pos) ->
case erlang:byte_size(Input) of
0 ->
none;
_ ->
case gleam_stdlib:bit_array_slice(Input, 0, 1) of
{ok, Byte_slice} ->
case gleam@bit_array:to_string(Byte_slice) of
{ok, _} ->
case gleam_stdlib:bit_array_slice(
Input,
1,
erlang:byte_size(Input) - 1
) of
{ok, Rest} ->
find_invalid_byte_position(Rest, Pos + 1);
{error, _} ->
{some, Pos}
end;
{error, _} ->
{some, Pos}
end;
{error, _} ->
{some, Pos}
end
end.
-file("src/aprsme/encoding.gleam", 170).
?DOC(" Get encoding information about a binary\n").
-spec encoding_info(bitstring()) -> encoding_info().
encoding_info(Input) ->
Byte_count = erlang:byte_size(Input),
case gleam@bit_array:to_string(Input) of
{ok, S} ->
{encoding_info, true, Byte_count, {some, string:length(S)}, none};
{error, _} ->
Invalid_pos = find_invalid_byte_position(Input, 0),
{encoding_info, false, Byte_count, none, Invalid_pos}
end.