1/**
2 Functions that generate widespread file
3 formats from nix data structures.
4
5 They all follow a similar interface:
6
7 ```nix
8 generator { config-attrs } data
9 ```
10
11 `config-attrs` are “holes” in the generators
12 with sensible default implementations that
13 can be overwritten. The default implementations
14 are mostly generators themselves, called with
15 their respective default values; they can be reused.
16
17 Tests can be found in ./tests/misc.nix
18
19 Further Documentation can be found [here](#sec-generators).
20*/
21{ lib }:
22
23let
24 inherit (lib)
25 addErrorContext
26 assertMsg
27 attrNames
28 concatLists
29 concatMapStringsSep
30 concatStrings
31 concatStringsSep
32 const
33 elem
34 escape
35 filter
36 flatten
37 foldl
38 functionArgs # Note: not the builtin; considers `__functor` in attrsets.
39 gvariant
40 hasInfix
41 head
42 id
43 init
44 isAttrs
45 isBool
46 isDerivation
47 isFloat
48 isFunction # Note: not the builtin; considers `__functor` in attrsets.
49 isInt
50 isList
51 isPath
52 isString
53 last
54 length
55 mapAttrs
56 mapAttrsToList
57 optionals
58 recursiveUpdate
59 replaceStrings
60 reverseList
61 splitString
62 tail
63 toList
64 ;
65
66 inherit (lib.strings)
67 escapeNixIdentifier
68 floatToString
69 match
70 split
71 toJSON
72 typeOf
73 escapeXML
74 ;
75
76 ## -- HELPER FUNCTIONS & DEFAULTS --
77in
78rec {
79 /**
80 Convert a value to a sensible default string representation.
81 The builtin `toString` function has some strange defaults,
82 suitable for bash scripts but not much else.
83
84 # Inputs
85
86 Options
87 : Empty set, there may be configuration options in the future
88
89 `v`
90 : 2\. Function argument
91 */
92 mkValueStringDefault =
93 { }:
94 v:
95 let
96 err = t: v: abort ("generators.mkValueStringDefault: " + "${t} not supported: ${toPretty { } v}");
97 in
98 if isInt v then
99 toString v
100 # convert derivations to store paths
101 else if isDerivation v then
102 toString v
103 # we default to not quoting strings
104 else if isString v then
105 v
106 # isString returns "1", which is not a good default
107 else if true == v then
108 "true"
109 # here it returns to "", which is even less of a good default
110 else if false == v then
111 "false"
112 else if null == v then
113 "null"
114 # if you have lists you probably want to replace this
115 else if isList v then
116 err "lists" v
117 # same as for lists, might want to replace
118 else if isAttrs v then
119 err "attrsets" v
120 # functions can’t be printed of course
121 else if isFunction v then
122 err "functions" v
123 # Floats currently can't be converted to precise strings,
124 # condition warning on nix version once this isn't a problem anymore
125 # See https://github.com/NixOS/nix/pull/3480
126 else if isFloat v then
127 floatToString v
128 else
129 err "this value is" (toString v);
130
131 /**
132 Generate a line of key k and value v, separated by
133 character sep. If sep appears in k, it is escaped.
134 Helper for synaxes with different separators.
135
136 mkValueString specifies how values should be formatted.
137
138 ```nix
139 mkKeyValueDefault {} ":" "f:oo" "bar"
140 > "f\:oo:bar"
141 ```
142
143 # Inputs
144
145 Structured function argument
146 : mkValueString (optional, default: `mkValueStringDefault {}`)
147 : Function to convert values to strings
148
149 `sep`
150
151 : 2\. Function argument
152
153 `k`
154
155 : 3\. Function argument
156
157 `v`
158
159 : 4\. Function argument
160 */
161 mkKeyValueDefault =
162 {
163 mkValueString ? mkValueStringDefault { },
164 }:
165 sep: k: v:
166 "${escape [ sep ] k}${sep}${mkValueString v}";
167
168 ## -- FILE FORMAT GENERATORS --
169
170 /**
171 Generate a key-value-style config file from an attrset.
172
173 # Inputs
174
175 Structured function argument
176
177 : mkKeyValue (optional, default: `mkKeyValueDefault {} "="`)
178 : format a setting line from key and value
179
180 : listsAsDuplicateKeys (optional, default: `false`)
181 : allow lists as values for duplicate keys
182
183 : indent (optional, default: `""`)
184 : Initial indentation level
185 */
186 toKeyValue =
187 {
188 mkKeyValue ? mkKeyValueDefault { } "=",
189 listsAsDuplicateKeys ? false,
190 indent ? "",
191 }:
192 let
193 mkLine = k: v: indent + mkKeyValue k v + "\n";
194 mkLines =
195 if listsAsDuplicateKeys then
196 k: v: map (mkLine k) (if isList v then v else [ v ])
197 else
198 k: v: [ (mkLine k v) ];
199 in
200 attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
201
202 /**
203 Generate an INI-style config file from an
204 attrset of sections to an attrset of key-value pairs.
205
206 # Inputs
207
208 Structured function argument
209
210 : mkSectionName (optional, default: `(name: escape [ "[" "]" ] name)`)
211 : apply transformations (e.g. escapes) to section names
212
213 : mkKeyValue (optional, default: `{} "="`)
214 : format a setting line from key and value
215
216 : listsAsDuplicateKeys (optional, default: `false`)
217 : allow lists as values for duplicate keys
218
219 # Examples
220 :::{.example}
221 ## `lib.generators.toINI` usage example
222
223 ```nix
224 generators.toINI {} {
225 foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
226 baz = { "also, integers" = 42; };
227 }
228
229 > [baz]
230 > also, integers=42
231 >
232 > [foo]
233 > ciao=bar
234 > hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
235 ```
236
237 The mk* configuration attributes can generically change
238 the way sections and key-value strings are generated.
239
240 For more examples see the test cases in ./tests/misc.nix.
241
242 :::
243 */
244 toINI =
245 {
246 mkSectionName ? (name: escape [ "[" "]" ] name),
247 mkKeyValue ? mkKeyValueDefault { } "=",
248 listsAsDuplicateKeys ? false,
249 }:
250 attrsOfAttrs:
251 let
252 # map function to string for each key val
253 mapAttrsToStringsSep =
254 sep: mapFn: attrs:
255 concatStringsSep sep (mapAttrsToList mapFn attrs);
256 mkSection =
257 sectName: sectValues:
258 ''
259 [${mkSectionName sectName}]
260 ''
261 + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
262 in
263 # map input to ini sections
264 mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
265
266 /**
267 Generate an INI-style config file from an attrset
268 specifying the global section (no header), and an
269 attrset of sections to an attrset of key-value pairs.
270
271 # Inputs
272
273 1\. Structured function argument
274
275 : mkSectionName (optional, default: `(name: escape [ "[" "]" ] name)`)
276 : apply transformations (e.g. escapes) to section names
277
278 : mkKeyValue (optional, default: `{} "="`)
279 : format a setting line from key and value
280
281 : listsAsDuplicateKeys (optional, default: `false`)
282 : allow lists as values for duplicate keys
283
284 2\. Structured function argument
285
286 : globalSection (required)
287 : global section key-value pairs
288
289 : sections (optional, default: `{}`)
290 : attrset of sections to key-value pairs
291
292 # Examples
293 :::{.example}
294 ## `lib.generators.toINIWithGlobalSection` usage example
295
296 ```nix
297 generators.toINIWithGlobalSection {} {
298 globalSection = {
299 someGlobalKey = "hi";
300 };
301 sections = {
302 foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
303 baz = { "also, integers" = 42; };
304 }
305
306 > someGlobalKey=hi
307 >
308 > [baz]
309 > also, integers=42
310 >
311 > [foo]
312 > ciao=bar
313 > hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
314 ```
315
316 The mk* configuration attributes can generically change
317 the way sections and key-value strings are generated.
318
319 For more examples see the test cases in ./tests/misc.nix.
320
321 :::
322
323 If you don’t need a global section, you can also use
324 `generators.toINI` directly, which only takes
325 the part in `sections`.
326 */
327 toINIWithGlobalSection =
328 {
329 mkSectionName ? (name: escape [ "[" "]" ] name),
330 mkKeyValue ? mkKeyValueDefault { } "=",
331 listsAsDuplicateKeys ? false,
332 }:
333 {
334 globalSection,
335 sections ? { },
336 }:
337 (
338 if globalSection == { } then
339 ""
340 else
341 (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) + "\n"
342 )
343 + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
344
345 /**
346 Generate a git-config file from an attrset.
347
348 It has two major differences from the regular INI format:
349
350 1. values are indented with tabs
351 2. sections can have sub-sections
352
353 Further: https://git-scm.com/docs/git-config#EXAMPLES
354
355 # Examples
356 :::{.example}
357 ## `lib.generators.toGitINI` usage example
358
359 ```nix
360 generators.toGitINI {
361 url."ssh://git@github.com/".insteadOf = "https://github.com";
362 user.name = "edolstra";
363 }
364
365 > [url "ssh://git@github.com/"]
366 > insteadOf = "https://github.com"
367 >
368 > [user]
369 > name = "edolstra"
370 ```
371
372 :::
373
374 # Inputs
375
376 `attrs`
377
378 : Key-value pairs to be converted to a git-config file.
379 See: https://git-scm.com/docs/git-config#_variables for possible values.
380 */
381 toGitINI =
382 attrs:
383 let
384 mkSectionName =
385 name:
386 let
387 containsQuote = hasInfix ''"'' name;
388 sections = splitString "." name;
389 section = head sections;
390 subsections = tail sections;
391 subsection = concatStringsSep "." subsections;
392 in
393 if containsQuote || subsections == [ ] then name else ''${section} "${subsection}"'';
394
395 mkValueString =
396 v:
397 let
398 escapedV = ''"${replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v}"'';
399 in
400 mkValueStringDefault { } (if isString v then escapedV else v);
401
402 # generation for multiple ini values
403 mkKeyValue =
404 k: v:
405 let
406 mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
407 in
408 concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
409
410 # converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
411 gitFlattenAttrs =
412 let
413 recurse =
414 path: value:
415 if isAttrs value && !isDerivation value then
416 mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
417 else if length path > 1 then
418 {
419 ${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
420 }
421 else
422 {
423 ${head path} = value;
424 };
425 in
426 attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
427
428 toINI_ = toINI { inherit mkKeyValue mkSectionName; };
429 in
430 toINI_ (gitFlattenAttrs attrs);
431
432 /**
433 mkKeyValueDefault wrapper that handles dconf INI quirks.
434 The main differences of the format is that it requires strings to be quoted.
435 */
436 mkDconfKeyValue = mkKeyValueDefault { mkValueString = v: toString (gvariant.mkValue v); } "=";
437
438 /**
439 Generates INI in dconf keyfile style. See https://help.gnome.org/admin/system-admin-guide/stable/dconf-keyfiles.html.en
440 for details.
441 */
442 toDconfINI = toINI { mkKeyValue = mkDconfKeyValue; };
443
444 /**
445 Recurses through a `Value` limited to a certain depth. (`depthLimit`)
446
447 If the depth is exceeded, an error is thrown, unless `throwOnDepthLimit` is set to `false`.
448
449 # Inputs
450
451 Structured function argument
452
453 : depthLimit (required)
454 : If this option is not null, the given value will stop evaluating at a certain depth
455
456 : throwOnDepthLimit (optional, default: `true`)
457 : If this option is true, an error will be thrown, if a certain given depth is exceeded
458
459 Value
460 : The value to be evaluated recursively
461 */
462 withRecursion =
463 {
464 depthLimit,
465 throwOnDepthLimit ? true,
466 }:
467 assert isInt depthLimit;
468 let
469 specialAttrs = [
470 "__functor"
471 "__functionArgs"
472 "__toString"
473 "__pretty"
474 ];
475 stepIntoAttr = evalNext: name: if elem name specialAttrs then id else evalNext;
476 transform =
477 depth:
478 if depthLimit != null && depth > depthLimit then
479 if throwOnDepthLimit then
480 throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
481 else
482 const "<unevaluated>"
483 else
484 id;
485 mapAny =
486 depth: v:
487 let
488 evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
489 in
490 if isAttrs v then
491 mapAttrs (stepIntoAttr evalNext) v
492 else if isList v then
493 map evalNext v
494 else
495 transform (depth + 1) v;
496 in
497 mapAny 0;
498
499 /**
500 Pretty print a value, akin to `builtins.trace`.
501
502 Should probably be a builtin as well.
503
504 The pretty-printed string should be suitable for rendering default values
505 in the NixOS manual. In particular, it should be as close to a valid Nix expression
506 as possible.
507
508 # Inputs
509
510 Structured function argument
511 : allowPrettyValues
512 : If this option is true, attrsets like { __pretty = fn; val = …; }
513 will use fn to convert val to a pretty printed representation.
514 (This means fn is type Val -> String.)
515 : multiline
516 : If this option is true, the output is indented with newlines for attribute sets and lists
517 : indent
518 : Initial indentation level
519
520 Value
521 : The value to be pretty printed
522 */
523 toPretty =
524 {
525 allowPrettyValues ? false,
526 multiline ? true,
527 indent ? "",
528 }:
529 let
530 go =
531 indent: v:
532 let
533 introSpace = if multiline then "\n${indent} " else " ";
534 outroSpace = if multiline then "\n${indent}" else " ";
535 in
536 if isInt v then
537 toString v
538 # toString loses precision on floats, so we use toJSON instead. This isn't perfect
539 # as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
540 # pretty-printing purposes this is acceptable.
541 else if isFloat v then
542 builtins.toJSON v
543 else if isString v then
544 let
545 lines = filter (v: !isList v) (split "\n" v);
546 escapeSingleline = escape [
547 "\\"
548 "\""
549 "\${"
550 ];
551 escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
552 singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
553 multilineResult =
554 let
555 escapedLines = map escapeMultiline lines;
556 # The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
557 # indentation level. Otherwise, '' is appended to the last line.
558 lastLine = last escapedLines;
559 in
560 "''"
561 + introSpace
562 + concatStringsSep introSpace (init escapedLines)
563 + (if lastLine == "" then outroSpace else introSpace + lastLine)
564 + "''";
565 in
566 if multiline && length lines > 1 then multilineResult else singlelineResult
567 else if true == v then
568 "true"
569 else if false == v then
570 "false"
571 else if null == v then
572 "null"
573 else if isPath v then
574 toString v
575 else if isList v then
576 if v == [ ] then
577 "[ ]"
578 else
579 "[" + introSpace + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace + "]"
580 else if isFunction v then
581 let
582 fna = functionArgs v;
583 showFnas = concatStringsSep ", " (
584 mapAttrsToList (name: hasDefVal: if hasDefVal then name + "?" else name) fna
585 );
586 in
587 if fna == { } then "<function>" else "<function, args: {${showFnas}}>"
588 else if isAttrs v then
589 # apply pretty values if allowed
590 if allowPrettyValues && v ? __pretty && v ? val then
591 v.__pretty v.val
592 else if v == { } then
593 "{ }"
594 else if v ? type && v.type == "derivation" then
595 "<derivation ${v.name or "???"}>"
596 else
597 "{"
598 + introSpace
599 + concatStringsSep introSpace (
600 mapAttrsToList (
601 name: value:
602 "${escapeNixIdentifier name} = ${
603 addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value)
604 };"
605 ) v
606 )
607 + outroSpace
608 + "}"
609 else
610 abort "generators.toPretty: should never happen (v = ${v})";
611 in
612 go indent;
613
614 /**
615 Translate a simple Nix expression to [Plist notation](https://en.wikipedia.org/wiki/Property_list).
616
617 # Inputs
618
619 Structured function argument
620
621 : escape (optional, default: `false`)
622 : If this option is true, XML special characters are escaped in string values and keys
623
624 Value
625 : The value to be converted to Plist
626 */
627 toPlist =
628 {
629 escape ? false,
630 }:
631 v:
632 let
633 expr =
634 ind: x:
635 if x == null then
636 ""
637 else if isBool x then
638 bool ind x
639 else if isInt x then
640 int ind x
641 else if isString x then
642 str ind x
643 else if isList x then
644 list ind x
645 else if isAttrs x then
646 attrs ind x
647 else if isPath x then
648 str ind (toString x)
649 else if isFloat x then
650 float ind x
651 else
652 abort "generators.toPlist: should never happen (v = ${v})";
653
654 literal = ind: x: ind + x;
655
656 maybeEscapeXML = if escape then escapeXML else x: x;
657
658 bool = ind: x: literal ind (if x then "<true/>" else "<false/>");
659 int = ind: x: literal ind "<integer>${toString x}</integer>";
660 str = ind: x: literal ind "<string>${maybeEscapeXML x}</string>";
661 key = ind: x: literal ind "<key>${maybeEscapeXML x}</key>";
662 float = ind: x: literal ind "<real>${toString x}</real>";
663
664 indent = ind: expr "\t${ind}";
665
666 item = ind: concatMapStringsSep "\n" (indent ind);
667
668 list =
669 ind: x:
670 concatStringsSep "\n" [
671 (literal ind "<array>")
672 (item ind x)
673 (literal ind "</array>")
674 ];
675
676 attrs =
677 ind: x:
678 concatStringsSep "\n" [
679 (literal ind "<dict>")
680 (attr ind x)
681 (literal ind "</dict>")
682 ];
683
684 attr =
685 let
686 attrFilter = name: value: name != "_module" && value != null;
687 in
688 ind: x:
689 concatStringsSep "\n" (
690 flatten (
691 mapAttrsToList (
692 name: value:
693 optionals (attrFilter name value) [
694 (key "\t${ind}" name)
695 (expr "\t${ind}" value)
696 ]
697 ) x
698 )
699 );
700
701 in
702 # TODO: As discussed in #356502, deprecated functionality should be removed sometime after 25.11.
703 lib.warnIf (!escape && lib.oldestSupportedReleaseIsAtLeast 2505)
704 "Using `lib.generators.toPlist` without `escape = true` is deprecated"
705 ''
706 <?xml version="1.0" encoding="UTF-8"?>
707 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
708 <plist version="1.0">
709 ${expr "" v}
710 </plist>'';
711
712 /**
713 Translate a simple Nix expression to Dhall notation.
714
715 Note that integers are translated to Integer and never
716 the Natural type.
717
718 # Inputs
719
720 Options
721
722 : Empty set, there may be configuration options in the future
723
724 Value
725
726 : The value to be converted to Dhall
727 */
728 toDhall =
729 { }@args:
730 v:
731 let
732 concatItems = concatStringsSep ", ";
733 in
734 if isAttrs v then
735 "{ ${concatItems (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v)} }"
736 else if isList v then
737 "[ ${concatItems (map (toDhall args) v)} ]"
738 else if isInt v then
739 "${if v < 0 then "" else "+"}${toString v}"
740 else if isBool v then
741 (if v then "True" else "False")
742 else if isFunction v then
743 abort "generators.toDhall: cannot convert a function to Dhall"
744 else if v == null then
745 abort "generators.toDhall: cannot convert a null to Dhall"
746 else
747 toJSON v;
748
749 /**
750 Translate a simple Nix expression to Lua representation with occasional
751 Lua-inlines that can be constructed by mkLuaInline function.
752
753 Configuration:
754
755 * multiline - by default is true which results in indented block-like view.
756 * indent - initial indent.
757 * asBindings - by default generate single value, but with this use attrset to set global vars.
758
759 Attention:
760
761 Regardless of multiline parameter there is no trailing newline.
762
763 # Inputs
764
765 Structured function argument
766
767 : multiline (optional, default: `true`)
768 : If this option is true, the output is indented with newlines for attribute sets and lists
769 : indent (optional, default: `""`)
770 : Initial indentation level
771 : asBindings (optional, default: `false`)
772 : Interpret as variable bindings
773
774 Value
775
776 : The value to be converted to Lua
777
778 # Type
779
780 ```
781 toLua :: AttrSet -> Any -> String
782 ```
783
784 # Examples
785 :::{.example}
786 ## `lib.generators.toLua` usage example
787
788 ```nix
789 generators.toLua {}
790 {
791 cmd = [ "typescript-language-server" "--stdio" ];
792 settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
793 }
794 ->
795 {
796 ["cmd"] = {
797 "typescript-language-server",
798 "--stdio"
799 },
800 ["settings"] = {
801 ["workspace"] = {
802 ["library"] = (vim.api.nvim_get_runtime_file("", true))
803 }
804 }
805 }
806 ```
807
808 :::
809 */
810 toLua =
811 {
812 multiline ? true,
813 indent ? "",
814 asBindings ? false,
815 }@args:
816 v:
817 let
818 innerIndent = "${indent} ";
819 introSpace = if multiline then "\n${innerIndent}" else " ";
820 outroSpace = if multiline then "\n${indent}" else " ";
821 innerArgs = args // {
822 indent = if asBindings then indent else innerIndent;
823 asBindings = false;
824 };
825 concatItems = concatStringsSep ",${introSpace}";
826 isLuaInline =
827 {
828 _type ? null,
829 ...
830 }:
831 _type == "lua-inline";
832
833 generatedBindings =
834 assert assertMsg (badVarNames == [ ]) "Bad Lua var names: ${toPretty { } badVarNames}";
835 concatStrings (mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v);
836
837 # https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
838 matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
839 badVarNames = filter (name: matchVarName name == null) (attrNames v);
840 in
841 if asBindings then
842 generatedBindings
843 else if v == null then
844 "nil"
845 else if isInt v || isFloat v || isString v || isBool v then
846 toJSON v
847 else if isPath v || isDerivation v then
848 toJSON "${v}"
849 else if isList v then
850 (
851 if v == [ ] then
852 "{}"
853 else
854 "{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}"
855 )
856 else if isAttrs v then
857 (
858 if isLuaInline v then
859 "(${v.expr})"
860 else if v == { } then
861 "{}"
862 else
863 "{${introSpace}${
864 concatItems (mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v)
865 }${outroSpace}}"
866 )
867 else
868 abort "generators.toLua: type ${typeOf v} is unsupported";
869
870 /**
871 Mark string as Lua expression to be inlined when processed by toLua.
872
873 # Inputs
874
875 `expr`
876
877 : 1\. Function argument
878
879 # Type
880
881 ```
882 mkLuaInline :: String -> AttrSet
883 ```
884 */
885 mkLuaInline = expr: {
886 _type = "lua-inline";
887 inherit expr;
888 };
889}
890// {
891 /**
892 Generates JSON from an arbitrary (non-function) value.
893 For more information see the documentation of the builtin.
894
895 # Inputs
896
897 Options
898
899 : Empty set, there may be configuration options in the future
900
901 Value
902
903 : The value to be converted to JSON
904 */
905 toJSON = { }: lib.strings.toJSON;
906
907 /**
908 YAML has been a strict superset of JSON since 1.2, so we
909 use toJSON. Before it only had a few differences referring
910 to implicit typing rules, so it should work with older
911 parsers as well.
912
913 # Inputs
914
915 Options
916
917 : Empty set, there may be configuration options in the future
918
919 Value
920
921 : The value to be converted to YAML
922 */
923 toYAML = { }: lib.strings.toJSON;
924}