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 }