Skip to main content

src/oaisp_ffi.erl

-module(oaisp_ffi).
-export([run/1, read_file/1, write_file/2, rename/2, delete/1]).

%% Run a shell command, capturing stdout (binary) and the exit status. stderr
%% is not redirected, so the child's progress output stays on the parent's
%% stderr and never mixes into the captured stdout. Returns {Status, Output}.
run(Command) ->
    Port = open_port(
        {spawn, binary_to_list(Command)},
        [stream, binary, exit_status, hide]
    ),
    collect(Port, <<>>).

collect(Port, Acc) ->
    receive
        {Port, {data, Data}} ->
            collect(Port, <<Acc/binary, Data/binary>>);
        {Port, {exit_status, Status}} ->
            {Status, Acc}
    end.

%% File access over the OTP `file` module. Each returns a value shaped like a
%% Gleam Result: {ok, _} | {error, ReasonBinary}.
read_file(Path) ->
    case file:read_file(Path) of
        {ok, Bytes} -> {ok, Bytes};
        {error, Reason} -> {error, reason(Reason)}
    end.

write_file(Path, Data) ->
    case file:write_file(Path, Data) of
        ok -> {ok, nil};
        {error, Reason} -> {error, reason(Reason)}
    end.

rename(Source, Destination) ->
    case file:rename(Source, Destination) of
        ok -> {ok, nil};
        {error, Reason} -> {error, reason(Reason)}
    end.

delete(Path) ->
    case file:delete(Path) of
        ok -> {ok, nil};
        {error, Reason} -> {error, reason(Reason)}
    end.

reason(Reason) when is_atom(Reason) -> atom_to_binary(Reason, utf8);
reason(Reason) -> list_to_binary(io_lib:format("~p", [Reason])).