-module(tty_ffi).
-export([stdin_is_tty/0, stdout_is_tty/0, stderr_is_tty/0, get_env/1]).
%% Exported for contract testing only; not used from Gleam.
-export([env_value_to_result/1]).
stdin_is_tty() -> tty_option_enabled(standard_io, stdin).
stdout_is_tty() -> tty_option_enabled(standard_io, stdout).
stderr_is_tty() -> tty_option_enabled(standard_io, stderr).
%% io:getopts/1 is available on OTP 26+. On OTP 26+ the standard_io device
%% reports per-stream TTY status via the stdin/stdout/stderr keys; the
%% standard_error device does NOT carry those keys, so all three queries
%% target standard_io. Any failure to read options (missing function on older
%% OTP, badarg, a closed/terminated I/O server at shutdown, etc.) falls back to
%% false so Gleam's Bool result is total. The catch-all is deliberate: the
%% public contract is "returns false when TTY status cannot be determined", so
%% no exception class should ever escape this function.
tty_option_enabled(Dev, Key) ->
try io:getopts(Dev) of
Opts when is_list(Opts) ->
proplists:get_value(Key, Opts, false) =:= true;
_ ->
false
catch
_:_ ->
false
end.
%% Returns {ok, Value} | {error, nil} to match Gleam's Result(String, Nil).
get_env(Name) when is_binary(Name) ->
case unicode:characters_to_list(Name) of
EnvName when is_list(EnvName) ->
case os:getenv(EnvName) of
false -> {error, nil};
Value -> env_value_to_result(Value)
end;
{error, _, _} ->
{error, nil}
end.
env_value_to_result(Value) ->
case unicode:characters_to_binary(Value) of
EnvValue when is_binary(EnvValue) -> {ok, EnvValue};
{error, _, _} -> {error, nil}
end.