This repository has been archived by the owner on Mar 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsymfony_port.erl
92 lines (84 loc) · 2.75 KB
/
symfony_port.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
% Module name
-module(symfony_port).
% Exported functions
-export([open/0, close/1, execute_command/3, execute_command/2]).
-export([init/1]). % internal function, should not be executed externally
% Configuration and interfaces
-include("symfony_port_config.hrl").
-include("symfony_port_interface.hrl").
% Init process with open port command
% @return process
open() ->
Process = spawn(?MODULE, init, [?PORT_CLI_COMMAND]),
execute_command(Process, ?INIT_COMMAND),
Process.
% Stop dispatching
% @param Process Process
close(Process) ->
execute_command(Process, ?EXIT_COMMAND),
Process ! stop.
% Execute command on port
% @param process Process
% @param string Name
% @param list Parameters
% @return string
% @exit port_not_responding
% @exit invalid_response
% @exit ErrorString
execute_command(Process, Name, Parameters) ->
PortMessage = io_lib:format("~s ~s~n", [Name, string:join(Parameters, " ")]),
Process ! #call{process=self(), message=PortMessage},
receive
{Process, Response} ->
IsOkResponse = string:str(Response, ?RESPONSE_PREFIX_OK) == 1,
IsErrorResponse = string:str(Response, ?RESPONSE_PREFIX_ERROR) == 1,
if
IsOkResponse ->
ResponseString = string:substr(Response, string:len(?RESPONSE_PREFIX_OK) + 1),
ResponseString;
IsErrorResponse ->
ErrorString = string:substr(Response, string:len(?RESPONSE_PREFIX_ERROR) + 1),
exit(ErrorString);
true ->
exit(invalid_response)
end
after ?PORT_TIMEOUT ->
exit(port_not_responding)
end.
% Shorter form of execute_command/3
% @param process Process
% @param string Name
% @param list Parameters
execute_command(Process, Name) ->
execute_command(Process, Name, []).
% Init port with specified CLI command
% @param string PortCommand
% @return process
init(PortCommand) ->
process_flag(trap_exit, true),
Port = open_port({spawn, PortCommand}, [stream, exit_status]),
dispatch(self(), Port).
% Dispatch requests and responses
% @param process Process
% @param process Port
% @exit normal
% @exit Reason
dispatch(Process, Port) ->
receive
#call{process=Caller, message=Message} ->
Port ! {self(), {command, Message}},
receive
{Port, {data, Data}} ->
Response = string:strip(Data, right, $\n),
Caller ! {Process, Response}
end,
dispatch(Process, Port);
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{'EXIT', Port, Reason} ->
exit(Reason)
end.