Skip to content

Commit 876d887

Browse files
committed
Add static message dispatch option
1 parent 0f9ff61 commit 876d887

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

src/typed.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,40 @@ function dispatch_msg(x::JSONRPCEndpoint, dispatcher::MsgDispatcher, msg)
8686
end
8787

8888
is_currently_handling_msg(d::MsgDispatcher) = d._currentlyHandlingMsg
89+
90+
macro message_dispatcher(name, body)
91+
quote
92+
function $(esc(name))(x, msg::Dict{String,Any}, context=nothing)
93+
method_name = msg["method"]::String
94+
95+
$(
96+
(
97+
:(
98+
if method_name == $(esc(i.args[2])).method
99+
param_type = get_param_type($(esc(i.args[2])))
100+
params = param_type === Nothing ? nothing : param_type <: NamedTuple ? convert(param_type,(;(Symbol(i[1])=>i[2] for i in msg["params"])...)) : param_type(msg["params"])
101+
102+
res = $(esc(i.args[3]))(x, params)
103+
104+
if $(esc(i.args[2])) isa RequestType
105+
if res isa JSONRPCError
106+
send_error_response(x, msg, res.code, res.msg, res.data)
107+
elseif res isa get_return_type($(esc(i.args[2])))
108+
send_success_response(x, msg, res)
109+
else
110+
error_msg = "The handler for the '$method_name' request returned a value of type $(typeof(res)), which is not a valid return type according to the request definition."
111+
send_error_response(x, msg, -32603, error_msg, nothing)
112+
error(error_msg)
113+
end
114+
end
115+
116+
return
117+
end
118+
) for i in filter(i->i isa Expr, body.args)
119+
)...
120+
)
121+
122+
error("Unknown method $method_name.")
123+
end
124+
end
125+
end

test/test_typed.jl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,118 @@ end
120120
@test typed_res(['f','o','o'], String) isa String
121121
@test typed_res("foo", String) isa String
122122
end
123+
124+
@testitem "Static message dispatcher" setup=[TestStructs] begin
125+
using Sockets
126+
using .TestStructs: Foo, Foo2
127+
128+
if Sys.iswindows()
129+
global_socket_name1 = "\\\\.\\pipe\\jsonrpc-testrun3"
130+
elseif Sys.isunix()
131+
global_socket_name1 = joinpath(tempdir(), "jsonrpc-testrun1")
132+
else
133+
error("Unknown operating system.")
134+
end
135+
136+
request1_type = JSONRPC.RequestType("request1", Foo, String)
137+
request2_type = JSONRPC.RequestType("request2", Nothing, String)
138+
notify1_type = JSONRPC.NotificationType("notify1", String)
139+
140+
global g_var = ""
141+
142+
server_is_up = Base.Condition()
143+
144+
JSONRPC.@message_dispatcher my_dispatcher begin
145+
request1_type => (conn, params) -> begin
146+
params.fieldA == 1 ? "YES" : "NO"
147+
end
148+
request2_type => (conn, params) -> JSONRPC.JSONRPCError(-32600, "Our message", nothing)
149+
notify1_type => (conn, params) -> global g_var = params
150+
end
151+
152+
server_task = @async try
153+
server = listen(global_socket_name1)
154+
notify(server_is_up)
155+
sock = accept(server)
156+
global conn = JSONRPC.JSONRPCEndpoint(sock, sock)
157+
global msg_dispatcher = JSONRPC.MsgDispatcher()
158+
159+
run(conn)
160+
161+
for msg in conn
162+
my_dispatcher(conn, msg)
163+
end
164+
catch err
165+
Base.display_error(stderr, err, catch_backtrace())
166+
end
167+
168+
wait(server_is_up)
169+
170+
sock2 = connect(global_socket_name1)
171+
conn2 = JSONRPCEndpoint(sock2, sock2)
172+
173+
run(conn2)
174+
175+
JSONRPC.send(conn2, notify1_type, "TEST")
176+
177+
res = JSONRPC.send(conn2, request1_type, Foo(fieldA=1, fieldB="FOO"))
178+
179+
@test res == "YES"
180+
@test g_var == "TEST"
181+
182+
@test_throws JSONRPC.JSONRPCError(-32600, "Our message", nothing) JSONRPC.send(conn2, request2_type, nothing)
183+
184+
close(conn2)
185+
close(sock2)
186+
close(conn)
187+
188+
fetch(server_task)
189+
190+
# Now we test a faulty server
191+
192+
if Sys.iswindows()
193+
global_socket_name2 = "\\\\.\\pipe\\jsonrpc-testrun4"
194+
elseif Sys.isunix()
195+
global_socket_name2 = joinpath(tempdir(), "jsonrpc-testrun2")
196+
else
197+
error("Unknown operating system.")
198+
end
199+
200+
server_is_up = Base.Condition()
201+
202+
JSONRPC.@message_dispatcher my_dispatcher2 begin
203+
request2_type => (conn, params) -> 34 # The request type requires a `String` return, so this tests whether we get an error.
204+
end
205+
206+
server_task2 = @async try
207+
server = listen(global_socket_name2)
208+
notify(server_is_up)
209+
sock = accept(server)
210+
global conn = JSONRPC.JSONRPCEndpoint(sock, sock)
211+
global msg_dispatcher = JSONRPC.MsgDispatcher()
212+
213+
run(conn)
214+
215+
for msg in conn
216+
@test_throws ErrorException("The handler for the 'request2' request returned a value of type $Int, which is not a valid return type according to the request definition.") my_dispatcher2(conn, msg)
217+
end
218+
catch err
219+
Base.display_error(stderr, err, catch_backtrace())
220+
end
221+
222+
wait(server_is_up)
223+
224+
sock2 = connect(global_socket_name2)
225+
conn2 = JSONRPCEndpoint(sock2, sock2)
226+
227+
run(conn2)
228+
229+
@test_throws JSONRPC.JSONRPCError(-32603, "The handler for the 'request2' request returned a value of type $Int, which is not a valid return type according to the request definition.", nothing) JSONRPC.send(conn2, request2_type, nothing)
230+
231+
close(conn2)
232+
close(sock2)
233+
close(conn)
234+
235+
fetch(server_task)
236+
237+
end

0 commit comments

Comments
 (0)