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