src/safe_runner.erl

-module(safe_runner).

%% Handles execution of the installed SAFE binary.
%% Path resolution delegates to safe_rel to avoid duplicating download-directory knowledge.

-export([fingerprint/2, analyse/2, version/1]).

-spec fingerprint(string(), {config_path, string()} | {config_json, string()}) ->
    ok | {error, {fingerprint, non_neg_integer()}}.
fingerprint(ProjectDir, ConfigSpec) ->
    {SafeExe, Dir} = safe_exe_and_dir(ProjectDir),
    Args = ["fingerprint"] ++ config_spec_to_args(ConfigSpec) ++ ["--project-root", ProjectDir],
    debug_print_command(SafeExe, Args),
    case safe_os:shell_passthrough(SafeExe, Args, [{cd, Dir}]) of
        0 -> ok;
        ExitCode -> {error, {fingerprint, ExitCode}}
    end.

-spec analyse(string(), {config_path, string()} | {config_json, string()}) ->
    ok | {error, {analyse, non_neg_integer()}}.
analyse(ProjectDir, ConfigSpec) ->
    {SafeExe, Dir} = safe_exe_and_dir(ProjectDir),
    Args = ["analyse"] ++ config_spec_to_args(ConfigSpec) ++ ["--project-root", ProjectDir],
    debug_print_command(SafeExe, Args),
    case safe_os:shell_passthrough(SafeExe, Args, [{cd, Dir}]) of
        0 -> ok;
        ExitCode -> {error, {analyse, ExitCode}}
    end.

-spec version(string()) -> ok | {error, {version, non_neg_integer()}}.
version(ProjectDir) ->
    {SafeExe, Dir} = safe_exe_and_dir(ProjectDir),
    Args = ["version"],
    debug_print_command(SafeExe, Args),
    case safe_os:shell_passthrough(SafeExe, Args, [{cd, Dir}]) of
        0 -> ok;
        ExitCode -> {error, {version, ExitCode}}
    end.

%%====================================================================
%% Internal functions
%%====================================================================

config_spec_to_args({config_path, Path}) -> ["--config-path", Path];
config_spec_to_args({config_json, Json}) -> ["--config-json", Json].

-spec safe_exe_and_dir(string()) -> {string(), string()}.
safe_exe_and_dir(ProjectDir) ->
    SafeExe = ensure_string(safe_rel:get_safe_binary_path(ProjectDir)),
    Dir = filename:dirname(SafeExe),
    {SafeExe, Dir}.

debug_print_command(SafeExe, Args) ->
    safe_print:debug(os:getenv("DEBUG") =:= "1", "SAFE command: ~s ~s", [
        SafeExe, string:join(Args, " ")
    ]).

-spec ensure_string(file:filename_all()) -> string().
ensure_string(Path) when is_binary(Path) -> binary_to_list(Path);
ensure_string(Path) when is_list(Path) -> Path.