_site/cover/resp_emulator.COVER.html

1 %% Copyright (c) 2023 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(resp_emulator).
17
18
19 -export([init/1]).
20 -export([recv/1]).
21 -include_lib("kernel/include/logger.hrl").
22
23
24 -record(entry,
25 {key :: undefined | binary(),
26 hash :: map()}).
27
28
29 init([]) ->
30
:-(
{ok,
31 #{table => ets:new(?MODULE, [{keypos, 2}, public, named_table]),
32 protocol => #{version => 2}}}.
33
34
35
36
37 recv(#{command := #{name := info}, message := {array, [_]}}) ->
38
:-(
{continue,
39 {encode,
40 {bulk,
41 ["# Server\r\nredis_version:", resp:version(), "\r\n"]}}};
42
43 recv(#{command := #{name := command}, message := {array, _}}) ->
44
:-(
{continue, {encode, {array, []}}};
45
46 recv(#{command := #{name := hello},
47 message := {array, [_, {bulk, <<"3">>}]},
48 data := #{protocol := Protocol} = Data}) ->
49
:-(
{continue,
50 Data#{protocol := Protocol#{version => 3}},
51 {encode,
52 {map,
53 [{{bulk, <<"server">>}, {bulk, <<"resp">>}},
54 {{bulk, <<"version">>}, {bulk, resp:version()}},
55 {{bulk,<<"proto">>}, {integer, 3}},
56 {{bulk, <<"id">>}, {integer, erlang:phash2(self())}},
57 {{bulk, <<"mode">>}, {bulk, <<"standalone">>}},
58 {{bulk, <<"role">>}, {bulk, <<"master">>}},
59 {{bulk, <<"modules">>}, {array, []}}]}}};
60
61 recv(#{command := #{name := hello},
62 message := {array, [_, {bulk, <<"2">>}]},
63 data := #{protocol := Protocol} = Data}) ->
64
:-(
{continue,
65 Data#{protocol := Protocol#{version => 2}},
66 {encode,
67 {array,
68 [{bulk, <<"server">>}, {bulk, <<"resp">>},
69 {bulk, <<"version">>}, {bulk, resp:version()},
70 {bulk, <<"proto">>}, {integer, 2},
71 {bulk, <<"id">>}, {integer, erlang:phash2(self())},
72 {bulk, <<"mode">>}, {bulk, <<"standalone">>},
73 {bulk, <<"role">>}, {bulk, <<"master">>},
74 {bulk, <<"modules">>}, {array, []}]}}};
75
76 recv(#{command := #{name := hset},
77 message := {array, [_, {bulk, Key} | NamedValues]}}) ->
78
:-(
Hash = hash(NamedValues),
79
:-(
ets:insert(?MODULE, #entry{key = Key, hash = Hash}),
80
:-(
{continue, {encode, {integer, map_size(Hash)}}};
81
82 recv(#{command := #{name := hget},
83 message := {array, [_, {bulk, Key}, {bulk, Field}]}}) ->
84
:-(
case ets:lookup(?MODULE, Key) of
85 [] ->
86
:-(
{continue, {encode, {array, []}}};
87
88 [#entry{hash = Hash}] ->
89
:-(
case maps:find(Field, Hash) of
90 {ok, Value} ->
91
:-(
{continue, {encode, {bulk, Value}}};
92
93 error ->
94
:-(
{continue, {encode, {array, []}}}
95 end
96 end;
97
98 recv(#{command := #{name := hgetall},
99 message := {array, [_, {bulk, Key}]}}) ->
100
:-(
case ets:lookup(?MODULE, Key) of
101 [] ->
102
:-(
{continue, {encode, {array, []}}};
103
104 [#entry{hash = Hash}] ->
105
:-(
{continue,
106 {encode,
107 {array,
108 maps:fold(
109 fun
110 (K, V, A) ->
111
:-(
[{bulk, K}, {bulk, V} | A]
112 end,
113 [],
114 Hash)}}}
115 end;
116
117 recv(#{message := {array, [{bulk, Command} | _]}} = Arg) ->
118
:-(
?LOG_DEBUG(#{unknown => Arg}),
119
:-(
{continue, {encode, {error, ["unknown command '", Command, "'"]}}}.
120
121
122 hash(NamedValues) ->
123
:-(
?FUNCTION_NAME(NamedValues, #{}).
124
125 hash([{bulk, Key}, {bulk, Value} | T], A) ->
126
:-(
?FUNCTION_NAME(T, A#{Key => Value});
127 hash([], A) ->
128
:-(
A.
Line Hits Source