1 module dls.server;
2 
3 import dls.protocol.handlers;
4 import dls.protocol.jsonrpc;
5 
6 shared static this()
7 {
8     import std.algorithm : map;
9     import std.array : join, split;
10     import std.meta : AliasSeq;
11     import std.traits : hasUDA, select;
12     import std.typecons : tuple;
13     import std.string : capitalize;
14 
15     foreach (modName; AliasSeq!("general", "client", "text_document", "window", "workspace"))
16     {
17         mixin("import dls.protocol.messages" ~ (modName.length ? "." ~ modName : "") ~ ";");
18         mixin("alias mod = dls.protocol.messages" ~ (modName.length ? "." ~ modName : "") ~ ";");
19 
20         foreach (thing; __traits(allMembers, mod))
21         {
22             mixin("alias t = " ~ thing ~ ";");
23 
24             static if (isHandler!t)
25             {
26                 enum attrs = tuple(__traits(getAttributes, t));
27                 enum attrsWithDefaults = tuple(modName[0] ~ modName.split('_')
28                             .map!capitalize().join()[1 .. $], thing, attrs.expand);
29                 enum parts = tuple(attrsWithDefaults[attrs.length > 0 ? 2 : 0],
30                             attrsWithDefaults[attrs.length > 1 ? 3 : 1]);
31                 enum method = select!(parts[0].length != 0)(parts[0] ~ "/", "") ~ parts[1];
32 
33                 pushHandler(method, &t);
34             }
35         }
36     }
37 }
38 
39 abstract class Server
40 {
41     import dls.protocol.interfaces : InitializeParams;
42     import dls.util.logger : logger;
43     import std.algorithm : find, findSplit;
44     import std.json : JSONValue;
45     import std.string : strip, stripRight;
46     import std.typecons : Nullable, nullable;
47 
48     static bool initialized = false;
49     static bool shutdown = false;
50     static bool exit = false;
51     private static InitializeParams _initState;
52 
53     @property static InitializeParams initState()
54     {
55         return _initState;
56     }
57 
58     @property static void initState(InitializeParams params)
59     {
60         _initState = params;
61 
62         debug
63         {
64             logger.trace = InitializeParams.Trace.verbose;
65         }
66         else
67         {
68             logger.trace = params.trace;
69         }
70     }
71 
72     @property static InitializeParams.InitializationOptions initOptions()
73     {
74         return _initState.initializationOptions.isNull
75             ? new InitializeParams.InitializationOptions() : _initState.initializationOptions;
76     }
77 
78     static void loop()
79     {
80         import std.conv : to;
81         import std.stdio : stdin;
82 
83         while (!stdin.eof && !exit)
84         {
85             string[][] headers;
86             string line;
87 
88             do
89             {
90                 line = stdin.readln().stripRight();
91                 auto parts = line.findSplit(":");
92 
93                 if (parts[1].length > 0)
94                 {
95                     headers ~= [parts[0], parts[2]];
96                 }
97             }
98             while (line.length > 0);
99 
100             if (headers.length == 0)
101             {
102                 continue;
103             }
104 
105             auto contentLengthResult = headers.find!((parts,
106                     name) => parts.length > 0 && parts[0] == name)("Content-Length");
107 
108             if (contentLengthResult.length == 0)
109             {
110                 logger.error("No valid Content-Length section in header");
111                 continue;
112             }
113 
114             static char[] buffer;
115             const contentLength = contentLengthResult[0][1].strip().to!size_t;
116             buffer.length = contentLength;
117             const content = stdin.rawRead(buffer);
118 
119             handleJSON(content);
120         }
121     }
122 
123     private static void handleJSON(in char[] content)
124     {
125         import dls.protocol.jsonrpc : send, sendError;
126         import dls.util.json : convertFromJSON;
127         import std.algorithm : canFind;
128         import std.json : JSONException, parseJSON;
129 
130         RequestMessage request;
131 
132         try
133         {
134             const json = parseJSON(content);
135 
136             if ("method" in json)
137             {
138                 if ("id" in json)
139                 {
140                     request = convertFromJSON!RequestMessage(json);
141 
142                     if (!shutdown && (initialized || ["initialize"].canFind(request.method)))
143                     {
144                         send(request.id, handler!RequestHandler(request.method)(request.params));
145                     }
146                     else
147                     {
148                         sendError(ErrorCodes.serverNotInitialized, request, JSONValue());
149                     }
150                 }
151                 else
152                 {
153                     auto notification = convertFromJSON!NotificationMessage(json);
154 
155                     if (initialized)
156                     {
157                         handler!NotificationHandler(notification.method)(notification.params);
158                     }
159                 }
160             }
161             else
162             {
163                 auto response = convertFromJSON!ResponseMessage(json);
164 
165                 if (response.error.isNull)
166                 {
167                     handler!ResponseHandler(response.id.str)(response.id.str, response.result);
168                 }
169                 else
170                 {
171                     logger.error(response.error.message);
172                 }
173             }
174         }
175         catch (JSONException e)
176         {
177             logger.errorf("%s: %s", ErrorCodes.parseError[0], e);
178             sendError(ErrorCodes.parseError, request, JSONValue(e.message));
179         }
180         catch (HandlerNotFoundException e)
181         {
182             logger.errorf("%s: %s", ErrorCodes.methodNotFound[0], e);
183             sendError(ErrorCodes.methodNotFound, request, JSONValue(e.message));
184         }
185         catch (Exception e)
186         {
187             logger.errorf("%s: %s", ErrorCodes.internalError[0], e);
188             sendError(ErrorCodes.internalError, request, JSONValue(e.message));
189         }
190     }
191 }