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 }