Loading nixos/doc/manual/development/settings-options.section.md +45 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,45 @@ have a predefined type and string generator already declared under and returning a set with TOML-specific attributes `type` and `generate` as specified [below](#pkgs-formats-result). `pkgs.formats.elixirConf { elixir ? pkgs.elixir }` : A function taking an attribute set with values `elixir` : The Elixir package which will be used to format the generated output It returns a set with Elixir-Config-specific attributes `type`, `lib`, and `generate` as specified [below](#pkgs-formats-result). The `lib` attribute contains functions to be used in settings, for generating special Elixir values: `mkRaw elixirCode` : Outputs the given string as raw Elixir code `mkGetEnv { envVariable, fallback ? null }` : Makes the configuration fetch an environment variable at runtime `mkAtom atom` : Outputs the given string as an Elixir atom, instead of the default Elixir binary string. Note: lowercase atoms still needs to be prefixed with `:` `mkTuple array` : Outputs the given array as an Elixir tuple, instead of the default Elixir list `mkMap attrset` : Outputs the given attribute set as an Elixir map, instead of the default Elixir keyword list ::: {#pkgs-formats-result} These functions all return an attribute set with these values: ::: Loading @@ -74,6 +113,12 @@ These functions all return an attribute set with these values: : A module system type representing a value of the format `lib` : Utility functions for convenience, or special interactions with the format. This attribute is optional. It may contain inside a `types` attribute containing types specific to this format. `generate` *`filename jsonValue`* : A function that can render a value of the format to a file. Returns Loading nixos/doc/manual/from_md/development/settings-options.section.xml +104 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,97 @@ </para> </listitem> </varlistentry> <varlistentry> <term> <literal>pkgs.formats.elixirConf { elixir ? pkgs.elixir }</literal> </term> <listitem> <para> A function taking an attribute set with values </para> <variablelist> <varlistentry> <term> <literal>elixir</literal> </term> <listitem> <para> The Elixir package which will be used to format the generated output </para> </listitem> </varlistentry> </variablelist> <para> It returns a set with Elixir-Config-specific attributes <literal>type</literal>, <literal>lib</literal>, and <literal>generate</literal> as specified <link linkend="pkgs-formats-result">below</link>. </para> <para> The <literal>lib</literal> attribute contains functions to be used in settings, for generating special Elixir values: </para> <variablelist> <varlistentry> <term> <literal>mkRaw elixirCode</literal> </term> <listitem> <para> Outputs the given string as raw Elixir code </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkGetEnv { envVariable, fallback ? null }</literal> </term> <listitem> <para> Makes the configuration fetch an environment variable at runtime </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkAtom atom</literal> </term> <listitem> <para> Outputs the given string as an Elixir atom, instead of the default Elixir binary string. Note: lowercase atoms still needs to be prefixed with <literal>:</literal> </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkTuple array</literal> </term> <listitem> <para> Outputs the given array as an Elixir tuple, instead of the default Elixir list </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkMap attrset</literal> </term> <listitem> <para> Outputs the given attribute set as an Elixir map, instead of the default Elixir keyword list </para> </listitem> </varlistentry> </variablelist> </listitem> </varlistentry> </variablelist> <para xml:id="pkgs-formats-result"> These functions all return an attribute set with these values: Loading @@ -152,6 +243,19 @@ </para> </listitem> </varlistentry> <varlistentry> <term> <literal>lib</literal> </term> <listitem> <para> Utility functions for convenience, or special interactions with the format. This attribute is optional. It may contain inside a <literal>types</literal> attribute containing types specific to this format. </para> </listitem> </varlistentry> <varlistentry> <term> <literal>generate</literal> Loading pkgs/pkgs-lib/formats.nix +207 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,15 @@ rec { # The description needs to be overwritten for recursive types type = ...; # Utility functions for convenience, or special interactions with the # format (optional) lib = { exampleFunction = ... # Types specific to the format (optional) types = { ... }; ... }; # generate :: Name -> Value -> Path # A function for generating a file with a value of such a type generate = ...; Loading Loading @@ -147,4 +156,202 @@ rec { ''; }; /* For configurations of Elixir project, like config.exs or runtime.exs Most Elixir project are configured using the [Config] Elixir DSL Since Elixir has more types than Nix, we need a way to map Nix types to more than 1 Elixir type. To that end, this format provides its own library, and its own set of types. To be more detailed, a Nix attribute set could correspond in Elixir to a [Keyword list] (the more common type), or it could correspond to a [Map]. A Nix string could correspond in Elixir to a [String] (also called "binary"), an [Atom], or a list of chars (usually discouraged). A Nix array could correspond in Elixir to a [List] or a [Tuple]. Some more types exists, like records, regexes, but since they are less used, we can leave the `mkRaw` function as an escape hatch. For more information on how to use this format in modules, please refer to the Elixir section of the Nixos documentation. TODO: special Elixir values doesn't show up nicely in the documentation [Config]: <https://hexdocs.pm/elixir/Config.html> [Keyword list]: <https://hexdocs.pm/elixir/Keyword.html> [Map]: <https://hexdocs.pm/elixir/Map.html> [String]: <https://hexdocs.pm/elixir/String.html> [Atom]: <https://hexdocs.pm/elixir/Atom.html> [List]: <https://hexdocs.pm/elixir/List.html> [Tuple]: <https://hexdocs.pm/elixir/Tuple.html> */ elixirConf = { elixir ? pkgs.elixir }: with lib; let toElixir = value: with builtins; if value == null then "nil" else if value == true then "true" else if value == false then "false" else if isInt value || isFloat value then toString value else if isString value then string value else if isAttrs value then attrs value else if isList value then list value else abort "formats.elixirConf: should never happen (value = ${value})"; escapeElixir = escape [ "\\" "#" "\"" ]; string = value: "\"${escapeElixir value}\""; attrs = set: if set ? _elixirType then specialType set else let toKeyword = name: value: "${name}: ${toElixir value}"; keywordList = concatStringsSep ", " (mapAttrsToList toKeyword set); in "[" + keywordList + "]"; listContent = values: concatStringsSep ", " (map toElixir values); list = values: "[" + (listContent values) + "]"; specialType = { value, _elixirType }: if _elixirType == "raw" then value else if _elixirType == "atom" then value else if _elixirType == "map" then elixirMap value else if _elixirType == "tuple" then tuple value else abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})"; elixirMap = set: let toEntry = name: value: "${toElixir name} => ${toElixir value}"; entries = concatStringsSep ", " (mapAttrsToList toEntry set); in "%{${entries}}"; tuple = values: "{${listContent values}}"; toConf = values: let keyConfig = rootKey: key: value: "config ${rootKey}, ${key}, ${toElixir value}"; keyConfigs = rootKey: values: mapAttrsToList (keyConfig rootKey) values; rootConfigs = flatten (mapAttrsToList keyConfigs values); in '' import Config ${concatStringsSep "\n" rootConfigs} ''; in { type = with lib.types; let valueType = nullOr (oneOf [ bool int float str (attrsOf valueType) (listOf valueType) ]) // { description = "Elixir value"; }; in attrsOf (attrsOf (valueType)); lib = let mkRaw = value: { inherit value; _elixirType = "raw"; }; in { inherit mkRaw; /* Fetch an environment variable at runtime, with optional fallback */ mkGetEnv = { envVariable, fallback ? null }: mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})"; /* Make an Elixir atom. Note: lowercase atoms still need to be prefixed by ':' */ mkAtom = value: { inherit value; _elixirType = "atom"; }; /* Make an Elixir tuple out of a list. */ mkTuple = value: { inherit value; _elixirType = "tuple"; }; /* Make an Elixir map out of an attribute set. */ mkMap = value: { inherit value; _elixirType = "map"; }; /* Contains Elixir types. Every type it exports can also be replaced by raw Elixir code (i.e. every type is `either type rawElixir`). It also reexports standard types, wrapping them so that they can also be raw Elixir. */ types = with lib.types; let isElixirType = type: x: (x._elixirType or "") == type; rawElixir = mkOptionType { name = "rawElixir"; description = "raw elixir"; check = isElixirType "raw"; }; elixirOr = other: either other rawElixir; in { inherit rawElixir elixirOr; atom = elixirOr (mkOptionType { name = "elixirAtom"; description = "elixir atom"; check = isElixirType "atom"; }); tuple = elixirOr (mkOptionType { name = "elixirTuple"; description = "elixir tuple"; check = isElixirType "tuple"; }); map = elixirOr (mkOptionType { name = "elixirMap"; description = "elixir map"; check = isElixirType "map"; }); # Wrap standard types, since anything in the Elixir configuration # can be raw Elixir } // lib.mapAttrs (_name: type: elixirOr type) lib.types; }; generate = name: value: pkgs.runCommandNoCC name { value = toConf value; passAsFile = [ "value" ]; nativeBuildInputs = [ elixir ]; } '' cp "$valuePath" "$out" mix format "$out" ''; }; } Loading
nixos/doc/manual/development/settings-options.section.md +45 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,45 @@ have a predefined type and string generator already declared under and returning a set with TOML-specific attributes `type` and `generate` as specified [below](#pkgs-formats-result). `pkgs.formats.elixirConf { elixir ? pkgs.elixir }` : A function taking an attribute set with values `elixir` : The Elixir package which will be used to format the generated output It returns a set with Elixir-Config-specific attributes `type`, `lib`, and `generate` as specified [below](#pkgs-formats-result). The `lib` attribute contains functions to be used in settings, for generating special Elixir values: `mkRaw elixirCode` : Outputs the given string as raw Elixir code `mkGetEnv { envVariable, fallback ? null }` : Makes the configuration fetch an environment variable at runtime `mkAtom atom` : Outputs the given string as an Elixir atom, instead of the default Elixir binary string. Note: lowercase atoms still needs to be prefixed with `:` `mkTuple array` : Outputs the given array as an Elixir tuple, instead of the default Elixir list `mkMap attrset` : Outputs the given attribute set as an Elixir map, instead of the default Elixir keyword list ::: {#pkgs-formats-result} These functions all return an attribute set with these values: ::: Loading @@ -74,6 +113,12 @@ These functions all return an attribute set with these values: : A module system type representing a value of the format `lib` : Utility functions for convenience, or special interactions with the format. This attribute is optional. It may contain inside a `types` attribute containing types specific to this format. `generate` *`filename jsonValue`* : A function that can render a value of the format to a file. Returns Loading
nixos/doc/manual/from_md/development/settings-options.section.xml +104 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,97 @@ </para> </listitem> </varlistentry> <varlistentry> <term> <literal>pkgs.formats.elixirConf { elixir ? pkgs.elixir }</literal> </term> <listitem> <para> A function taking an attribute set with values </para> <variablelist> <varlistentry> <term> <literal>elixir</literal> </term> <listitem> <para> The Elixir package which will be used to format the generated output </para> </listitem> </varlistentry> </variablelist> <para> It returns a set with Elixir-Config-specific attributes <literal>type</literal>, <literal>lib</literal>, and <literal>generate</literal> as specified <link linkend="pkgs-formats-result">below</link>. </para> <para> The <literal>lib</literal> attribute contains functions to be used in settings, for generating special Elixir values: </para> <variablelist> <varlistentry> <term> <literal>mkRaw elixirCode</literal> </term> <listitem> <para> Outputs the given string as raw Elixir code </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkGetEnv { envVariable, fallback ? null }</literal> </term> <listitem> <para> Makes the configuration fetch an environment variable at runtime </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkAtom atom</literal> </term> <listitem> <para> Outputs the given string as an Elixir atom, instead of the default Elixir binary string. Note: lowercase atoms still needs to be prefixed with <literal>:</literal> </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkTuple array</literal> </term> <listitem> <para> Outputs the given array as an Elixir tuple, instead of the default Elixir list </para> </listitem> </varlistentry> <varlistentry> <term> <literal>mkMap attrset</literal> </term> <listitem> <para> Outputs the given attribute set as an Elixir map, instead of the default Elixir keyword list </para> </listitem> </varlistentry> </variablelist> </listitem> </varlistentry> </variablelist> <para xml:id="pkgs-formats-result"> These functions all return an attribute set with these values: Loading @@ -152,6 +243,19 @@ </para> </listitem> </varlistentry> <varlistentry> <term> <literal>lib</literal> </term> <listitem> <para> Utility functions for convenience, or special interactions with the format. This attribute is optional. It may contain inside a <literal>types</literal> attribute containing types specific to this format. </para> </listitem> </varlistentry> <varlistentry> <term> <literal>generate</literal> Loading
pkgs/pkgs-lib/formats.nix +207 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,15 @@ rec { # The description needs to be overwritten for recursive types type = ...; # Utility functions for convenience, or special interactions with the # format (optional) lib = { exampleFunction = ... # Types specific to the format (optional) types = { ... }; ... }; # generate :: Name -> Value -> Path # A function for generating a file with a value of such a type generate = ...; Loading Loading @@ -147,4 +156,202 @@ rec { ''; }; /* For configurations of Elixir project, like config.exs or runtime.exs Most Elixir project are configured using the [Config] Elixir DSL Since Elixir has more types than Nix, we need a way to map Nix types to more than 1 Elixir type. To that end, this format provides its own library, and its own set of types. To be more detailed, a Nix attribute set could correspond in Elixir to a [Keyword list] (the more common type), or it could correspond to a [Map]. A Nix string could correspond in Elixir to a [String] (also called "binary"), an [Atom], or a list of chars (usually discouraged). A Nix array could correspond in Elixir to a [List] or a [Tuple]. Some more types exists, like records, regexes, but since they are less used, we can leave the `mkRaw` function as an escape hatch. For more information on how to use this format in modules, please refer to the Elixir section of the Nixos documentation. TODO: special Elixir values doesn't show up nicely in the documentation [Config]: <https://hexdocs.pm/elixir/Config.html> [Keyword list]: <https://hexdocs.pm/elixir/Keyword.html> [Map]: <https://hexdocs.pm/elixir/Map.html> [String]: <https://hexdocs.pm/elixir/String.html> [Atom]: <https://hexdocs.pm/elixir/Atom.html> [List]: <https://hexdocs.pm/elixir/List.html> [Tuple]: <https://hexdocs.pm/elixir/Tuple.html> */ elixirConf = { elixir ? pkgs.elixir }: with lib; let toElixir = value: with builtins; if value == null then "nil" else if value == true then "true" else if value == false then "false" else if isInt value || isFloat value then toString value else if isString value then string value else if isAttrs value then attrs value else if isList value then list value else abort "formats.elixirConf: should never happen (value = ${value})"; escapeElixir = escape [ "\\" "#" "\"" ]; string = value: "\"${escapeElixir value}\""; attrs = set: if set ? _elixirType then specialType set else let toKeyword = name: value: "${name}: ${toElixir value}"; keywordList = concatStringsSep ", " (mapAttrsToList toKeyword set); in "[" + keywordList + "]"; listContent = values: concatStringsSep ", " (map toElixir values); list = values: "[" + (listContent values) + "]"; specialType = { value, _elixirType }: if _elixirType == "raw" then value else if _elixirType == "atom" then value else if _elixirType == "map" then elixirMap value else if _elixirType == "tuple" then tuple value else abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})"; elixirMap = set: let toEntry = name: value: "${toElixir name} => ${toElixir value}"; entries = concatStringsSep ", " (mapAttrsToList toEntry set); in "%{${entries}}"; tuple = values: "{${listContent values}}"; toConf = values: let keyConfig = rootKey: key: value: "config ${rootKey}, ${key}, ${toElixir value}"; keyConfigs = rootKey: values: mapAttrsToList (keyConfig rootKey) values; rootConfigs = flatten (mapAttrsToList keyConfigs values); in '' import Config ${concatStringsSep "\n" rootConfigs} ''; in { type = with lib.types; let valueType = nullOr (oneOf [ bool int float str (attrsOf valueType) (listOf valueType) ]) // { description = "Elixir value"; }; in attrsOf (attrsOf (valueType)); lib = let mkRaw = value: { inherit value; _elixirType = "raw"; }; in { inherit mkRaw; /* Fetch an environment variable at runtime, with optional fallback */ mkGetEnv = { envVariable, fallback ? null }: mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})"; /* Make an Elixir atom. Note: lowercase atoms still need to be prefixed by ':' */ mkAtom = value: { inherit value; _elixirType = "atom"; }; /* Make an Elixir tuple out of a list. */ mkTuple = value: { inherit value; _elixirType = "tuple"; }; /* Make an Elixir map out of an attribute set. */ mkMap = value: { inherit value; _elixirType = "map"; }; /* Contains Elixir types. Every type it exports can also be replaced by raw Elixir code (i.e. every type is `either type rawElixir`). It also reexports standard types, wrapping them so that they can also be raw Elixir. */ types = with lib.types; let isElixirType = type: x: (x._elixirType or "") == type; rawElixir = mkOptionType { name = "rawElixir"; description = "raw elixir"; check = isElixirType "raw"; }; elixirOr = other: either other rawElixir; in { inherit rawElixir elixirOr; atom = elixirOr (mkOptionType { name = "elixirAtom"; description = "elixir atom"; check = isElixirType "atom"; }); tuple = elixirOr (mkOptionType { name = "elixirTuple"; description = "elixir tuple"; check = isElixirType "tuple"; }); map = elixirOr (mkOptionType { name = "elixirMap"; description = "elixir map"; check = isElixirType "map"; }); # Wrap standard types, since anything in the Elixir configuration # can be raw Elixir } // lib.mapAttrs (_name: type: elixirOr type) lib.types; }; generate = name: value: pkgs.runCommandNoCC name { value = toConf value; passAsFile = [ "value" ]; nativeBuildInputs = [ elixir ]; } '' cp "$valuePath" "$out" mix format "$out" ''; }; }