-module(porttest). -behavior(gen_server). -export([ hello/0, factorial/1 ]). -export([ start_link/0, stop/0 ]). -export([ init/1, handle_call/3, handle_cast/2, terminate/2 ]). -record(state, {port}). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). stop() -> gen_server:call(?MODULE, stop). hello() -> gen_server:call(?MODULE, hello). factorial(N) when is_integer(N), N >= 0 -> case gen_server:call(?MODULE, {factorial, N}) of {ok, Result} when is_integer(Result) -> Result; {error, _} = Error -> Error end; factorial(_) -> {error, badarg}. %% helper parse_result("error:" ++ Rest) -> {error, list_to_binary(Rest)}; parse_result(Str) -> case string:to_integer(Str) of {Int, []} -> {ok, Int}; _ -> {error, badarg} end. init([]) -> Path = "./porttest", %% compiled zig program Port = open_port({spawn, Path}, [binary, exit_status]), {ok, #state{port = Port}}. handle_call(hello, _From, State) -> Port = State#state.port, Cmd = <<"hello">>, port_command(Port, <<(byte_size(Cmd)):16, Cmd/binary>>), receive {Port, {data, <<_Len:16, Resp/binary>>}} -> {reply, binary_to_list(Resp), State} after 5000 -> {reply, {error, timeout}, State} end; handle_call({factorial, N}, _From, State) -> Port = State#state.port, BinCmd = <<"factorial ", (integer_to_binary(N))/binary>>, port_command(Port, <<(byte_size(BinCmd)):16, BinCmd/binary>>), receive {Port, {data, <<_Len:16, Resp/binary>>}} -> Reply = parse_result( binary_to_list(Resp) ), {reply, Reply, State} after 5000 -> {reply, {error, timeout}, State} end; handle_call(stop, _From, State) -> Port = State#state.port, port_close(Port), {stop, normal, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. terminate(_Reason, _State) -> ok.