-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} -> <<"">>; {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).