1use std::collections::HashMap;
2
3use serde::Serialize;
4
5use crate::Value;
6
7pub fn playground_source(config: GraphQLPlaygroundConfig) -> String {
17 let title = config.title.unwrap_or("GraphQL Playground");
18 r##"
19<!DOCTYPE html>
20
21<html>
22
23<head>
24 <meta charset=utf-8 />
25 <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
26 <title>%GRAPHQL_PLAYGROUND_TITLE%</title>
27 <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />
28 <link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/favicon.png" />
29 <script src="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
30 <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700" />
31</head>
32
33<body>
34 <style type="text/css">
35 html {
36 font-family: "Open Sans", sans-serif;
37 overflow: hidden;
38 }
39
40 body {
41 margin: 0;
42 background: #172a3a;
43 }
44
45 .playgroundIn {
46 -webkit-animation: playgroundIn 0.5s ease-out forwards;
47 animation: playgroundIn 0.5s ease-out forwards;
48 }
49
50 @-webkit-keyframes playgroundIn {
51 from {
52 opacity: 0;
53 -webkit-transform: translateY(10px);
54 -ms-transform: translateY(10px);
55 transform: translateY(10px);
56 }
57 to {
58 opacity: 1;
59 -webkit-transform: translateY(0);
60 -ms-transform: translateY(0);
61 transform: translateY(0);
62 }
63 }
64
65 @keyframes playgroundIn {
66 from {
67 opacity: 0;
68 -webkit-transform: translateY(10px);
69 -ms-transform: translateY(10px);
70 transform: translateY(10px);
71 }
72 to {
73 opacity: 1;
74 -webkit-transform: translateY(0);
75 -ms-transform: translateY(0);
76 transform: translateY(0);
77 }
78 }
79 </style>
80
81 <style type="text/css">
82 .fadeOut {
83 -webkit-animation: fadeOut 0.5s ease-out forwards;
84 animation: fadeOut 0.5s ease-out forwards;
85 }
86
87 @-webkit-keyframes fadeIn {
88 from {
89 opacity: 0;
90 -webkit-transform: translateY(-10px);
91 -ms-transform: translateY(-10px);
92 transform: translateY(-10px);
93 }
94 to {
95 opacity: 1;
96 -webkit-transform: translateY(0);
97 -ms-transform: translateY(0);
98 transform: translateY(0);
99 }
100 }
101
102 @keyframes fadeIn {
103 from {
104 opacity: 0;
105 -webkit-transform: translateY(-10px);
106 -ms-transform: translateY(-10px);
107 transform: translateY(-10px);
108 }
109 to {
110 opacity: 1;
111 -webkit-transform: translateY(0);
112 -ms-transform: translateY(0);
113 transform: translateY(0);
114 }
115 }
116
117 @-webkit-keyframes fadeOut {
118 from {
119 opacity: 1;
120 -webkit-transform: translateY(0);
121 -ms-transform: translateY(0);
122 transform: translateY(0);
123 }
124 to {
125 opacity: 0;
126 -webkit-transform: translateY(-10px);
127 -ms-transform: translateY(-10px);
128 transform: translateY(-10px);
129 }
130 }
131
132 @keyframes fadeOut {
133 from {
134 opacity: 1;
135 -webkit-transform: translateY(0);
136 -ms-transform: translateY(0);
137 transform: translateY(0);
138 }
139 to {
140 opacity: 0;
141 -webkit-transform: translateY(-10px);
142 -ms-transform: translateY(-10px);
143 transform: translateY(-10px);
144 }
145 }
146
147 @-webkit-keyframes appearIn {
148 from {
149 opacity: 0;
150 -webkit-transform: translateY(0px);
151 -ms-transform: translateY(0px);
152 transform: translateY(0px);
153 }
154 to {
155 opacity: 1;
156 -webkit-transform: translateY(0);
157 -ms-transform: translateY(0);
158 transform: translateY(0);
159 }
160 }
161
162 @keyframes appearIn {
163 from {
164 opacity: 0;
165 -webkit-transform: translateY(0px);
166 -ms-transform: translateY(0px);
167 transform: translateY(0px);
168 }
169 to {
170 opacity: 1;
171 -webkit-transform: translateY(0);
172 -ms-transform: translateY(0);
173 transform: translateY(0);
174 }
175 }
176
177 @-webkit-keyframes scaleIn {
178 from {
179 -webkit-transform: scale(0);
180 -ms-transform: scale(0);
181 transform: scale(0);
182 }
183 to {
184 -webkit-transform: scale(1);
185 -ms-transform: scale(1);
186 transform: scale(1);
187 }
188 }
189
190 @keyframes scaleIn {
191 from {
192 -webkit-transform: scale(0);
193 -ms-transform: scale(0);
194 transform: scale(0);
195 }
196 to {
197 -webkit-transform: scale(1);
198 -ms-transform: scale(1);
199 transform: scale(1);
200 }
201 }
202
203 @-webkit-keyframes innerDrawIn {
204 0% {
205 stroke-dashoffset: 70;
206 }
207 50% {
208 stroke-dashoffset: 140;
209 }
210 100% {
211 stroke-dashoffset: 210;
212 }
213 }
214
215 @keyframes innerDrawIn {
216 0% {
217 stroke-dashoffset: 70;
218 }
219 50% {
220 stroke-dashoffset: 140;
221 }
222 100% {
223 stroke-dashoffset: 210;
224 }
225 }
226
227 @-webkit-keyframes outerDrawIn {
228 0% {
229 stroke-dashoffset: 76;
230 }
231 100% {
232 stroke-dashoffset: 152;
233 }
234 }
235
236 @keyframes outerDrawIn {
237 0% {
238 stroke-dashoffset: 76;
239 }
240 100% {
241 stroke-dashoffset: 152;
242 }
243 }
244
245 .hHWjkv {
246 -webkit-transform-origin: 0px 0px;
247 -ms-transform-origin: 0px 0px;
248 transform-origin: 0px 0px;
249 -webkit-transform: scale(0);
250 -ms-transform: scale(0);
251 transform: scale(0);
252 -webkit-animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
253 animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
254 }
255
256 .gCDOzd {
257 -webkit-transform-origin: 0px 0px;
258 -ms-transform-origin: 0px 0px;
259 transform-origin: 0px 0px;
260 -webkit-transform: scale(0);
261 -ms-transform: scale(0);
262 transform: scale(0);
263 -webkit-animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
264 animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
265 }
266
267 .hmCcxi {
268 -webkit-transform-origin: 0px 0px;
269 -ms-transform-origin: 0px 0px;
270 transform-origin: 0px 0px;
271 -webkit-transform: scale(0);
272 -ms-transform: scale(0);
273 transform: scale(0);
274 -webkit-animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
275 animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
276 }
277
278 .eHamQi {
279 -webkit-transform-origin: 0px 0px;
280 -ms-transform-origin: 0px 0px;
281 transform-origin: 0px 0px;
282 -webkit-transform: scale(0);
283 -ms-transform: scale(0);
284 transform: scale(0);
285 -webkit-animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
286 animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
287 }
288
289 .byhgGu {
290 -webkit-transform-origin: 0px 0px;
291 -ms-transform-origin: 0px 0px;
292 transform-origin: 0px 0px;
293 -webkit-transform: scale(0);
294 -ms-transform: scale(0);
295 transform: scale(0);
296 -webkit-animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
297 animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
298 }
299
300 .llAKP {
301 -webkit-transform-origin: 0px 0px;
302 -ms-transform-origin: 0px 0px;
303 transform-origin: 0px 0px;
304 -webkit-transform: scale(0);
305 -ms-transform: scale(0);
306 transform: scale(0);
307 -webkit-animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
308 animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
309 }
310
311 .bglIGM {
312 -webkit-transform-origin: 64px 28px;
313 -ms-transform-origin: 64px 28px;
314 transform-origin: 64px 28px;
315 -webkit-transform: scale(0);
316 -ms-transform: scale(0);
317 transform: scale(0);
318 -webkit-animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
319 animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
320 }
321
322 .ksxRII {
323 -webkit-transform-origin: 95.98500061035156px 46.510000228881836px;
324 -ms-transform-origin: 95.98500061035156px 46.510000228881836px;
325 transform-origin: 95.98500061035156px 46.510000228881836px;
326 -webkit-transform: scale(0);
327 -ms-transform: scale(0);
328 transform: scale(0);
329 -webkit-animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
330 animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
331 }
332
333 .cWrBmb {
334 -webkit-transform-origin: 95.97162628173828px 83.4900016784668px;
335 -ms-transform-origin: 95.97162628173828px 83.4900016784668px;
336 transform-origin: 95.97162628173828px 83.4900016784668px;
337 -webkit-transform: scale(0);
338 -ms-transform: scale(0);
339 transform: scale(0);
340 -webkit-animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
341 animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
342 }
343
344 .Wnusb {
345 -webkit-transform-origin: 64px 101.97999572753906px;
346 -ms-transform-origin: 64px 101.97999572753906px;
347 transform-origin: 64px 101.97999572753906px;
348 -webkit-transform: scale(0);
349 -ms-transform: scale(0);
350 transform: scale(0);
351 -webkit-animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
352 animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
353 }
354
355 .bfPqf {
356 -webkit-transform-origin: 32.03982162475586px 83.4900016784668px;
357 -ms-transform-origin: 32.03982162475586px 83.4900016784668px;
358 transform-origin: 32.03982162475586px 83.4900016784668px;
359 -webkit-transform: scale(0);
360 -ms-transform: scale(0);
361 transform: scale(0);
362 -webkit-animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
363 animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
364 }
365
366 .edRCTN {
367 -webkit-transform-origin: 32.033552169799805px 46.510000228881836px;
368 -ms-transform-origin: 32.033552169799805px 46.510000228881836px;
369 transform-origin: 32.033552169799805px 46.510000228881836px;
370 -webkit-transform: scale(0);
371 -ms-transform: scale(0);
372 transform: scale(0);
373 -webkit-animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
374 animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
375 }
376
377 .iEGVWn {
378 opacity: 0;
379 stroke-dasharray: 76;
380 -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s;
381 animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s;
382 -webkit-animation-iteration-count: 1, 1;
383 animation-iteration-count: 1, 1;
384 }
385
386 .bsocdx {
387 opacity: 0;
388 stroke-dasharray: 76;
389 -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s;
390 animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s;
391 -webkit-animation-iteration-count: 1, 1;
392 animation-iteration-count: 1, 1;
393 }
394
395 .jAZXmP {
396 opacity: 0;
397 stroke-dasharray: 76;
398 -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s;
399 animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s;
400 -webkit-animation-iteration-count: 1, 1;
401 animation-iteration-count: 1, 1;
402 }
403
404 .hSeArx {
405 opacity: 0;
406 stroke-dasharray: 76;
407 -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s;
408 animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s;
409 -webkit-animation-iteration-count: 1, 1;
410 animation-iteration-count: 1, 1;
411 }
412
413 .bVgqGk {
414 opacity: 0;
415 stroke-dasharray: 76;
416 -webkit-animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s;
417 animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s;
418 -webkit-animation-iteration-count: 1, 1;
419 animation-iteration-count: 1, 1;
420 }
421
422 .hEFqBt {
423 opacity: 0;
424 stroke-dasharray: 76;
425 -webkit-animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s;
426 animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s;
427 -webkit-animation-iteration-count: 1, 1;
428 animation-iteration-count: 1, 1;
429 }
430
431 .dzEKCM {
432 opacity: 0;
433 stroke-dasharray: 70;
434 -webkit-animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s;
435 animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s;
436 -webkit-animation-iteration-count: infinite, 1;
437 animation-iteration-count: infinite, 1;
438 }
439
440 .DYnPx {
441 opacity: 0;
442 stroke-dasharray: 70;
443 -webkit-animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s;
444 animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s;
445 -webkit-animation-iteration-count: infinite, 1;
446 animation-iteration-count: infinite, 1;
447 }
448
449 .hjPEAQ {
450 opacity: 0;
451 stroke-dasharray: 70;
452 -webkit-animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s;
453 animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s;
454 -webkit-animation-iteration-count: infinite, 1;
455 animation-iteration-count: infinite, 1;
456 }
457
458 #loading-wrapper {
459 position: absolute;
460 width: 100vw;
461 height: 100vh;
462 display: -webkit-box;
463 display: -webkit-flex;
464 display: -ms-flexbox;
465 display: flex;
466 -webkit-align-items: center;
467 -webkit-box-align: center;
468 -ms-flex-align: center;
469 align-items: center;
470 -webkit-box-pack: center;
471 -webkit-justify-content: center;
472 -ms-flex-pack: center;
473 justify-content: center;
474 -webkit-flex-direction: column;
475 -ms-flex-direction: column;
476 flex-direction: column;
477 }
478
479 .logo {
480 width: 75px;
481 height: 75px;
482 margin-bottom: 20px;
483 opacity: 0;
484 -webkit-animation: fadeIn 0.5s ease-out forwards;
485 animation: fadeIn 0.5s ease-out forwards;
486 }
487
488 .text {
489 font-size: 32px;
490 font-weight: 200;
491 text-align: center;
492 color: rgba(255, 255, 255, 0.6);
493 opacity: 0;
494 -webkit-animation: fadeIn 0.5s ease-out forwards;
495 animation: fadeIn 0.5s ease-out forwards;
496 }
497
498 .dGfHfc {
499 font-weight: 400;
500 }
501 </style>
502 <div id="loading-wrapper">
503 <svg class="logo" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
504 <title>GraphQL Playground Logo</title>
505 <defs>
506 <linearGradient id="linearGradient-1" x1="4.86%" x2="96.21%" y1="0%" y2="99.66%">
507 <stop stop-color="#E00082" stop-opacity=".8" offset="0%"></stop>
508 <stop stop-color="#E00082" offset="100%"></stop>
509 </linearGradient>
510 </defs>
511 <g>
512 <rect id="Gradient" width="127.96" height="127.96" y="1" fill="url(#linearGradient-1)" rx="4"></rect>
513 <path id="Border" fill="#E00082" fill-rule="nonzero" d="M4.7 2.84c-1.58 0-2.86 1.28-2.86 2.85v116.57c0 1.57 1.28 2.84 2.85 2.84h116.57c1.57 0 2.84-1.26 2.84-2.83V5.67c0-1.55-1.26-2.83-2.83-2.83H4.67zM4.7 0h116.58c3.14 0 5.68 2.55 5.68 5.7v116.58c0 3.14-2.54 5.68-5.68 5.68H4.68c-3.13 0-5.68-2.54-5.68-5.68V5.68C-1 2.56 1.55 0 4.7 0z"></path>
514 <path class="bglIGM" x="64" y="28" fill="#fff" d="M64 36c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8" style="transform: translate(100px, 100px);"></path>
515 <path class="ksxRII" x="95.98500061035156" y="46.510000228881836" fill="#fff" d="M89.04 50.52c-2.2-3.84-.9-8.73 2.94-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.76.9-10.97-2.94"
516 style="transform: translate(100px, 100px);"></path>
517 <path class="cWrBmb" x="95.97162628173828" y="83.4900016784668" fill="#fff" d="M102.9 87.5c-2.2 3.84-7.1 5.15-10.94 2.94-3.84-2.2-5.14-7.12-2.94-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.86 2.23 5.16 7.12 2.94 10.96"
518 style="transform: translate(100px, 100px);"></path>
519 <path class="Wnusb" x="64" y="101.97999572753906" fill="#fff" d="M64 110c-4.43 0-8-3.6-8-8.02 0-4.44 3.57-8.02 8-8.02s8 3.58 8 8.02c0 4.4-3.57 8.02-8 8.02"
520 style="transform: translate(100px, 100px);"></path>
521 <path class="bfPqf" x="32.03982162475586" y="83.4900016784668" fill="#fff" d="M25.1 87.5c-2.2-3.84-.9-8.73 2.93-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.74.9-10.95-2.94"
522 style="transform: translate(100px, 100px);"></path>
523 <path class="edRCTN" x="32.033552169799805" y="46.510000228881836" fill="#fff" d="M38.96 50.52c-2.2 3.84-7.12 5.15-10.95 2.94-3.82-2.2-5.12-7.12-2.92-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.83 2.23 5.14 7.12 2.94 10.96"
524 style="transform: translate(100px, 100px);"></path>
525 <path class="iEGVWn" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M63.55 27.5l32.9 19-32.9-19z"></path>
526 <path class="bsocdx" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M96 46v38-38z"></path>
527 <path class="jAZXmP" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M96.45 84.5l-32.9 19 32.9-19z"></path>
528 <path class="hSeArx" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M64.45 103.5l-32.9-19 32.9 19z"></path>
529 <path class="bVgqGk" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M32 84V46v38z"></path>
530 <path class="hEFqBt" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M31.55 46.5l32.9-19-32.9 19z"></path>
531 <path class="dzEKCM" id="Triangle-Bottom" stroke="#fff" stroke-width="4" d="M30 84h70" stroke-linecap="round"></path>
532 <path class="DYnPx" id="Triangle-Left" stroke="#fff" stroke-width="4" d="M65 26L30 87" stroke-linecap="round"></path>
533 <path class="hjPEAQ" id="Triangle-Right" stroke="#fff" stroke-width="4" d="M98 87L63 26" stroke-linecap="round"></path>
534 </g>
535 </svg>
536 <div class="text">Loading
537 <span class="dGfHfc">GraphQL Playground</span>
538 </div>
539 </div>
540
541 <div id="root" />
542 <script type="text/javascript">
543 window.addEventListener('load', function (event) {
544
545 const loadingWrapper = document.getElementById('loading-wrapper');
546 loadingWrapper.classList.add('fadeOut');
547
548
549 const root = document.getElementById('root');
550 root.classList.add('playgroundIn');
551
552 GraphQLPlayground.init(root, GRAPHQL_PLAYGROUND_CONFIG)
553 })
554 </script>
555</body>
556</html>
557 "##.replace("GRAPHQL_PLAYGROUND_CONFIG", &match serde_json::to_string(&config) {
558 Ok(str) => str,
559 Err(_) => "{}".to_string()
560 })
561 .replace("%GRAPHQL_PLAYGROUND_TITLE%", title)
562}
563
564#[derive(Serialize)]
566#[serde(rename_all = "camelCase")]
567pub struct GraphQLPlaygroundConfig<'a> {
568 endpoint: &'a str,
569 subscription_endpoint: Option<&'a str>,
570 headers: Option<HashMap<&'a str, &'a str>>,
571 settings: Option<HashMap<&'a str, Value>>,
572 title: Option<&'a str>,
573}
574
575impl<'a> GraphQLPlaygroundConfig<'a> {
576 pub fn new(endpoint: &'a str) -> Self {
578 Self {
579 endpoint,
580 subscription_endpoint: None,
581 headers: Default::default(),
582 settings: Default::default(),
583 title: Default::default(),
584 }
585 }
586
587 #[must_use]
589 pub fn subscription_endpoint(mut self, endpoint: &'a str) -> Self {
590 self.subscription_endpoint = Some(endpoint);
591 self
592 }
593
594 #[must_use]
596 pub fn with_header(mut self, name: &'a str, value: &'a str) -> Self {
597 if let Some(headers) = &mut self.headers {
598 headers.insert(name, value);
599 } else {
600 let mut headers = HashMap::new();
601 headers.insert(name, value);
602 self.headers = Some(headers);
603 }
604 self
605 }
606
607 #[must_use]
609 pub fn title(mut self, title: &'a str) -> Self {
610 self.title = Some(title);
611 self
612 }
613
614 #[must_use]
624 pub fn with_setting(mut self, name: &'a str, value: impl Into<Value>) -> Self {
625 let value = value.into();
626
627 if let Some(settings) = &mut self.settings {
628 settings.insert(name, value);
629 } else {
630 let mut settings = HashMap::new();
631 settings.insert(name, value);
632 self.settings = Some(settings);
633 }
634 self
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use indexmap::IndexMap;
641
642 use super::*;
643
644 #[test]
645 fn test_with_setting_can_use_any_json_value() {
646 let settings = GraphQLPlaygroundConfig::new("")
647 .with_setting("string", "string")
648 .with_setting("bool", false)
649 .with_setting("number", 10)
650 .with_setting("null", Value::Null)
651 .with_setting("array", Vec::from([1, 2, 3]))
652 .with_setting("object", IndexMap::new());
653
654 let json = serde_json::to_value(settings).unwrap();
655 let settings = json["settings"].as_object().unwrap();
656
657 assert!(settings["string"].as_str().is_some());
658 assert!(settings["bool"].as_bool().is_some());
659 assert!(settings["number"].as_u64().is_some());
660 assert!(settings["null"].as_null().is_some());
661 assert!(settings["array"].as_array().is_some());
662 assert!(settings["object"].as_object().is_some());
663 }
664}