1 |
|
%% @doc Interface for actions that handler modules might request `mongoose_c2s' to act upon. |
2 |
|
%% Note that some of the keys take a list of elements, which, in the case of a hook with many |
3 |
|
%% handlers, means that at the end of the queue `mongoose_c2s' will get a list of requests |
4 |
|
%% by different modules. These will be acted upon in the same order they were inserted in the |
5 |
|
%% hook list, according to their priority. |
6 |
|
%% The following keys are defined: |
7 |
|
%% - `state_mod': key-value pairs of gen_mod module names and their desired state. |
8 |
|
%% - `actions': a list of valid `gen_statem:action()' to request the `mongoose_c2s' engine. |
9 |
|
%% - `c2s_state': a new state is requested for the state machine. |
10 |
|
%% - `c2s_data': a new state data is requested for the state machine. |
11 |
|
%% - `stop': an action of type `{cast, {stop, Reason}}' is to be triggered. |
12 |
|
%% - `hard_stop': no other request is allowed, the state machine is immediatly triggered to stop. |
13 |
|
%% - `route': mongoose_acc elements to trigger the whole `handle_route' pipeline. |
14 |
|
%% - `flush': mongoose_acc elements to trigger the `handle_flush` pipeline. |
15 |
|
%% - `socket_send': xml elements to send on the socket to the user. |
16 |
|
%% - `socket_send_first': xml elements to send on the socket to the user, but appended first. |
17 |
|
-module(mongoose_c2s_acc). |
18 |
|
|
19 |
|
-export([new/0, new/1, |
20 |
|
get_statem_result/1, |
21 |
|
from_mongoose_acc/2, |
22 |
|
to_acc/3, to_acc_many/2 |
23 |
|
]). |
24 |
|
|
25 |
|
-type key() :: state_mod |
26 |
|
| actions |
27 |
|
| c2s_state |
28 |
|
| c2s_data |
29 |
|
| stop |
30 |
|
| hard_stop |
31 |
|
| route |
32 |
|
| flush |
33 |
|
| socket_send |
34 |
|
| socket_send_first. |
35 |
|
-type pairs() :: [pair()]. |
36 |
|
-type pair() :: {state_mod, {module(), term()}} |
37 |
|
| {actions, gen_statem:action()} |
38 |
|
| {c2s_state, mongoose_c2s:state()} |
39 |
|
| {c2s_data, mongoose_c2s:data()} |
40 |
|
| {stop, term() | {shutdown, atom()}} |
41 |
|
| {hard_stop, term() | {shutdown, atom()}} |
42 |
|
| {route, mongoose_acc:t()} |
43 |
|
| {flush, mongoose_acc:t()} |
44 |
|
| {socket_send, exml:element() | [exml:element()]} |
45 |
|
| {socket_send_first, exml:element() | [exml:element()]}. |
46 |
|
|
47 |
|
-type t() :: #{ |
48 |
|
state_mod := #{module() => term()}, |
49 |
|
actions := [gen_statem:action()], |
50 |
|
c2s_state := undefined | mongoose_c2s:state(), |
51 |
|
c2s_data := undefined | mongoose_c2s:data(), |
52 |
|
hard_stop := undefined | Reason :: term(), |
53 |
|
socket_send := [exml:element()] |
54 |
|
}. |
55 |
|
|
56 |
|
-type params() :: #{ |
57 |
|
state_mod => #{module() => term()}, |
58 |
|
actions => [gen_statem:action()], |
59 |
|
c2s_state => mongoose_c2s:state(), |
60 |
|
c2s_data => mongoose_c2s:data(), |
61 |
|
stop => Reason :: term(), |
62 |
|
hard_stop => Reason :: term(), |
63 |
|
route => [mongoose_acc:t()], |
64 |
|
flush => [mongoose_acc:t()], |
65 |
|
socket_send => [exml:element()] |
66 |
|
}. |
67 |
|
|
68 |
|
-export_type([t/0, pairs/0]). |
69 |
|
|
70 |
|
%% -------------------------------------------------------- |
71 |
|
%% API |
72 |
|
%% -------------------------------------------------------- |
73 |
|
|
74 |
|
-spec new() -> t(). |
75 |
|
new() -> |
76 |
121081 |
#{ |
77 |
|
state_mod => #{}, |
78 |
|
actions => [], |
79 |
|
c2s_state => undefined, |
80 |
|
c2s_data => undefined, |
81 |
|
hard_stop => undefined, |
82 |
|
socket_send => [] |
83 |
|
}. |
84 |
|
|
85 |
|
-spec new(params()) -> t(). |
86 |
|
new(Params) -> |
87 |
11639 |
Params1 = extract_stop(Params), |
88 |
11639 |
Params2 = extract_routes(Params1), |
89 |
11639 |
Params3 = extract_flushes(Params2), |
90 |
11639 |
maps:merge(new(), Params3). |
91 |
|
|
92 |
|
extract_stop(Params = #{stop := Reason}) -> |
93 |
4 |
WithoutStop = maps:remove(stop, Params), |
94 |
4 |
NewAction = [{next_event, cast, {stop, Reason}}], |
95 |
4 |
Fun = fun(Actions) -> [NewAction | Actions] end, |
96 |
4 |
maps:update_with(actions, Fun, NewAction, WithoutStop); |
97 |
|
extract_stop(Params) -> |
98 |
11635 |
Params. |
99 |
|
|
100 |
|
extract_flushes(Params = #{flush := Accs}) -> |
101 |
:-( |
WithoutFlush = maps:remove(flush, Params), |
102 |
:-( |
NewAction = [{next_event, internal, {flush, Acc}} || Acc <- Accs ], |
103 |
:-( |
Fun = fun(Actions) -> NewAction ++ Actions end, |
104 |
:-( |
maps:update_with(actions, Fun, NewAction, WithoutFlush); |
105 |
|
extract_flushes(Params) -> |
106 |
11639 |
Params. |
107 |
|
|
108 |
|
extract_routes(Params = #{route := Accs}) -> |
109 |
5811 |
WithoutRoute = maps:remove(route, Params), |
110 |
5811 |
NewAction = [{next_event, info, {route, Acc}} || Acc <- Accs ], |
111 |
5811 |
Fun = fun(Actions) -> NewAction ++ Actions end, |
112 |
5811 |
maps:update_with(actions, Fun, NewAction, WithoutRoute); |
113 |
|
extract_routes(Params) -> |
114 |
5828 |
Params. |
115 |
|
|
116 |
|
-spec get_statem_result(mongoose_acc:t()) -> mongoose_c2s_acc:t(). |
117 |
|
get_statem_result(Acc) -> |
118 |
63608 |
C2SAcc = #{actions := Actions, |
119 |
|
socket_send := SocketSend} = mongoose_acc:get_statem_acc(Acc), |
120 |
63608 |
C2SAcc#{actions := lists:reverse(Actions), |
121 |
|
socket_send := lists:reverse(SocketSend)}. |
122 |
|
|
123 |
|
-spec from_mongoose_acc(mongoose_acc:t(), key()) -> term(). |
124 |
|
from_mongoose_acc(Acc, Key) -> |
125 |
:-( |
#{Key := Value} = mongoose_acc:get_statem_acc(Acc), |
126 |
:-( |
Value. |
127 |
|
|
128 |
|
-spec to_acc(mongoose_acc:t(), state_mod, {module(), term()}) -> mongoose_acc:t(); |
129 |
|
(mongoose_acc:t(), actions, gen_statem:action() | [gen_statem:action()]) -> mongoose_acc:t(); |
130 |
|
(mongoose_acc:t(), c2s_state, mongoose_c2s:state()) -> mongoose_acc:t(); |
131 |
|
(mongoose_acc:t(), c2s_data, mongoose_c2s:data()) -> mongoose_acc:t(); |
132 |
|
(mongoose_acc:t(), hard_stop, atom()) -> mongoose_acc:t(); |
133 |
|
(mongoose_acc:t(), stop, atom() | {shutdown, atom()}) -> mongoose_acc:t(); |
134 |
|
(mongoose_acc:t(), route, mongoose_acc:t()) -> mongoose_acc:t(); |
135 |
|
(mongoose_acc:t(), flush, mongoose_acc:t()) -> mongoose_acc:t(); |
136 |
|
(mongoose_acc:t(), socket_send, exml:element() | [exml:element()]) -> mongoose_acc:t(); |
137 |
|
(mongoose_acc:t(), socket_send_first, exml:element() | [exml:element()]) -> mongoose_acc:t(). |
138 |
|
to_acc(Acc, Key, NewValue) -> |
139 |
15212 |
C2SAcc = mongoose_acc:get_statem_acc(Acc), |
140 |
15212 |
C2SAcc1 = to_c2s_acc(C2SAcc, {Key, NewValue}), |
141 |
15212 |
mongoose_acc:set_statem_acc(C2SAcc1, Acc). |
142 |
|
|
143 |
|
-spec to_acc_many(mongoose_acc:t(), pairs()) -> mongoose_acc:t(). |
144 |
|
to_acc_many(Acc, Pairs) -> |
145 |
5498 |
C2SAcc = mongoose_acc:get_statem_acc(Acc), |
146 |
5498 |
to_acc_many(Acc, C2SAcc, Pairs). |
147 |
|
|
148 |
|
-spec to_acc_many(mongoose_acc:t(), mongoose_c2s_acc:t(), pairs()) -> mongoose_acc:t(). |
149 |
|
to_acc_many(Acc, C2SAcc, []) -> |
150 |
5498 |
mongoose_acc:set_statem_acc(C2SAcc, Acc); |
151 |
|
to_acc_many(Acc, C2SAcc, [Pair | Pairs]) -> |
152 |
11074 |
NewCAcc = to_c2s_acc(C2SAcc, Pair), |
153 |
11074 |
to_acc_many(Acc, NewCAcc, Pairs). |
154 |
|
|
155 |
|
-spec to_c2s_acc(mongoose_c2s_acc:t(), pair()) -> mongoose_c2s_acc:t(). |
156 |
|
to_c2s_acc(C2SAcc = #{state_mod := ModStates}, {state_mod, {ModName, ModState}}) -> |
157 |
14442 |
C2SAcc#{state_mod := ModStates#{ModName => ModState}}; |
158 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {actions, NewActions}) when is_list(NewActions) -> |
159 |
6110 |
C2SAcc#{actions := lists:reverse(NewActions) ++ Actions}; |
160 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {actions, Action}) -> |
161 |
122 |
C2SAcc#{actions := [Action | Actions]}; |
162 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {route, Accs}) when is_list(Accs) -> |
163 |
5112 |
Routes = [{next_event, info, {route, Acc}} || Acc <- Accs ], |
164 |
5112 |
C2SAcc#{actions := lists:reverse(Routes) ++ Actions}; |
165 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {route, Acc}) -> |
166 |
120 |
C2SAcc#{actions := [{next_event, info, {route, Acc}} | Actions]}; |
167 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {flush, Accs}) when is_list(Accs) -> |
168 |
8 |
Routes = [{next_event, internal, {flush, Acc}} || Acc <- Accs ], |
169 |
8 |
C2SAcc#{actions := lists:reverse(Routes) ++ Actions}; |
170 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {flush, Acc}) -> |
171 |
18 |
C2SAcc#{actions := [{next_event, internal, {flush, Acc}} | Actions]}; |
172 |
|
to_c2s_acc(C2SAcc = #{socket_send := Stanzas}, {socket_send, NewStanzas}) when is_list(NewStanzas) -> |
173 |
12 |
C2SAcc#{socket_send := lists:reverse(NewStanzas) ++ Stanzas}; |
174 |
|
to_c2s_acc(C2SAcc = #{socket_send := Stanzas}, {socket_send, Stanza}) -> |
175 |
194 |
C2SAcc#{socket_send := [Stanza | Stanzas]}; |
176 |
|
to_c2s_acc(C2SAcc = #{socket_send := Stanzas}, {socket_send_first, NewStanzas}) when is_list(NewStanzas) -> |
177 |
:-( |
C2SAcc#{socket_send := Stanzas ++ lists:reverse(NewStanzas)}; |
178 |
|
to_c2s_acc(C2SAcc = #{socket_send := Stanzas}, {socket_send_first, Stanza}) -> |
179 |
20 |
C2SAcc#{socket_send := Stanzas ++ [Stanza]}; |
180 |
|
to_c2s_acc(C2SAcc = #{actions := Actions}, {stop, Reason}) -> |
181 |
4 |
C2SAcc#{actions := [{next_event, cast, {stop, Reason}} | Actions]}; |
182 |
|
to_c2s_acc(C2SAcc, {Key, NewValue}) -> |
183 |
124 |
#{Key := _OldValue} = C2SAcc, |
184 |
124 |
C2SAcc#{Key := NewValue}. |