1+ module tests.json ;
2+
3+ import juptune.core.ds : ArrayNonShrink;
4+ import juptune.core.util : Result;
5+ import juptune.http : Http1Writer, Http1Version, Http1MessageSummary;
6+
7+ import tests.common : putServerAndDate, log;
8+
9+ struct JsonHeaderInput
10+ {
11+ // No header input, it's here just so the overarching logic exists.
12+ }
13+
14+ private struct Message
15+ {
16+ string message;
17+ }
18+
19+ void handleJson (
20+ scope ref JsonHeaderInput input,
21+ scope ref Http1Writer writer,
22+ scope ref Http1MessageSummary summary,
23+ ) nothrow
24+ {
25+ import juptune.core.util : then;
26+ import juptune.core.util.conv : IntToCharBuffer, toBase10;
27+
28+ ArrayNonShrink! char buffer;
29+ buffer.reserve (256 );
30+
31+ auto result = serialise(buffer, Message(" Hello, World!" ));
32+ if (result.isError)
33+ {
34+ result = writer.putResponseLine(Http1Version.http11, 500 , " Internal Error" ).then! (
35+ () => writer.putServerAndDate(),
36+ () => writer.finishHeaders(),
37+ () => writer.finishBody(),
38+ () => writer.finishTrailers(),
39+ () => writer.finishMessage(summary)
40+ );
41+ if (result.isError)
42+ {
43+ log(" writing error response [json] failed: " , result);
44+ return ;
45+ }
46+
47+ log(" serialising value failed: " , result);
48+ return ;
49+ }
50+
51+ IntToCharBuffer contentLengthBuffer;
52+ const contentLength = toBase10(buffer.length, contentLengthBuffer);
53+
54+ result = writer.putResponseLine(Http1Version.http11, 200 , " OK" ).then! (
55+ () => writer.putServerAndDate(),
56+ () => writer.putHeader(" Content-Length" , contentLength),
57+ () => writer.putHeader(" Content-Type" , " application/json" ),
58+ () => writer.finishHeaders(),
59+ () => writer.putBody(buffer.slice),
60+ () => writer.finishBody(),
61+ () => writer.finishTrailers(),
62+ () => writer.finishMessage(summary)
63+ );
64+ if (result.isError)
65+ {
66+ log(" writing response [json] failed: " , result);
67+ return ;
68+ }
69+ }
70+
71+ // There's currently no built-in serialiser, however because of D's incredibly powerful metaprogramming
72+ // this watered-down serialiser would likely generate almost the exact same code as a full-blown serialiser
73+ // in this simple case.
74+ private Result serialise (T)(scope ref ArrayNonShrink! char buffer, T value)
75+ if (is (T == struct ))
76+ {
77+ import juptune.data.json : JsonBuilder;
78+ scope append = (scope const (char )[] text) {
79+ buffer.put(text);
80+ return Result.noError;
81+ };
82+
83+ ubyte [8 ] depthBuffer;
84+ auto builder = JsonBuilder! (typeof (append))(append, depthBuffer[]);
85+
86+ auto result = builder.startObject();
87+ if (result.isError)
88+ return result;
89+
90+ static foreach (fieldSymbol; value.tupleof)
91+ {{
92+ immutable FieldName = __traits(identifier, fieldSymbol);
93+
94+ result = builder.putObjectValue(FieldName, mixin (" value." ~ FieldName));
95+ if (result.isError)
96+ return result;
97+ }}
98+
99+ result = builder.endObject();
100+ if (result.isError)
101+ return result;
102+ return builder.finish();
103+ }
0 commit comments