-module(sandbox).
-export([eval_pure/2, eval_pure/3]).

%% Sandboxed evaluation of an Erlang fun.
%%
%% eval_pure/2(Fun, Arg)            -> {ok, Result} | {error, Reason}
%% eval_pure/3(Fun, Arg1, Arg2)     -> {ok, Result} | {error, Reason}
%%
%% The 3-arity variant matches the (Activity, State) -> NewState
%% shape of projection folds. The projection scheduler can wrap
%% every fold call in `sandbox:eval_pure(Fun, Act, State)` to
%% ensure a misbehaving fold body can't crash the projection
%% gen_server.
%%
%% v1 sandboxing is just the try/catch envelope: no gas budget,
%% no IO denial, no environment stripping. Real sandboxing lands
%% with SX-source eval (the fold body would then be an SX form
%% evaluated under the spec/harness platform). The API shape is
%% stable — callers don't need to change when that arrives.

%% Port note: this Erlang implementation catches by explicit
%% class names (throw, error, exit) rather than the open
%% `Class:Reason` pattern. The wrappers below enumerate the three.

eval_pure(Fun, Arg) ->
    try Fun(Arg) of
        Result -> {ok, Result}
    catch
        throw:Reason -> {error, {throw, Reason}};
        error:Reason -> {error, {error, Reason}};
        exit:Reason  -> {error, {exit, Reason}}
    end.

eval_pure(Fun, Arg1, Arg2) ->
    try Fun(Arg1, Arg2) of
        Result -> {ok, Result}
    catch
        throw:Reason -> {error, {throw, Reason}};
        error:Reason -> {error, {error, Reason}};
        exit:Reason  -> {error, {exit, Reason}}
    end.
