1/**
2 A partial and basic implementation of GVariant formatted strings.
3 See [GVariant Format Strings](https://docs.gtk.org/glib/gvariant-format-strings.html) for details.
4
5 :::{.warning}
6 This API is not considered fully stable and it might therefore
7 change in backwards incompatible ways without prior notice.
8 :::
9*/
10
11# This file is based on https://github.com/nix-community/home-manager
12# Copyright (c) 2017-2022 Home Manager contributors
13{ lib }:
14
15let
16 inherit (lib)
17 concatMapStringsSep
18 concatStrings
19 escape
20 head
21 replaceStrings
22 ;
23
24 mkPrimitive = t: v: {
25 _type = "gvariant";
26 type = t;
27 value = v;
28 __toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html
29 };
30
31 type = {
32 arrayOf = t: "a${t}";
33 maybeOf = t: "m${t}";
34 tupleOf = ts: "(${concatStrings ts})";
35 dictionaryEntryOf = nameType: valueType: "{${nameType}${valueType}}";
36 string = "s";
37 boolean = "b";
38 uchar = "y";
39 int16 = "n";
40 uint16 = "q";
41 int32 = "i";
42 uint32 = "u";
43 int64 = "x";
44 uint64 = "t";
45 double = "d";
46 variant = "v";
47 };
48
49in
50rec {
51
52 inherit type;
53
54 /**
55 Check if a value is a GVariant value
56
57 # Inputs
58
59 `v`
60
61 : value to check
62
63 # Type
64
65 ```
66 isGVariant :: Any -> Bool
67 ```
68 */
69 isGVariant = v: v._type or "" == "gvariant";
70
71 intConstructors = [
72 {
73 name = "mkInt32";
74 type = type.int32;
75 min = -2147483648;
76 max = 2147483647;
77 }
78 {
79 name = "mkUint32";
80 type = type.uint32;
81 min = 0;
82 max = 4294967295;
83 }
84 {
85 name = "mkInt64";
86 type = type.int64;
87 # Nix does not support such large numbers.
88 min = null;
89 max = null;
90 }
91 {
92 name = "mkUint64";
93 type = type.uint64;
94 min = 0;
95 # Nix does not support such large numbers.
96 max = null;
97 }
98 {
99 name = "mkInt16";
100 type = type.int16;
101 min = -32768;
102 max = 32767;
103 }
104 {
105 name = "mkUint16";
106 type = type.uint16;
107 min = 0;
108 max = 65535;
109 }
110 {
111 name = "mkUchar";
112 type = type.uchar;
113 min = 0;
114 max = 255;
115 }
116 ];
117
118 /**
119 Returns the GVariant value that most closely matches the given Nix value.
120 If no GVariant value can be found unambiguously then error is thrown.
121
122 # Inputs
123
124 `v`
125
126 : 1\. Function argument
127
128 # Type
129
130 ```
131 mkValue :: Any -> gvariant
132 ```
133 */
134 mkValue =
135 v:
136 if builtins.isBool v then
137 mkBoolean v
138 else if builtins.isFloat v then
139 mkDouble v
140 else if builtins.isString v then
141 mkString v
142 else if builtins.isList v then
143 mkArray v
144 else if isGVariant v then
145 v
146 else if builtins.isInt v then
147 let
148 validConstructors = builtins.filter (
149 { min, max, ... }: (min == null || min <= v) && (max == null || v <= max)
150 ) intConstructors;
151 in
152 throw ''
153 The GVariant type for number “${builtins.toString v}” is unclear.
154 Please wrap the value with one of the following, depending on the value type in GSettings schema:
155
156 ${lib.concatMapStringsSep "\n" (
157 { name, type, ... }: "- `lib.gvariant.${name}` for `${type}`"
158 ) validConstructors}
159 ''
160 else if builtins.isAttrs v then
161 throw "Cannot construct GVariant value from an attribute set. If you want to construct a dictionary, you will need to create an array containing items constructed with `lib.gvariant.mkDictionaryEntry`."
162 else
163 throw "The GVariant type of “${builtins.typeOf v}” can't be inferred.";
164
165 /**
166 Returns the GVariant array from the given type of the elements and a Nix list.
167
168 # Inputs
169
170 `elems`
171
172 : 1\. Function argument
173
174 # Type
175
176 ```
177 mkArray :: [Any] -> gvariant
178 ```
179
180 # Examples
181 :::{.example}
182 ## `lib.gvariant.mkArray` usage example
183
184 ```nix
185 # Creating a string array
186 lib.gvariant.mkArray [ "a" "b" "c" ]
187 ```
188
189 :::
190 */
191 mkArray =
192 elems:
193 let
194 vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems);
195 elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (
196 map (v: v.type) vs
197 )) "Elements in a list should have same type." (head vs).type;
198 in
199 mkPrimitive (type.arrayOf elemType) vs
200 // {
201 __toString = self: "@${self.type} [${concatMapStringsSep "," toString self.value}]";
202 };
203
204 /**
205 Returns the GVariant array from the given empty Nix list.
206
207 # Inputs
208
209 `elemType`
210
211 : 1\. Function argument
212
213 # Type
214
215 ```
216 mkEmptyArray :: gvariant.type -> gvariant
217 ```
218
219 # Examples
220 :::{.example}
221 ## `lib.gvariant.mkEmptyArray` usage example
222
223 ```nix
224 # Creating an empty string array
225 lib.gvariant.mkEmptyArray (lib.gvariant.type.string)
226 ```
227
228 :::
229 */
230 mkEmptyArray =
231 elemType:
232 mkPrimitive (type.arrayOf elemType) [ ]
233 // {
234 __toString = self: "@${self.type} []";
235 };
236
237 /**
238 Returns the GVariant variant from the given Nix value. Variants are containers
239 of different GVariant type.
240
241 # Inputs
242
243 `elem`
244
245 : 1\. Function argument
246
247 # Type
248
249 ```
250 mkVariant :: Any -> gvariant
251 ```
252
253 # Examples
254 :::{.example}
255 ## `lib.gvariant.mkVariant` usage example
256
257 ```nix
258 lib.gvariant.mkArray [
259 (lib.gvariant.mkVariant "a string")
260 (lib.gvariant.mkVariant (lib.gvariant.mkInt32 1))
261 ]
262 ```
263
264 :::
265 */
266 mkVariant =
267 elem:
268 let
269 gvarElem = mkValue elem;
270 in
271 mkPrimitive type.variant gvarElem
272 // {
273 __toString = self: "<${toString self.value}>";
274 };
275
276 /**
277 Returns the GVariant dictionary entry from the given key and value.
278
279 # Inputs
280
281 `name`
282
283 : The key of the entry
284
285 `value`
286
287 : The value of the entry
288
289 # Type
290
291 ```
292 mkDictionaryEntry :: String -> Any -> gvariant
293 ```
294
295 # Examples
296 :::{.example}
297 ## `lib.gvariant.mkDictionaryEntry` usage example
298
299 ```nix
300 # A dictionary describing an Epiphany’s search provider
301 [
302 (lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany"))
303 (lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d"))
304 (lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo"))
305 ]
306 ```
307
308 :::
309 */
310 mkDictionaryEntry =
311 name: value:
312 let
313 name' = mkValue name;
314 value' = mkValue value;
315 dictionaryType = type.dictionaryEntryOf name'.type value'.type;
316 in
317 mkPrimitive dictionaryType { inherit name value; }
318 // {
319 __toString = self: "@${self.type} {${name'},${value'}}";
320 };
321
322 /**
323 Returns the GVariant maybe from the given element type.
324
325 # Inputs
326
327 `elemType`
328
329 : 1\. Function argument
330
331 `elem`
332
333 : 2\. Function argument
334
335 # Type
336
337 ```
338 mkMaybe :: gvariant.type -> Any -> gvariant
339 ```
340 */
341 mkMaybe =
342 elemType: elem:
343 mkPrimitive (type.maybeOf elemType) elem
344 // {
345 __toString =
346 self: if self.value == null then "@${self.type} nothing" else "just ${toString self.value}";
347 };
348
349 /**
350 Returns the GVariant nothing from the given element type.
351
352 # Inputs
353
354 `elemType`
355
356 : 1\. Function argument
357
358 # Type
359
360 ```
361 mkNothing :: gvariant.type -> gvariant
362 ```
363 */
364 mkNothing = elemType: mkMaybe elemType null;
365
366 /**
367 Returns the GVariant just from the given Nix value.
368
369 # Inputs
370
371 `elem`
372
373 : 1\. Function argument
374
375 # Type
376
377 ```
378 mkJust :: Any -> gvariant
379 ```
380 */
381 mkJust =
382 elem:
383 let
384 gvarElem = mkValue elem;
385 in
386 mkMaybe gvarElem.type gvarElem;
387
388 /**
389 Returns the GVariant tuple from the given Nix list.
390
391 # Inputs
392
393 `elems`
394
395 : 1\. Function argument
396
397 # Type
398
399 ```
400 mkTuple :: [Any] -> gvariant
401 ```
402 */
403 mkTuple =
404 elems:
405 let
406 gvarElems = map mkValue elems;
407 tupleType = type.tupleOf (map (e: e.type) gvarElems);
408 in
409 mkPrimitive tupleType gvarElems
410 // {
411 __toString = self: "@${self.type} (${concatMapStringsSep "," toString self.value})";
412 };
413
414 /**
415 Returns the GVariant boolean from the given Nix bool value.
416
417 # Inputs
418
419 `v`
420
421 : 1\. Function argument
422
423 # Type
424
425 ```
426 mkBoolean :: Bool -> gvariant
427 ```
428 */
429 mkBoolean =
430 v:
431 mkPrimitive type.boolean v
432 // {
433 __toString = self: if self.value then "true" else "false";
434 };
435
436 /**
437 Returns the GVariant string from the given Nix string value.
438
439 # Inputs
440
441 `v`
442
443 : 1\. Function argument
444
445 # Type
446
447 ```
448 mkString :: String -> gvariant
449 ```
450 */
451 mkString =
452 v:
453 let
454 sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
455 in
456 mkPrimitive type.string v
457 // {
458 __toString = self: "'${sanitize self.value}'";
459 };
460
461 /**
462 Returns the GVariant object path from the given Nix string value.
463
464 # Inputs
465
466 `v`
467
468 : 1\. Function argument
469
470 # Type
471
472 ```
473 mkObjectpath :: String -> gvariant
474 ```
475 */
476 mkObjectpath =
477 v:
478 mkPrimitive type.string v
479 // {
480 __toString = self: "objectpath '${escape [ "'" ] self.value}'";
481 };
482
483 /**
484 Returns the GVariant uchar from the given Nix int value.
485
486 # Type
487
488 ```
489 mkUchar :: Int -> gvariant
490 ```
491 */
492 mkUchar = mkPrimitive type.uchar;
493
494 /**
495 Returns the GVariant int16 from the given Nix int value.
496
497 # Type
498
499 ```
500 mkInt16 :: Int -> gvariant
501 ```
502 */
503 mkInt16 = mkPrimitive type.int16;
504
505 /**
506 Returns the GVariant uint16 from the given Nix int value.
507
508 # Type
509
510 ```
511 mkUint16 :: Int -> gvariant
512 ```
513 */
514 mkUint16 = mkPrimitive type.uint16;
515
516 /**
517 Returns the GVariant int32 from the given Nix int value.
518
519 # Inputs
520
521 `v`
522
523 : 1\. Function argument
524
525 # Type
526
527 ```
528 mkInt32 :: Int -> gvariant
529 ```
530 */
531 mkInt32 =
532 v:
533 mkPrimitive type.int32 v
534 // {
535 __toString = self: toString self.value;
536 };
537
538 /**
539 Returns the GVariant uint32 from the given Nix int value.
540
541 # Type
542
543 ```
544 mkUint32 :: Int -> gvariant
545 ```
546 */
547 mkUint32 = mkPrimitive type.uint32;
548
549 /**
550 Returns the GVariant int64 from the given Nix int value.
551
552 # Type
553
554 ```
555 mkInt64 :: Int -> gvariant
556 ```
557 */
558 mkInt64 = mkPrimitive type.int64;
559
560 /**
561 Returns the GVariant uint64 from the given Nix int value.
562
563 # Type
564
565 ```
566 mkUint64 :: Int -> gvariant
567 ```
568 */
569 mkUint64 = mkPrimitive type.uint64;
570
571 /**
572 Returns the GVariant double from the given Nix float value.
573
574 # Inputs
575
576 `v`
577
578 : 1\. Function argument
579
580 # Type
581
582 ```
583 mkDouble :: Float -> gvariant
584 ```
585 */
586 mkDouble =
587 v:
588 mkPrimitive type.double v
589 // {
590 __toString = self: toString self.value;
591 };
592}