src/rebar_calzone_prv.erl

% SPDX-FileCopyrightText: 2026 Dipl. Phys. Peer Stritzinger GmbH
%
% SPDX-License-Identifier: Apache-2.0

-module(rebar_calzone_prv).
-include_lib("rebar_calzone/src/rebar_calzone.hrl").

-export([init/1, do/1, format_error/1]).

%--- Includes ------------------------------------------------------------------



%--- Macros --------------------------------------------------------------------


%--- API -----------------------------------------------------------------------
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
    Provider = providers:create([
        {name, calzone},            % The 'user friendly' name of the task
        {module, ?MODULE},            % The module implementation of the task
        {bare, true},                 % The task can be run by the user, always true
        {deps, [compile]},                % The list of dependencies
        {example, "rebar3 calzone"}, % How to use the plugin
        {opts, options()},                   % list of options understood by the plugin
        {short_desc, "Calzone"},
        {desc, "Calzone"}
    ]),
    {ok, rebar_state:add_provider(State, Provider)}.


-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
    {Args, _ExtraArgs} = rebar_state:command_parsed_args(State),
    TargetOS = proplists:get_value(os, Args),
    TargetArch = proplists:get_value(arch, Args),
    ConfigOptions = rebar_state:get(State, rebar_calzone),
    Mode = get_option(mode, ConfigOptions, escript),
    BaseDir = rebar_dir:base_dir(State),
    CalzoneDir = filename:join(BaseDir, "calzone"),
    [AppInfo] = rebar_state:project_apps(State),
    Version = rebar_app_info:original_vsn(AppInfo),
    Opts = #{
             target_os => TargetOS,
             target_arch => TargetArch,
             mode => Mode,
             base_dir => BaseDir,
             calzone_dir => CalzoneDir,
             version => Version
    },
    copy_otp_lib(CalzoneDir),
    {Options2, State2} = release(Mode, Opts, State),
    ok = rebar_calzone_record:record(Options2),
    rebar_calzone_pack:bake(Options2),
    {ok, State2}.

-spec format_error(any()) ->  iolist().
format_error(Reason) ->
    io_lib:format("~p", [Reason]).

%--- Private -------------------------------------------------------------------
options() -> 
    {Arch, OS} = system_architecture(),
    [{os, undefined, "os",  {atom, OS}, "Specify the target OS"},
     {arch, undefined, "arch", {string, Arch},
     "Specify the target Arch"}].

system_architecture() ->
    [Arch, _, OS | _] = string:split(erlang:system_info(system_architecture), "-", all),
    {arch_to_atom(Arch), os_to_atom(OS)}.

os_to_atom("linux") -> linux;
os_to_atom(["darwin", _]) -> macos.

arch_to_atom("aarch64") -> aarch64;
arch_to_atom("x86_64") -> x86_64.

copy_otp_lib(CalzoneDir) ->
    RootDir = code:root_dir(),
    ErtsVersion = erlang:system_info(version),
    rebar_calzone_utils:copy_directory(RootDir, CalzoneDir, ["bin"]),
    file:delete(filename:join([CalzoneDir, "bin", "empd"])),
    BinEscript = filename:join([CalzoneDir, "bin", "escript"]),
    % Making bin/escript executable
    ok = file:change_mode(BinEscript, 8#755),
    rebar_calzone_utils:copy_directory(RootDir, CalzoneDir, ["erts-" ++ ErtsVersion]),
    rebar_calzone_utils:copy_directory(RootDir, CalzoneDir, ["lib"]).

release(escript, #{base_dir := BaseDir} = Opts, State) ->
    #{calzone_dir := CalzoneDir} = Opts,
    {ok, S} = rebar_prv_escriptize:do(State),
    EscriptName = to_list(rebar_state:get(State, escript_name)),
    BinDir = filename:join([BaseDir, "bin"]),
    EscriptPath = filename:join([BinDir, EscriptName]),
    EscriptDest = filename:join([CalzoneDir, "bin", EscriptName]),
    {ok, _} = file:copy(EscriptPath, EscriptDest),
    {Opts#{dir => BinDir,
           name => EscriptName}, S};
release(release, #{base_dir := BaseDir} = Opts, State) ->
    {ok, S} = rebar_prv_release:do(State),
    Relx = rebar_state:get(S, relx, []),
    {release, {RelName, _}, [_]} = lists:keyfind(release, 1, Relx),
    {Opts#{dir => filename:join([BaseDir, "rel", RelName]),
           name => atom_to_list(RelName)}, S}.

-spec get_option(atom(), proplists:proplist(), term()) -> term().
get_option(Option, Options, Default) ->
    case lists:keyfind(Option, 1, Options) of
        {Option, Value} -> Value;
        false -> Default
    end.

to_list(Name) when is_list(Name) -> Name;
to_list(Name) when is_atom(Name) -> atom_to_list(Name);
to_list(Name) when is_binary(Name) -> binary_to_list(Name).