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 concatStrings escape head replaceStrings;
18
19 mkPrimitive = t: v: {
20 _type = "gvariant";
21 type = t;
22 value = v;
23 __toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html
24 };
25
26 type = {
27 arrayOf = t: "a${t}";
28 maybeOf = t: "m${t}";
29 tupleOf = ts: "(${concatStrings ts})";
30 dictionaryEntryOf = nameType: valueType: "{${nameType}${valueType}}";
31 string = "s";
32 boolean = "b";
33 uchar = "y";
34 int16 = "n";
35 uint16 = "q";
36 int32 = "i";
37 uint32 = "u";
38 int64 = "x";
39 uint64 = "t";
40 double = "d";
41 variant = "v";
42 };
43
44 /* Check if a value is a GVariant value
45
46 Type:
47 isGVariant :: Any -> Bool
48 */
49 isGVariant = v: v._type or "" == "gvariant";
50
51in
52rec {
53
54 inherit type isGVariant;
55
56 intConstructors = [
57 {
58 name = "mkInt32";
59 type = type.int32;
60 min = -2147483648;
61 max = 2147483647;
62 }
63 {
64 name = "mkUint32";
65 type = type.uint32;
66 min = 0;
67 max = 4294967295;
68 }
69 {
70 name = "mkInt64";
71 type = type.int64;
72 # Nix does not support such large numbers.
73 min = null;
74 max = null;
75 }
76 {
77 name = "mkUint64";
78 type = type.uint64;
79 min = 0;
80 # Nix does not support such large numbers.
81 max = null;
82 }
83 {
84 name = "mkInt16";
85 type = type.int16;
86 min = -32768;
87 max = 32767;
88 }
89 {
90 name = "mkUint16";
91 type = type.uint16;
92 min = 0;
93 max = 65535;
94 }
95 {
96 name = "mkUchar";
97 type = type.uchar;
98 min = 0;
99 max = 255;
100 }
101 ];
102
103 /* Returns the GVariant value that most closely matches the given Nix value.
104 If no GVariant value can be found unambiguously then error is thrown.
105
106 Type:
107 mkValue :: Any -> gvariant
108 */
109 mkValue = v:
110 if builtins.isBool v then
111 mkBoolean v
112 else if builtins.isFloat v then
113 mkDouble v
114 else if builtins.isString v then
115 mkString v
116 else if builtins.isList v then
117 mkArray v
118 else if isGVariant v then
119 v
120 else if builtins.isInt v then
121 let
122 validConstructors = builtins.filter ({ min, max, ... }: (min == null || min <= v) && (max == null || v <= max)) intConstructors;
123 in
124 throw ''
125 The GVariant type for number “${builtins.toString v}” is unclear.
126 Please wrap the value with one of the following, depending on the value type in GSettings schema:
127
128 ${lib.concatMapStringsSep "\n" ({ name, type, ...}: "- `lib.gvariant.${name}` for `${type}`") validConstructors}
129 ''
130 else if builtins.isAttrs v then
131 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`."
132 else
133 throw "The GVariant type of “${builtins.typeOf v}” can't be inferred.";
134
135 /* Returns the GVariant array from the given type of the elements and a Nix list.
136
137 Type:
138 mkArray :: [Any] -> gvariant
139
140 Example:
141 # Creating a string array
142 lib.gvariant.mkArray [ "a" "b" "c" ]
143 */
144 mkArray = elems:
145 let
146 vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems);
147 elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs))
148 "Elements in a list should have same type."
149 (head vs).type;
150 in
151 mkPrimitive (type.arrayOf elemType) vs // {
152 __toString = self:
153 "@${self.type} [${concatMapStringsSep "," toString self.value}]";
154 };
155
156 /* Returns the GVariant array from the given empty Nix list.
157
158 Type:
159 mkEmptyArray :: gvariant.type -> gvariant
160
161 Example:
162 # Creating an empty string array
163 lib.gvariant.mkEmptyArray (lib.gvariant.type.string)
164 */
165 mkEmptyArray = elemType: mkPrimitive (type.arrayOf elemType) [ ] // {
166 __toString = self: "@${self.type} []";
167 };
168
169
170 /* Returns the GVariant variant from the given Nix value. Variants are containers
171 of different GVariant type.
172
173 Type:
174 mkVariant :: Any -> gvariant
175
176 Example:
177 lib.gvariant.mkArray [
178 (lib.gvariant.mkVariant "a string")
179 (lib.gvariant.mkVariant (lib.gvariant.mkInt32 1))
180 ]
181 */
182 mkVariant = elem:
183 let gvarElem = mkValue elem;
184 in mkPrimitive type.variant gvarElem // {
185 __toString = self: "<${toString self.value}>";
186 };
187
188 /* Returns the GVariant dictionary entry from the given key and value.
189
190 Type:
191 mkDictionaryEntry :: String -> Any -> gvariant
192
193 Example:
194 # A dictionary describing an Epiphany’s search provider
195 [
196 (lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany"))
197 (lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d"))
198 (lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo"))
199 ]
200 */
201 mkDictionaryEntry =
202 # The key of the entry
203 name:
204 # The value of the entry
205 value:
206 let
207 name' = mkValue name;
208 value' = mkValue value;
209 dictionaryType = type.dictionaryEntryOf name'.type value'.type;
210 in
211 mkPrimitive dictionaryType { inherit name value; } // {
212 __toString = self: "@${self.type} {${name'},${value'}}";
213 };
214
215 /* Returns the GVariant maybe from the given element type.
216
217 Type:
218 mkMaybe :: gvariant.type -> Any -> gvariant
219 */
220 mkMaybe = elemType: elem:
221 mkPrimitive (type.maybeOf elemType) elem // {
222 __toString = self:
223 if self.value == null then
224 "@${self.type} nothing"
225 else
226 "just ${toString self.value}";
227 };
228
229 /* Returns the GVariant nothing from the given element type.
230
231 Type:
232 mkNothing :: gvariant.type -> gvariant
233 */
234 mkNothing = elemType: mkMaybe elemType null;
235
236 /* Returns the GVariant just from the given Nix value.
237
238 Type:
239 mkJust :: Any -> gvariant
240 */
241 mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
242
243 /* Returns the GVariant tuple from the given Nix list.
244
245 Type:
246 mkTuple :: [Any] -> gvariant
247 */
248 mkTuple = elems:
249 let
250 gvarElems = map mkValue elems;
251 tupleType = type.tupleOf (map (e: e.type) gvarElems);
252 in
253 mkPrimitive tupleType gvarElems // {
254 __toString = self:
255 "@${self.type} (${concatMapStringsSep "," toString self.value})";
256 };
257
258 /* Returns the GVariant boolean from the given Nix bool value.
259
260 Type:
261 mkBoolean :: Bool -> gvariant
262 */
263 mkBoolean = v:
264 mkPrimitive type.boolean v // {
265 __toString = self: if self.value then "true" else "false";
266 };
267
268 /* Returns the GVariant string from the given Nix string value.
269
270 Type:
271 mkString :: String -> gvariant
272 */
273 mkString = v:
274 let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
275 in mkPrimitive type.string v // {
276 __toString = self: "'${sanitize self.value}'";
277 };
278
279 /* Returns the GVariant object path from the given Nix string value.
280
281 Type:
282 mkObjectpath :: String -> gvariant
283 */
284 mkObjectpath = v:
285 mkPrimitive type.string v // {
286 __toString = self: "objectpath '${escape [ "'" ] self.value}'";
287 };
288
289 /* Returns the GVariant uchar from the given Nix int value.
290
291 Type:
292 mkUchar :: Int -> gvariant
293 */
294 mkUchar = mkPrimitive type.uchar;
295
296 /* Returns the GVariant int16 from the given Nix int value.
297
298 Type:
299 mkInt16 :: Int -> gvariant
300 */
301 mkInt16 = mkPrimitive type.int16;
302
303 /* Returns the GVariant uint16 from the given Nix int value.
304
305 Type:
306 mkUint16 :: Int -> gvariant
307 */
308 mkUint16 = mkPrimitive type.uint16;
309
310 /* Returns the GVariant int32 from the given Nix int value.
311
312 Type:
313 mkInt32 :: Int -> gvariant
314 */
315 mkInt32 = v:
316 mkPrimitive type.int32 v // {
317 __toString = self: toString self.value;
318 };
319
320 /* Returns the GVariant uint32 from the given Nix int value.
321
322 Type:
323 mkUint32 :: Int -> gvariant
324 */
325 mkUint32 = mkPrimitive type.uint32;
326
327 /* Returns the GVariant int64 from the given Nix int value.
328
329 Type:
330 mkInt64 :: Int -> gvariant
331 */
332 mkInt64 = mkPrimitive type.int64;
333
334 /* Returns the GVariant uint64 from the given Nix int value.
335
336 Type:
337 mkUint64 :: Int -> gvariant
338 */
339 mkUint64 = mkPrimitive type.uint64;
340
341 /* Returns the GVariant double from the given Nix float value.
342
343 Type:
344 mkDouble :: Float -> gvariant
345 */
346 mkDouble = v:
347 mkPrimitive type.double v // {
348 __toString = self: toString self.value;
349 };
350}