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 class HandlerNotFoundException : Exception 37 { 38 this(const string method) 39 { 40 super("No handler found for method " ~ method); 41 } 42 } 43 44 /++ 45 Checks if a method has a response handler registered for it. Used to determine 46 if the server should send a request or a notification to the client (if the 47 method has a response handler, then the server will expect a response and thus 48 send a request instead of a notification). 49 +/ 50 bool hasResponseHandler(const string method) 51 { 52 return (method in responseHandlers) !is null; 53 } 54 55 /++ 56 Registers a new handler of any kind (`RequestHandler`, `NotificationHandler` or 57 `ResponseHandler`). 58 +/ 59 void pushHandler(F)(const string method, F func) 60 if (isSomeFunction!F && !is(F == RequestHandler) 61 && !is(F == NotificationHandler) && !is(F == ResponseHandler)) 62 { 63 import dls.util.json : convertFromJSON; 64 import std.traits : Parameters, ReturnType; 65 66 static if ((Parameters!F).length == 1) 67 { 68 pushHandler(method, (Nullable!JSONValue params) { 69 import dls.util.json : convertToJSON; 70 71 auto arg = convertFromJSON!((Parameters!F)[0])(params.isNull ? JSONValue(null) : params); 72 73 static if (is(ReturnType!F == void)) 74 { 75 func(arg); 76 } 77 else 78 { 79 return convertToJSON(func(arg)); 80 } 81 }); 82 } 83 else static if ((Parameters!F).length == 2) 84 { 85 pushHandler(method, (string id, Nullable!JSONValue params) => func(id, 86 convertFromJSON!((Parameters!F)[1])(params.isNull ? JSONValue(null) : params))); 87 } 88 else 89 { 90 static assert(false); 91 } 92 } 93 94 /++ Registers a new static `RequestHandler`. +/ 95 private void pushHandler(const string method, RequestHandler h) 96 { 97 requestHandlers[method] = h; 98 } 99 100 /++ Registers a new static `NotificationHandler`. +/ 101 private void pushHandler(const string method, NotificationHandler h) 102 { 103 notificationHandlers[method] = h; 104 } 105 106 /++ Registers a new static `ResponseHandler`. +/ 107 private void pushHandler(const string method, ResponseHandler h) 108 { 109 responseHandlers[method] = h; 110 } 111 112 /++ Registers a new dynamic `ResponseHandler` (used at runtime) +/ 113 void pushHandler(const string id, const string method) 114 { 115 runtimeResponseHandlers[id] = responseHandlers[method]; 116 } 117 118 /++ 119 Returns the `RequestHandler`/`NotificationHandler`/`ResponseHandler` 120 corresponding to a specific LSP method. 121 +/ 122 T handler(T)(const string methodOrId) 123 if (is(T == RequestHandler) || is(T == NotificationHandler) || is(T == ResponseHandler)) 124 { 125 static if (is(T == RequestHandler)) 126 { 127 alias handlers = requestHandlers; 128 } 129 else static if (is(T == NotificationHandler)) 130 { 131 alias handlers = notificationHandlers; 132 } 133 else 134 { 135 alias handlers = runtimeResponseHandlers; 136 } 137 138 if (methodOrId in handlers) 139 { 140 auto h = handlers[methodOrId]; 141 142 static if (is(T == ResponseHandler)) 143 { 144 runtimeResponseHandlers.remove(methodOrId); 145 } 146 147 return h; 148 } 149 150 throw new HandlerNotFoundException(methodOrId); 151 }