-module(gleamdoc_ffi).
-export([
export_package_interface/2,
interface_fingerprint/1,
stdout_supports_color/0,
stderr_supports_color/0,
exit_status/1
]).
stdout_supports_color() ->
supports_color(standard_io).
stderr_supports_color() ->
supports_color(standard_error).
supports_color(Device) ->
os:getenv("NO_COLOR") =:= false andalso
os:getenv("TERM") =/= "dumb" andalso
case io:columns(Device) of
{ok, _} -> true;
_ -> false
end.
export_package_interface(Directory, Output) ->
case os:find_executable("gleam") of
false -> {error, <<"Could not find the Gleam executable on PATH.">>};
Gleam ->
run(
Gleam,
[
"export",
"package-interface",
"--out",
binary_to_list(Output)
],
binary_to_list(Directory)
)
end.
run(Executable, Arguments, Directory) ->
Port = open_port(
{spawn_executable, Executable},
[
binary,
exit_status,
stderr_to_stdout,
use_stdio,
{args, Arguments},
{cd, Directory}
]
),
collect(Port, []).
collect(Port, Output) ->
receive
{Port, {data, Data}} -> collect(Port, [Data | Output]);
{Port, {exit_status, 0}} -> {ok, nil};
{Port, {exit_status, _}} ->
{error, iolist_to_binary(lists:reverse(Output))}
end.
interface_fingerprint(Paths) ->
try
Hash = lists:foldl(
fun(Path, Context) ->
Content = case file:read_file(Path) of
{ok, Data} -> Data;
{error, enoent} -> <<"<missing>">>;
{error, Reason} -> error({read_failed, Path, Reason})
end,
crypto:hash_update(Context, [Path, 0, Content, 0])
end,
crypto:hash_init(sha256),
lists:sort(Paths)
),
{ok, binary:encode_hex(crypto:hash_final(Hash), lowercase)}
catch
error:{read_failed, Path, Reason} ->
{error, iolist_to_binary(io_lib:format(
"Could not fingerprint ~ts: ~tp", [Path, Reason]
))}
end.
exit_status(Status) ->
erlang:halt(Status).