-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.