1 /* 2 *Copyright (C) 2018 Laurent Tréguier 3 * 4 *This file is part of DLS. 5 * 6 *DLS is free software: you can redistribute it and/or modify 7 *it under the terms of the GNU General Public License as published by 8 *the Free Software Foundation, either version 3 of the License, or 9 *(at your option) any later version. 10 * 11 *DLS is distributed in the hope that it will be useful, 12 *but WITHOUT ANY WARRANTY; without even the implied warranty of 13 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 *GNU General Public License for more details. 15 * 16 *You should have received a copy of the GNU General Public License 17 *along with DLS. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 module dls.protocol.handlers; 22 23 import std.json : JSONValue; 24 import std.traits : isSomeFunction; 25 import std.typecons : Nullable; 26 27 alias RequestHandler = Nullable!JSONValue delegate(Nullable!JSONValue); 28 alias NotificationHandler = void delegate(Nullable!JSONValue); 29 alias ResponseHandler = void delegate(string id, Nullable!JSONValue); 30 31 private shared RequestHandler[string] requestHandlers; 32 private shared NotificationHandler[string] notificationHandlers; 33 private shared ResponseHandler[string] responseHandlers; 34 private shared ResponseHandler[string] runtimeResponseHandlers; 35 36 /++ 37 Checks if a function is correct handler function. These will only be registered 38 at startup time and will never be unregistered. 39 +/ 40 template isHandler(func...) 41 { 42 enum isHandler = isSomeFunction!func; 43 } 44 45 class HandlerNotFoundException : Exception 46 { 47 this(string method) 48 { 49 super("No handler found for method " ~ method); 50 } 51 } 52 53 /++ 54 Checks if a method has a response handler registered for it. Used to determine 55 if the server should send a request or a notification to the client (if the 56 method has a response handler, then the server will expect a response and thus 57 send a request instead of a notification). 58 +/ 59 bool hasResponseHandler(string method) 60 { 61 return (method in responseHandlers) !is null; 62 } 63 64 /++ 65 Registers a new handler of any kind (`RequestHandler`, `NotificationHandler` or 66 `ResponseHandler`). 67 +/ 68 void pushHandler(F)(string method, F func) 69 if (isSomeFunction!F && !is(F == RequestHandler) 70 && !is(F == NotificationHandler) && !is(F == ResponseHandler)) 71 { 72 import dls.util.json : convertFromJSON; 73 import std.traits : Parameters, ReturnType; 74 75 static if ((Parameters!F).length == 1) 76 { 77 pushHandler(method, (Nullable!JSONValue params) { 78 import dls.util.json : convertToJSON; 79 80 auto arg = convertFromJSON!((Parameters!F)[0])(params.isNull ? JSONValue(null) : params); 81 82 static if (is(ReturnType!F == void)) 83 { 84 func(arg); 85 } 86 else 87 { 88 return convertToJSON(func(arg)); 89 } 90 }); 91 } 92 else static if ((Parameters!F).length == 2) 93 { 94 pushHandler(method, (string id, Nullable!JSONValue params) => func(id, 95 convertFromJSON!((Parameters!F)[1])(params.isNull ? JSONValue(null) : params))); 96 } 97 else 98 { 99 static assert(false); 100 } 101 } 102 103 /++ Registers a new static `RequestHandler`. +/ 104 private void pushHandler(string method, RequestHandler h) 105 { 106 requestHandlers[method] = h; 107 } 108 109 /++ Registers a new static `NotificationHandler`. +/ 110 private void pushHandler(string method, NotificationHandler h) 111 { 112 notificationHandlers[method] = h; 113 } 114 115 /++ Registers a new static `ResponseHandler`. +/ 116 private void pushHandler(string method, ResponseHandler h) 117 { 118 responseHandlers[method] = h; 119 } 120 121 /++ Registers a new dynamic `ResponseHandler` (used at runtime) +/ 122 void pushHandler(string id, string method) 123 { 124 runtimeResponseHandlers[id] = responseHandlers[method]; 125 } 126 127 /++ 128 Returns the `RequestHandler`/`NotificationHandler`/`ResponseHandler` 129 corresponding to a specific LSP method. 130 +/ 131 T handler(T)(string methodOrId) 132 if (is(T == RequestHandler) || is(T == NotificationHandler) || is(T == ResponseHandler)) 133 { 134 static if (is(T == RequestHandler)) 135 { 136 alias handlers = requestHandlers; 137 } 138 else static if (is(T == NotificationHandler)) 139 { 140 alias handlers = notificationHandlers; 141 } 142 else 143 { 144 alias handlers = runtimeResponseHandlers; 145 } 146 147 if (methodOrId in handlers) 148 { 149 auto h = handlers[methodOrId]; 150 151 static if (is(T == ResponseHandler)) 152 { 153 runtimeResponseHandlers.remove(methodOrId); 154 } 155 156 return h; 157 } 158 159 throw new HandlerNotFoundException(methodOrId); 160 }