1 module dls.protocol.jsonrpc; 2 3 import std.json : JSONValue; 4 import std.typecons : Nullable, Tuple, nullable, tuple; 5 6 private enum jsonrpcVersion = "2.0"; 7 private enum eol = "\r\n"; 8 9 abstract class Message 10 { 11 string jsonrpc = jsonrpcVersion; 12 } 13 14 class RequestMessage : Message 15 { 16 JSONValue id; 17 string method; 18 Nullable!JSONValue params; 19 } 20 21 class ResponseMessage : Message 22 { 23 JSONValue id; 24 Nullable!JSONValue result; 25 Nullable!ResponseError error; 26 } 27 28 class ResponseError 29 { 30 int code; 31 string message; 32 Nullable!JSONValue data; 33 34 static ResponseError fromErrorCode(ErrorCodes errorCode, JSONValue data) 35 { 36 auto response = new ResponseError(); 37 response.code = errorCode[0]; 38 response.message = errorCode[1]; 39 response.data = data; 40 return response.nullable; 41 } 42 } 43 44 class NotificationMessage : Message 45 { 46 string method; 47 Nullable!JSONValue params; 48 } 49 50 enum ErrorCodes : Tuple!(int, string) 51 { 52 parseError = tuple(-32_700, "Parse error"), 53 invalidRequest = tuple(-32_600, 54 "Invalid Request"), 55 methodNotFound = tuple( 56 -32_601, "Method not found"), 57 invalidParams = tuple(-32_602, 58 "Invalid params"), 59 internalError = tuple(-32_603, 60 "Internal error"), 61 serverNotInitialized = tuple(-32_202, 62 "Server not initialized"), 63 unknownErrorCode = tuple(-32_201, 64 "Unknown error"), 65 requestCancelled = tuple(-32_800, "Request cancelled") 66 } 67 68 class CancelParams 69 { 70 JSONValue id; 71 } 72 73 void sendError(ErrorCodes error, RequestMessage request, JSONValue data) 74 { 75 if (request !is null) 76 { 77 send(request.id, Nullable!JSONValue(), ResponseError.fromErrorCode(error, data).nullable); 78 } 79 } 80 81 /++ Sends a request or a notification message. +/ 82 string send(string method, Nullable!JSONValue params = Nullable!JSONValue()) 83 { 84 import dls.protocol.handlers : hasRegisteredHandler, pushHandler; 85 import std.uuid : randomUUID; 86 87 if (hasRegisteredHandler(method)) 88 { 89 auto id = "dls-" ~ randomUUID().toString(); 90 pushHandler(id, method); 91 send!RequestMessage(JSONValue(id), method, params, Nullable!ResponseError()); 92 return id; 93 } 94 95 send!NotificationMessage(JSONValue(), method, params, Nullable!ResponseError()); 96 return null; 97 } 98 99 string send(T)(string method, T params) if (!is(T : Nullable!JSONValue)) 100 { 101 import dls.util.json : convertToJSON; 102 103 return send(method, convertToJSON(params).nullable); 104 } 105 106 /++ Sends a response message. +/ 107 void send(JSONValue id, Nullable!JSONValue result, 108 Nullable!ResponseError error = Nullable!ResponseError()) 109 { 110 send!ResponseMessage(id, null, result, error); 111 } 112 113 private void send(T : Message)(JSONValue id, string method, 114 Nullable!JSONValue payload, Nullable!ResponseError error) 115 { 116 import std.meta : AliasSeq; 117 import std.traits : select; 118 119 auto message = new T(); 120 121 __traits(getMember, message, select!(__traits(hasMember, T, "params"))("params", "result")) = payload; 122 123 foreach (member; AliasSeq!("id", "method", "error")) 124 { 125 static if (__traits(hasMember, T, member)) 126 { 127 mixin("message." ~ member ~ " = " ~ member ~ ";"); 128 } 129 } 130 131 send(message); 132 } 133 134 private void send(T : Message)(T m) 135 { 136 import dls.util.json : convertToJSON; 137 import std.conv : to; 138 import std.stdio : stdout; 139 import std.utf : toUTF8; 140 141 auto message = convertToJSON(m); 142 auto messageString = message.get().toString(); 143 144 synchronized 145 { 146 foreach (chunk; ["Content-Length: ", messageString.length.to!string, 147 eol, eol, messageString]) 148 { 149 stdout.rawWrite(chunk.toUTF8()); 150 } 151 152 stdout.flush(); 153 } 154 }