src/libsodium.erl

%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
%% vim: ts=4 sw=4 ft=erlang noet
%%%-------------------------------------------------------------------
%%% @author Andrew Bennett <potatosaladx@gmail.com>
%%% @copyright 2015-2022, Andrew Bennett
%%% @doc
%%%
%%% @end
%%% Created :  28 Dec 2015 by Andrew Bennett <potatosaladx@gmail.com>
%%%-------------------------------------------------------------------
-module(libsodium).

-include("libsodium.hrl").

%% API
-export([start/0]).
-export([call/2]).
-export([call/3]).
-export([call/4]).
-export([open/0]).
-export([close/1]).

-define(MAYBE_START_LIBSODIUM(F), try
	F
catch
	_:_ ->
		_ = libsodium:start(),
		F
end).

%%%===================================================================
%%% API
%%%===================================================================

start() ->
	application:ensure_all_started(?MODULE).

call(Namespace, Function)
		when is_atom(Namespace)
		andalso is_atom(Function) ->
	?MAYBE_START_LIBSODIUM(call(Namespace, Function, {})).

call(Namespace, Function, Arguments)
		when is_atom(Namespace)
		andalso is_atom(Function)
		andalso is_tuple(Arguments) ->
	?MAYBE_START_LIBSODIUM(call(erlang:whereis(?LIBSODIUM_DRIVER_ATOM), Namespace, Function, Arguments)).

call(Port, Namespace, Function, Arguments)
		when is_port(Port)
		andalso is_atom(Namespace)
		andalso is_atom(Function)
		andalso is_tuple(Arguments) ->
	driver_call(Port, ?LIBSODIUM_ASYNC_CALL, Namespace, Function, Arguments).

open() ->
	erlang:open_port({spawn_driver, ?LIBSODIUM_DRIVER_NAME}, [binary]).

close(P) ->
	try
		true = erlang:port_close(P),
		receive
			{'EXIT', P, _} ->
				ok
		after
			0 ->
				ok
		end
	catch
		_:_ ->
			erlang:error(badarg)
	end.

%%%-------------------------------------------------------------------
%%% Internal functions
%%%-------------------------------------------------------------------

%% @private
driver_call(Port, Command, Namespace, Function, Arguments) ->
	Tag = erlang:make_ref(),
	case erlang:port_call(Port, Command, {Tag, Namespace, Function, Arguments}) of
		Tag ->
			receive
				{Tag, Reply} ->
					Reply
			end;
		{Tag, Error} ->
			Error
	end.