at master 25 kB view raw
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 dont 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}