1 module dls.protocol.handlers;
2 
3 import std.json : JSONValue;
4 import std.typecons : Nullable;
5 import std.traits;
6 
7 alias RequestHandler = Nullable!JSONValue delegate(Nullable!JSONValue);
8 alias NotificationHandler = void delegate(Nullable!JSONValue);
9 alias ResponseHandler = void delegate(string id, Nullable!JSONValue);
10 
11 private shared RequestHandler[string] requestHandlers;
12 private shared NotificationHandler[string] notificationHandlers;
13 private shared ResponseHandler[string] responseHandlers;
14 private shared ResponseHandler[string] runtimeResponseHandlers;
15 
16 /++
17 Checks if a function is correct handler function. These will only be registered
18 at startup time and will never be unregistered.
19 +/
20 template isHandler(func...)
21 {
22     enum isHandler = __traits(compiles, isSomeFunction!func) && isSomeFunction!func;
23 }
24 
25 class HandlerNotFoundException : Exception
26 {
27     this(string method)
28     {
29         super("No handler found for method " ~ method);
30     }
31 }
32 
33 /++
34 Checks if a method has a handler registered for it. Used to determine if the
35 server should send a request or a notification to the client (if the method has
36 a handler, then the server will expect a response and thus send a request).
37 +/
38 bool hasRegisteredHandler(string method)
39 {
40     return (method in requestHandlers) || (method in notificationHandlers)
41         || (method in responseHandlers);
42 }
43 
44 /++
45 Registers a new handler of any kind (`RequestHandler`, `NotificationHandler` or
46 `ResponseHandler`).
47 +/
48 void pushHandler(F)(string method, F func)
49         if (isSomeFunction!F && !is(F == RequestHandler)
50             && !is(F == NotificationHandler) && !is(F == ResponseHandler))
51 {
52     import dls.util.json : convertFromJSON;
53 
54     static if ((Parameters!F).length == 1)
55     {
56         pushHandler(method, (Nullable!JSONValue params) {
57             import dls.util.json : convertToJSON;
58 
59             auto arg = convertFromJSON!((Parameters!F)[0])(params.isNull ? JSONValue(null) : params);
60 
61             static if (is(ReturnType!F == void))
62             {
63                 func(arg);
64             }
65             else
66             {
67                 return convertToJSON(func(arg));
68             }
69         });
70     }
71     else static if ((Parameters!F).length == 2)
72     {
73         pushHandler(method, (string id, Nullable!JSONValue params) => func(id,
74                 convertFromJSON!((Parameters!F)[1])(params.isNull ? JSONValue(null) : params)));
75     }
76     else
77     {
78         static assert(false);
79     }
80 }
81 
82 /++ Registers a new static `RequestHandler`. +/
83 private void pushHandler(string method, RequestHandler h)
84 {
85     requestHandlers[method] = h;
86 }
87 
88 /++ Registers a new static `NotificationHandler`. +/
89 private void pushHandler(string method, NotificationHandler h)
90 {
91     notificationHandlers[method] = h;
92 }
93 
94 /++ Registers a new static `ResponseHandler`. +/
95 private void pushHandler(string method, ResponseHandler h)
96 {
97     responseHandlers[method] = h;
98 }
99 
100 /++ Registers a new dynamic `ResponseHandler` (used at runtime) +/
101 void pushHandler(string id, string method)
102 {
103     runtimeResponseHandlers[id] = responseHandlers[method];
104 }
105 
106 /++
107 Returns the `RequestHandler`/`NotificationHandler`/`ResponseHandler`
108 corresponding to a specific LSP method.
109 +/
110 auto handler(T)(string methodOrId)
111         if (is(T == RequestHandler) || is(T == NotificationHandler) || is(T == ResponseHandler))
112 {
113     static if (is(T == RequestHandler))
114     {
115         alias handlers = requestHandlers;
116     }
117     else static if (is(T == NotificationHandler))
118     {
119         alias handlers = notificationHandlers;
120     }
121     else
122     {
123         alias handlers = runtimeResponseHandlers;
124     }
125 
126     if (methodOrId in handlers)
127     {
128         auto h = handlers[methodOrId];
129 
130         static if (is(T == ResponseHandler))
131         {
132             runtimeResponseHandlers.remove(methodOrId);
133         }
134 
135         return h;
136     }
137 
138     throw new HandlerNotFoundException(methodOrId);
139 }